Skip to content

Commit

Permalink
chore(avatar): migrate to Svelte 5
Browse files Browse the repository at this point in the history
  • Loading branch information
vnphanquang committed May 20, 2024
1 parent e6452c6 commit 5c05a51
Show file tree
Hide file tree
Showing 11 changed files with 118 additions and 122 deletions.
2 changes: 1 addition & 1 deletion .changeset/brave-tips-do.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,4 @@
"@svelte-put/preprocess-inline-svg": major
---

switch to using modern Svelte AST
[BREAKING] switch to using modern Svelte AST
2 changes: 1 addition & 1 deletion .changeset/odd-rules-argue.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,4 @@
"@svelte-put/clickoutside": major
---

drop support for Svelte 4 and lower (will still work as of now but no guarantees in the future)
[BREAKING] drop support for Svelte 4 and lower (will still work as of now but no guarantees in the future)
5 changes: 5 additions & 0 deletions .changeset/soft-countries-add.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@svelte-put/avatar': major
---

[BREAKING] drop support for Svelte 4 and lower; now using runes and snippets
2 changes: 1 addition & 1 deletion .changeset/tiny-dancers-live.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,4 @@
"@svelte-put/preprocess-inline-svg": major
---

support Svelte v5, drop support for v4 and below, now written in JS with JSDocs
[BREAKING] support Svelte v5, drop support for v4 and below, now written in JS with JSDocs
2 changes: 1 addition & 1 deletion .changeset/twelve-tools-type.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,4 @@
"@svelte-put/preprocess-auto-slug": major
---

drop support for Svelte 4 and lower, now using Svelte modern AST and witten in JS with JSDocs
[BREAKING] drop support for Svelte 4 and lower, now using Svelte modern AST and witten in JS with JSDocs
43 changes: 17 additions & 26 deletions packages/avatar/src/Avatar.svelte
Original file line number Diff line number Diff line change
@@ -1,31 +1,20 @@
<script>
import { onMount } from 'svelte';
import { resolveAlt, resolveSize, resolveSrc, DEFINITIVE_FALLBACK } from './avatar.utils.js';
/** @type {import('./Avatar.svelte.d.ts').AvatarProps['src']} */
export let src = undefined;
/** @type {import('./Avatar.svelte.d.ts').AvatarProps['gravatar']} */
export let gravatar = undefined;
/** @type {import('./Avatar.svelte.d.ts').AvatarProps['uiAvatar']} */
export let uiAvatar = undefined;
/** @type {import('./Avatar.svelte.d.ts').AvatarProps['fallback']} */
export let fallback = undefined;
/** @type {import('./Avatar.svelte.d.ts').AvatarProps['size']} */
export let size = undefined;
/** @type {import('./Avatar.svelte.d.ts').AvatarProps['alt']} */
export let alt = undefined;
/** @type {import('./Avatar.svelte.d.ts').AvatarProps} */
let { src, gravatar, uiAvatar, fallback, size, alt, class: cls = '', img, ...rest } = $props();
let rAlt = $derived(resolveAlt(alt, gravatar, uiAvatar, src));
let rSize = $derived(resolveSize(32, size, src, gravatar, uiAvatar));
let sources = $derived(resolveSrc(src, gravatar, uiAvatar, fallback));
let rSrc = $state(DEFINITIVE_FALLBACK);
$: rAlt = resolveAlt(alt, gravatar, uiAvatar, src);
$: rSize = resolveSize(32, size, src, gravatar, uiAvatar);
$: sources = resolveSrc(src, gravatar, uiAvatar, fallback);
let rSrc = DEFINITIVE_FALLBACK;
/** @type {HTMLImageElement | undefined} */
let element = $state(undefined);
/** @type {HTMLImageElement} */
let element;
onMount(async () => {
$effect(() => {
let rElement = element;
if ($$slots.default) {
if (img) {
rElement = new Image();
rElement.style.display = 'none';
document.body.appendChild(rElement);
Expand All @@ -50,18 +39,20 @@
Svelte image wrapper component for displaying avatar
@public
-->
<slot src={rSrc} size={rSize} alt={rAlt} {sources}>
{#if img}
{@render img({ src: rSrc, size: rSize, alt: rAlt, sources })}
{:else}
<img
src={rSrc}
alt={rAlt}
height={rSize}
width={rSize}
class="svelte-put-avatar {$$props.class ?? ''}"
class="svelte-put-avatar {cls}"
data-sources={sources.join(',')}
bind:this={element}
{...$$restProps}
{...rest}
/>
</slot>
{/if}
<style>
:global(:where(.svelte-put-avatar)) {
Expand Down
117 changes: 47 additions & 70 deletions packages/avatar/src/Avatar.svelte.d.ts
Original file line number Diff line number Diff line change
@@ -1,68 +1,6 @@
import type { SvelteComponent } from 'svelte';
import type { Snippet, SvelteComponent } from 'svelte';
import type { HTMLImgAttributes } from 'svelte/elements';

/**
* Slots of Avatar component
*
*/
export interface AvatarSlots {
default: {
/** The resolved avatar url */
src: string;
/** The resolved alt text, if any */
alt: string;
/** The resolved size, if any */
size: number;
/** All the images source from prop */
sources: string[];
};
}

/**
* Props to Avatar component
*
*/
export interface AvatarProps extends HTMLImgAttributes {
/**
* "src" attribute. This option is of highest priority
*/
src?: string;
/**
* Either the email for {@link https://en.gravatar.com/site/implement/images | Gravatar }
* or {@link GravatarOptions } object. This option take second priority.
*
* By default, the Gravatar `default` query is set to 404, so that if gravatar is not valid,
* the next priority url will be used. Be careful when setting this `default` to other values,
* as gravatar will then always return a valid resource, hence stopping the resolution flow
* (i.e uiAvatar & fallback will never be used).
*/
gravatar?: GravatarOptions | string;
/**
* Either the name for building initials with {@link https://ui-avatars.com | UIAvatar }
* or {@link UIAvatarOptions } object. This option take third priority.
*/
uiAvatar?: string | UIAvatarOptions;
/**
* The fallback url string. This option takes lowest priority.
* It defaults to the internal fallback (https://www.gravatar.com/avatar?d=mp).
* If you provide a different url that is not a valid resource, the internal fallback
* will be used.
*/
fallback?: string;
/**
* value for "width" & "height" attribute of <img>.
* Will have no effect if default slot is overridden
*/
size?: number;
/**
* value for "alt" attribute of <img>.
* Default to uiAvatar.name or gravatar.email if any.
* Will have no effect if default slot is overridden
*/
alt?: string;
class?: string;
}

/**
* URL or option to fallback when email hash returns no match from Gravatar.
* See {@link https://en.gravatar.com/site/implement/images | Gravatar } for
Expand All @@ -80,11 +18,6 @@ export type GravatarDefault =
| 'blank'
| `${'http' | 'https'}://${string}.${'png' | 'jpg' | 'jpeg' | 'gif'}`;

/**
* Options for building {@link https://en.gravatar.com/site/implement/images | Gravatar } url.
* Each option should map to a supported Gravatar query param
*
*/
export interface GravatarOptions {
/** email to md5-hash */
email: string;
Expand Down Expand Up @@ -126,5 +59,49 @@ export interface UIAvatarOptions {
format?: 'svg' | 'png';
}

// eslint-disable-next-line @typescript-eslint/ban-types
export default class Avatar extends SvelteComponent<AvatarProps, {}, AvatarSlots> {}
/**
* Props to Avatar component
*/
export interface AvatarProps extends HTMLImgAttributes {
/**
* "src" attribute. This option is of highest priority
*/
src?: string;
/**
* Either the email for {@link https://en.gravatar.com/site/implement/images | Gravatar }
* or {@link GravatarOptions } object. This option take second priority.
*
* By default, the Gravatar `default` query is set to 404, so that if gravatar is not valid,
* the next priority url will be used. Be careful when setting this `default` to other values,
* as gravatar will then always return a valid resource, hence stopping the resolution flow
* (i.e uiAvatar & fallback will never be used).
*/
gravatar?: GravatarOptions | string;
/**
* Either the name for building initials with {@link https://ui-avatars.com | UIAvatar }
* or {@link UIAvatarOptions } object. This option take third priority.
*/
uiAvatar?: string | UIAvatarOptions;
/**
* The fallback url string. This option takes lowest priority.
* It defaults to the internal fallback (https://www.gravatar.com/avatar?d=mp).
* If you provide a different url that is not a valid resource, the internal fallback
* will be used.
*/
fallback?: string;
/**
* value for "width" & "height" attribute of <img>.
* Will have no effect if default slot is overridden
*/
size?: number;
/**
* value for "alt" attribute of <img>.
* Default to uiAvatar.name or gravatar.email if any.
* Will have no effect if default slot is overridden
*/
alt?: string;
img?: Snippet<[{ src: string; size: number; alt: string; sources: string[] }]>;
}

// eslint-disable-next-line @typescript-eslint/no-explicit-any
export default class Avatar extends SvelteComponent<AvatarProps, any, any> {}
6 changes: 3 additions & 3 deletions packages/avatar/src/avatar.helpers.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import md5 from 'md5';
/**
* Builds a {@link https://ui-avatars.com | UIAvatar } url
*
* @param {string | import('./Avatar.svelte.d.ts').UIAvatarOptions} input - name for UIAvatar or object of options
* @param {string | import('./LegacyAvatar.svelte.js').UIAvatarOptions} input - name for UIAvatar or object of options
* @returns {string} the constructed UIAvatar URL
*/
export function uiAvatar(input) {
Expand All @@ -25,12 +25,12 @@ export function uiAvatar(input) {
/**
* Builds a {@link https://en.gravatar.com/site/implement/images | Gravatar } url
*
* @param {string | import('./Avatar.svelte.d.ts').GravatarOptions} input - email for Gravatar or object of options
* @param {string | import('./LegacyAvatar.svelte.js').GravatarOptions} input - email for Gravatar or object of options
* @returns {string} the constructed Gravatar URL
*/
export function gravatar(input) {
let email = '';
/** @type {Omit<import('./Avatar.svelte.d.ts').GravatarOptions, 'email'>} */
/** @type {Omit<import('./LegacyAvatar.svelte.js').GravatarOptions, 'email'>} */
let options = {};
if (typeof input === 'string') {
email = input;
Expand Down
12 changes: 6 additions & 6 deletions packages/avatar/src/avatar.utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -46,8 +46,8 @@ function nonNullableFilter(value) {
/**
* @package
* @param {string | undefined} alt
* @param {import('./Avatar.svelte.d.ts').AvatarProps['gravatar'] | undefined} gravatar
* @param {import('./Avatar.svelte.d.ts').AvatarProps['uiAvatar'] | undefined} uiAvatar
* @param {import('./Avatar.svelte').AvatarProps['gravatar'] | undefined} gravatar
* @param {import('./Avatar.svelte').AvatarProps['uiAvatar'] | undefined} uiAvatar
* @param {string | undefined } src
* @returns {string}
*/
Expand All @@ -71,8 +71,8 @@ export function resolveAlt(
* @param {number} fallback
* @param {number | undefined} size
* @param {string | undefined} src
* @param {import('./Avatar.svelte.d.ts').AvatarProps['gravatar'] | undefined} gravatar
* @param {import('./Avatar.svelte.d.ts').AvatarProps['uiAvatar'] | undefined} uiAvatar
* @param {import('./Avatar.svelte').AvatarProps['gravatar'] | undefined} gravatar
* @param {import('./Avatar.svelte').AvatarProps['uiAvatar'] | undefined} uiAvatar
* @returns {number}
*/
export function resolveSize(
Expand All @@ -98,8 +98,8 @@ export const DEFINITIVE_FALLBACK = 'https://www.gravatar.com/avatar?d=mp';
/**
* @package
* @param {string | undefined} src
* @param {import('./Avatar.svelte.d.ts').AvatarProps['gravatar'] | undefined} gravatar
* @param {import('./Avatar.svelte.d.ts').AvatarProps['uiAvatar'] | undefined} uiAvatar
* @param {import('./Avatar.svelte').AvatarProps['gravatar'] | undefined} gravatar
* @param {import('./Avatar.svelte').AvatarProps['uiAvatar'] | undefined} uiAvatar
* @param {string | undefined} fallback
* @returns {string[]}
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,12 @@
let uiAvatarVariant = 'Minimal';
</script>

<!--
TODO add migration docs
- drop support for v4 and below
- no more default slot, use the `img` snippet instead
-->

## Installation

<enhanced-code-block group display="tabs" bind:title={$packageManager}>
Expand Down
43 changes: 30 additions & 13 deletions sites/v5-playground/src/routes/+page.svelte
Original file line number Diff line number Diff line change
@@ -1,18 +1,35 @@
<script>
// eslint-disable-next-line no-undef
let name = $state('world');
let obj = {
key: 'value',
};
function func() {
return 'func';
}
import Avatar from '@svelte-put/avatar/Avatar.svelte';
/** passed from parent */
export let color = 'blue';
</script>

<h2>Hello {name}! {obj.key} {func()} {name === 'a' ? 1 : 2}</h2>

<h3 id="override">Something H3</h3>
<main style:padding="200px">
<Avatar
src="https://img.freepik.com/free-photo/adorable-jack-russell-retriever-puppy-portrait_53876-64825.jpg?w=2000"
uiAvatar="Jim+Hopper"
size={50}
class="custom-avatar rounded-full"
--border-color={color}
/>

<svg data-inline-src="phosphor/acorn" width="500" height="500" class="text-blue-500"></svg>
<div style:margin-top="40px">
<Avatar
size={50}
gravatar="[email protected]"
uiAvatar="Billy+Hargrove"
>
{#snippet img({ alt, size, sources, src })}
<img {src} {alt} width={size} height={size} data-sources={sources} />
{/snippet}
</Avatar>
</div>
</main>

<p>something else</p>
<style lang="postcss">
:global(.custom-avatar) {
border: 2px var(--border-color) solid;
box-shadow: 0 0 0 10px hsl(0deg 0% 50%), 0 0 0 15px hsl(0deg 0% 60%),
0 0 0 20px hsl(0deg 0% 70%), 0 0 0 25px hsl(0deg 0% 80%), 0 0 0 30px hsl(0deg 0% 90%);
}
</style>

0 comments on commit 5c05a51

Please sign in to comment.