lutra 0.1.0 → 0.1.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (154) hide show
  1. package/dist/components/Avatar.svelte +105 -0
  2. package/dist/components/Avatar.svelte.d.ts +14 -0
  3. package/dist/components/Close.svelte +76 -0
  4. package/dist/components/Close.svelte.d.ts +7 -0
  5. package/dist/components/ContextTip.svelte +41 -0
  6. package/dist/components/ContextTip.svelte.d.ts +7 -0
  7. package/dist/components/Icon.svelte +62 -0
  8. package/dist/components/Icon.svelte.d.ts +8 -0
  9. package/dist/components/IconButton.svelte +120 -0
  10. package/dist/components/IconButton.svelte.d.ts +16 -0
  11. package/dist/components/Image.svelte +172 -0
  12. package/dist/components/Image.svelte.d.ts +56 -0
  13. package/dist/components/Indicator.svelte +387 -0
  14. package/dist/components/Indicator.svelte.d.ts +12 -0
  15. package/dist/components/Inset.svelte +23 -0
  16. package/dist/components/Inset.svelte.d.ts +7 -0
  17. package/dist/components/Layout.svelte +2 -1
  18. package/dist/components/MenuDropdown.svelte +195 -0
  19. package/dist/components/MenuDropdown.svelte.d.ts +16 -0
  20. package/dist/components/MenuItem.svelte +159 -0
  21. package/dist/components/MenuItem.svelte.d.ts +11 -0
  22. package/dist/components/MenuItemContent.svelte +25 -0
  23. package/dist/components/MenuItemContent.svelte.d.ts +10 -0
  24. package/dist/components/MenuTypes.d.ts +79 -0
  25. package/dist/components/MenuTypes.js +1 -0
  26. package/dist/components/Modal.svelte +149 -0
  27. package/dist/components/Modal.svelte.d.ts +16 -0
  28. package/dist/components/Notification.svelte +115 -0
  29. package/dist/components/Notification.svelte.d.ts +12 -0
  30. package/dist/components/Overlay.svelte +31 -0
  31. package/dist/components/Overlay.svelte.d.ts +14 -0
  32. package/dist/components/OverlayContainer.svelte +31 -0
  33. package/dist/components/OverlayContainer.svelte.d.ts +18 -0
  34. package/dist/components/OverlayLayer.svelte +168 -0
  35. package/dist/components/OverlayLayer.svelte.d.ts +8 -0
  36. package/dist/components/PageContent.svelte +4 -82
  37. package/dist/components/PageContent.svelte.d.ts +0 -31
  38. package/dist/components/TabbedContent.svelte +74 -0
  39. package/dist/components/TabbedContent.svelte.d.ts +11 -0
  40. package/dist/components/TabbedContentItem.svelte +33 -0
  41. package/dist/components/TabbedContentItem.svelte.d.ts +10 -0
  42. package/dist/components/Table.svelte +41 -0
  43. package/dist/components/Table.svelte.d.ts +13 -0
  44. package/dist/components/Tabs.svelte +216 -0
  45. package/dist/components/Tabs.svelte.d.ts +20 -0
  46. package/dist/components/Tag.svelte +120 -0
  47. package/dist/components/Tag.svelte.d.ts +21 -0
  48. package/dist/components/Theme.svelte +32 -14
  49. package/dist/components/Tooltip.svelte +8 -8
  50. package/dist/components/UIContent.svelte +19 -0
  51. package/dist/components/UIContent.svelte.d.ts +7 -0
  52. package/dist/components/index.d.ts +28 -0
  53. package/dist/components/index.js +29 -0
  54. package/dist/components/notifications.svelte.d.ts +21 -0
  55. package/dist/components/notifications.svelte.js +30 -0
  56. package/dist/components/overlays.svelte.d.ts +36 -0
  57. package/dist/components/overlays.svelte.js +44 -0
  58. package/dist/css/1-props.css +389 -724
  59. package/dist/css/2-base.css +257 -123
  60. package/dist/css/3-typo.css +74 -34
  61. package/dist/css/4-layout.css +364 -1
  62. package/dist/css/5-media.css +106 -11
  63. package/dist/css/lutra.css +2 -1
  64. package/dist/css/themes/DefaultTheme.css +209 -0
  65. package/dist/form/Button.svelte +58 -0
  66. package/dist/form/Button.svelte.d.ts +15 -0
  67. package/dist/form/Datepicker.svelte +311 -0
  68. package/dist/form/Datepicker.svelte.d.ts +9 -0
  69. package/dist/form/FieldContent.svelte +178 -0
  70. package/dist/form/FieldContent.svelte.d.ts +21 -0
  71. package/dist/form/FieldError.svelte +24 -0
  72. package/dist/form/FieldError.svelte.d.ts +7 -0
  73. package/dist/form/Fieldset.svelte +103 -0
  74. package/dist/form/Fieldset.svelte.d.ts +20 -0
  75. package/dist/form/Form.svelte +220 -0
  76. package/dist/form/Form.svelte.d.ts +38 -0
  77. package/dist/form/FormActions.svelte +80 -0
  78. package/dist/form/FormActions.svelte.d.ts +9 -0
  79. package/dist/form/FormSection.svelte +96 -0
  80. package/dist/form/FormSection.svelte.d.ts +9 -0
  81. package/dist/form/ImageUpload.svelte +299 -0
  82. package/dist/form/ImageUpload.svelte.d.ts +20 -0
  83. package/dist/form/Input.svelte +444 -0
  84. package/dist/form/Input.svelte.d.ts +108 -0
  85. package/dist/form/InputLength.svelte +42 -0
  86. package/dist/form/InputLength.svelte.d.ts +9 -0
  87. package/dist/form/Label.svelte +88 -0
  88. package/dist/form/Label.svelte.d.ts +16 -0
  89. package/dist/form/LogoUpload.svelte +115 -0
  90. package/dist/form/LogoUpload.svelte.d.ts +18 -0
  91. package/dist/form/Select.svelte +186 -0
  92. package/dist/form/Select.svelte.d.ts +59 -0
  93. package/dist/form/Textarea.svelte +265 -0
  94. package/dist/form/Textarea.svelte.d.ts +95 -0
  95. package/dist/form/Toggle.svelte +4 -0
  96. package/dist/form/Toggle.svelte.d.ts +18 -0
  97. package/dist/form/client.svelte.d.ts +45 -0
  98. package/dist/form/client.svelte.js +102 -0
  99. package/dist/form/form.d.ts +55 -0
  100. package/dist/form/form.js +345 -0
  101. package/dist/form/index.d.ts +17 -0
  102. package/dist/form/index.js +17 -0
  103. package/dist/form/types.d.ts +55 -0
  104. package/dist/form/types.js +1 -0
  105. package/dist/icons/IconAlert.svelte +3 -0
  106. package/dist/icons/IconAlert.svelte.d.ts +26 -0
  107. package/dist/icons/IconCopy.svelte +3 -0
  108. package/dist/icons/IconCopy.svelte.d.ts +26 -0
  109. package/dist/icons/IconDone.svelte +3 -0
  110. package/dist/icons/IconDone.svelte.d.ts +26 -0
  111. package/dist/icons/IconError.svelte +3 -0
  112. package/dist/icons/IconError.svelte.d.ts +26 -0
  113. package/dist/icons/IconHelp.svelte +3 -0
  114. package/dist/icons/IconHelp.svelte.d.ts +26 -0
  115. package/dist/icons/IconHide.svelte +3 -0
  116. package/dist/icons/IconHide.svelte.d.ts +26 -0
  117. package/dist/icons/IconInfo.svelte +3 -0
  118. package/dist/icons/IconInfo.svelte.d.ts +26 -0
  119. package/dist/icons/IconLink.svelte +3 -0
  120. package/dist/icons/IconLink.svelte.d.ts +26 -0
  121. package/dist/icons/IconMenuBurger.svelte +3 -0
  122. package/dist/icons/IconMenuBurger.svelte.d.ts +26 -0
  123. package/dist/icons/IconMenuDots.svelte +3 -0
  124. package/dist/icons/IconMenuDots.svelte.d.ts +26 -0
  125. package/dist/icons/IconSearch.svelte +3 -0
  126. package/dist/icons/IconSearch.svelte.d.ts +26 -0
  127. package/dist/icons/IconShow.svelte +3 -0
  128. package/dist/icons/IconShow.svelte.d.ts +26 -0
  129. package/dist/icons/IconSuccess.svelte +3 -0
  130. package/dist/icons/IconSuccess.svelte.d.ts +26 -0
  131. package/dist/icons/IconWarning.svelte +3 -0
  132. package/dist/icons/IconWarning.svelte.d.ts +26 -0
  133. package/dist/icons/index.d.ts +14 -0
  134. package/dist/icons/index.js +14 -0
  135. package/dist/index.d.ts +3 -5
  136. package/dist/index.js +3 -5
  137. package/dist/util/StringOrComponent.svelte +20 -0
  138. package/dist/util/StringOrComponent.svelte.d.ts +8 -0
  139. package/dist/util/StringOrSnippet.svelte +16 -0
  140. package/dist/util/StringOrSnippet.svelte.d.ts +8 -0
  141. package/dist/util/attr.d.ts +5 -0
  142. package/dist/util/attr.js +21 -0
  143. package/dist/util/color.d.ts +51 -0
  144. package/dist/util/color.js +97 -0
  145. package/dist/util/dom.d.ts +15 -0
  146. package/dist/util/dom.js +73 -0
  147. package/dist/util/keyboard.svelte.d.ts +22 -0
  148. package/dist/util/keyboard.svelte.js +161 -0
  149. package/dist/util/locale.d.ts +1 -0
  150. package/dist/util/locale.js +47 -0
  151. package/dist/util/settings.d.ts +4 -0
  152. package/dist/util/settings.js +1 -0
  153. package/package.json +20 -11
  154. package/dist/css/0-layers.css +0 -1
@@ -0,0 +1,105 @@
1
+ <script lang="ts">
2
+ import type { Snippet } from 'svelte';
3
+ import { hexRelativeLuminance, stringToColor } from '../util/color.js';
4
+ import Image from './Image.svelte';
5
+
6
+ /**
7
+ * @description
8
+ * An avatar is a user profile photo or a logo. It can be a placeholder or a URL to an image. If there is no image, the first letter of the name will be used as a placeholder.
9
+ * You can pick from three crop styles: circle, square, or rounded. The color of the placeholder is based on the name, making each visually distinct from each other.
10
+ * @cssprop --border-radius - The border radius of the avatar when cropped as rounded.
11
+ * @cssprop --mask-image - Custom mask image to use for the avatar.
12
+ * @example
13
+ * <p>With picture:</p>
14
+ * <Avatar name="Auth70" shape="rounded" src="https://avatars.githubusercontent.com/u/122825113?s=200&v=4" --size="4rem" />
15
+ * <p>Without picture:</p>
16
+ * <Avatar name="Moxie" shape="square" />
17
+ * <Avatar name="Napoleon" shape="rounded" />
18
+ * <Avatar name="Byron" shape="circle" />
19
+ */
20
+ let {
21
+ src,
22
+ srcset,
23
+ alt,
24
+ color,
25
+ size = '3rem',
26
+ name = "N/A",
27
+ shape = 'circle',
28
+ children,
29
+ }: {
30
+ src?: string;
31
+ srcset?: string;
32
+ alt?: string;
33
+ color?: string;
34
+ size?: string;
35
+ name?: string;
36
+ shape?: 'circle' | 'square' | 'rounded';
37
+ children?: Snippet;
38
+ } = $props();
39
+
40
+ const userColor = (src || srcset) ? '#666666' : color ?? stringToColor(name);
41
+ const luminance = hexRelativeLuminance(userColor);
42
+ const textColor = luminance < 0.5 ? 'rgba(255,255,255,0.85)' : 'rgba(0,0,0,0.85)';
43
+ const firstCharacter = [...name][0]; // supports multi-byte characters. name[0] would not.
44
+ </script>
45
+
46
+ <figure class="Avatar {shape}" style="--user-color: {userColor}; --text-color: {textColor}; --size: {size}">
47
+ {#if src}
48
+ <Image
49
+ src={src}
50
+ srcset={srcset}
51
+ alt={alt ?? name}
52
+ fit="cover"
53
+ --width={size}
54
+ --height={size}
55
+ />
56
+ {:else}
57
+ <div class="Placeholder">
58
+ {#if children}
59
+ {@render children()}
60
+ {:else}
61
+ <span>{firstCharacter}</span>
62
+ {/if}
63
+ </div>
64
+ {/if}
65
+ </figure>
66
+
67
+ <style>
68
+ .Avatar {
69
+ display: flex;
70
+ align-items: center;
71
+ justify-content: center;
72
+ margin: 0;
73
+ block-size: var(--size, 3rem);
74
+ inline-size: var(--size, 3rem);
75
+ overflow: clip;
76
+ background-color: var(--user-color);
77
+ mask-image: var(--mask-image, none);
78
+ mask-size: 100% 100%;
79
+ mask-repeat: no-repeat;
80
+ -webkit-mask-image: var(--mask-image, none);
81
+ -webkit-mask-size: 100% 100%;
82
+ -webkit-mask-repeat: no-repeat;
83
+ }
84
+ .Avatar.circle { border-radius: 50%; }
85
+ .Avatar.square { border-radius: 0; }
86
+ .Avatar.rounded { border-radius: var(--border-radius); }
87
+ .Avatar img {
88
+ block-size: 100%;
89
+ inline-size: 100%;
90
+ object-fit: cover;
91
+ }
92
+ .Avatar .Placeholder {
93
+ display: flex;
94
+ align-items: center;
95
+ justify-content: center;
96
+ block-size: 100%;
97
+ inline-size: 100%;
98
+ background-color: var(--user-color);
99
+ color: var(--text-color);
100
+ user-select: none;
101
+ font-size: 1rem;
102
+ font-weight: 600;
103
+ user-select: none;
104
+ }
105
+ </style>
@@ -0,0 +1,14 @@
1
+ import type { Snippet } from 'svelte';
2
+ type $$ComponentProps = {
3
+ src?: string;
4
+ srcset?: string;
5
+ alt?: string;
6
+ color?: string;
7
+ size?: string;
8
+ name?: string;
9
+ shape?: 'circle' | 'square' | 'rounded';
10
+ children?: Snippet;
11
+ };
12
+ declare const Avatar: import("svelte").Component<$$ComponentProps, {}, "">;
13
+ type Avatar = ReturnType<typeof Avatar>;
14
+ export default Avatar;
@@ -0,0 +1,76 @@
1
+ <script lang="ts">
2
+ let {
3
+ onclick,
4
+ position
5
+ }: {
6
+ onclick?: (e: MouseEvent) => void;
7
+ position?: "top left" | "top right" | "bottom left" | "bottom right";
8
+ } = $props();
9
+ </script>
10
+
11
+ <button class="Close {position}" {onclick} aria-label="Close">
12
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
13
+ <line x1="18" y1="6" x2="6" y2="18"></line>
14
+ <line x1="6" y1="6" x2="18" y2="18"></line>
15
+ </svg>
16
+ </button>
17
+
18
+ <style>
19
+ .Close {
20
+ cursor: pointer;
21
+ padding: var(--close-padding, 0.5rem);
22
+ border-radius: 50%;
23
+ color: var(--text-color, light-dark(black, white));
24
+ cursor: pointer;
25
+ pointer-events: auto;
26
+ border: none;
27
+ }
28
+
29
+ button {
30
+ background-color: transparent;
31
+ border: none;
32
+ padding: 0;
33
+ margin: 0;
34
+ font: inherit;
35
+ color: inherit;
36
+ text-align: inherit;
37
+ display: inline-block;
38
+ }
39
+
40
+ svg {
41
+ display: block;
42
+ margin: 0;
43
+ width: max(1.5rem, 16px);
44
+ height: max(1.5rem, 16px);
45
+ }
46
+
47
+ .Close:hover {
48
+ color: var(--text-color-subtle, light-dark(rgba(0, 0, 0, 0.5), rgba(255, 255, 255, 0.5)));
49
+ }
50
+
51
+ .Close.top {
52
+ position: absolute;
53
+ top: 0;
54
+ right: 0;
55
+ z-index: 100;
56
+ }
57
+
58
+ .Close.bottom {
59
+ position: absolute;
60
+ bottom: 0;
61
+ right: 0;
62
+ }
63
+
64
+ .Close.left {
65
+ position: absolute;
66
+ top: 0;
67
+ left: 0;
68
+ right: auto;
69
+ }
70
+
71
+ .Close.right {
72
+ position: absolute;
73
+ top: 0;
74
+ right: 0;
75
+ }
76
+ </style>
@@ -0,0 +1,7 @@
1
+ type $$ComponentProps = {
2
+ onclick?: (e: MouseEvent) => void;
3
+ position?: "top left" | "top right" | "bottom left" | "bottom right";
4
+ };
5
+ declare const Close: import("svelte").Component<$$ComponentProps, {}, "">;
6
+ type Close = ReturnType<typeof Close>;
7
+ export default Close;
@@ -0,0 +1,41 @@
1
+ <script lang="ts">
2
+ import type { Snippet } from "svelte";
3
+ import Tooltip from "./Tooltip.svelte";
4
+ import Help from "../icons/IconHelp.svelte";
5
+ import Icon from "./Icon.svelte";
6
+
7
+ /**
8
+ * @description
9
+ * A component that displays a context tip.
10
+ * @example
11
+ * <ContextTip tip="This is a context tip" />
12
+ */
13
+ let {
14
+ tip
15
+ }: {
16
+ tip: string | Snippet;
17
+ } = $props();
18
+ </script>
19
+
20
+ <Tooltip {tip}>
21
+ <a href="#contexttip" onclick={(e) => { e.preventDefault(); e.stopPropagation(); }}>
22
+ <Icon icon={Help} --icon-width="16px" --icon-height="16px" --cursor="help" --vertical-align="baseline" />
23
+ </a>
24
+ </Tooltip>
25
+
26
+ <style>
27
+ a {
28
+ border-radius: 50%;
29
+ color: var(--overlay-color, light-dark(black, white));
30
+ height: 16px;
31
+ width: 16px;
32
+ padding: 0;
33
+ display: inline-flex;
34
+ align-items: center;
35
+ justify-content: center;
36
+ }
37
+ a:focus-visible {
38
+ color: var(--focus-ring-color);
39
+ outline-offset: 0px;
40
+ }
41
+ </style>
@@ -0,0 +1,7 @@
1
+ import type { Snippet } from "svelte";
2
+ type $$ComponentProps = {
3
+ tip: string | Snippet;
4
+ };
5
+ declare const ContextTip: import("svelte").Component<$$ComponentProps, {}, "">;
6
+ type ContextTip = ReturnType<typeof ContextTip>;
7
+ export default ContextTip;
@@ -0,0 +1,62 @@
1
+ <script lang="ts">
2
+ import type { Component } from "svelte";
3
+
4
+ /**
5
+ * @description
6
+ * A component that displays an icon. It can be an image url or a component. The icon will be centered in the container.
7
+ * @cssprop --icon-width - The width of the icon. (Default: var(--font-size, 1em))
8
+ * @cssprop --icon-height - The height of the icon. (Default: var(--font-size, 1em))
9
+ * @cssprop --icon-color - The color of the icon. (Default: var(--text-color, currentColor))
10
+ * @example
11
+ * <script>
12
+ * import Copy from 'lutra/Icons/Copy.svelte';
13
+ * import Link from 'lutra/Icons/Link.svelte';
14
+ * <\/script>
15
+ * <Icon icon={Link} alt="Link icon" />
16
+ * <Icon icon={Copy} alt="Copy icon" />
17
+ */
18
+
19
+ let {
20
+ icon,
21
+ alt,
22
+ }: {
23
+ icon: string | Component;
24
+ alt?: string,
25
+ } = $props();
26
+
27
+ let Icon = icon as Component;
28
+
29
+ </script>
30
+
31
+ <span class="Icon" aria-label="icon">
32
+ {#if typeof icon === "string"}
33
+ <img src={icon} alt={alt ? alt : 'Icon'} width="100%" height="100%" />
34
+ {:else}
35
+ <Icon />
36
+ {/if}
37
+ </span>
38
+
39
+ <style>
40
+ .Icon {
41
+ display: inline-flex;
42
+ align-items: center;
43
+ justify-content: center;
44
+ font-size: 1em;
45
+ line-height: inherit;
46
+ width: max(
47
+ var(--icon-width,
48
+ var(--icon-height, 1rem)
49
+ ), 16px);
50
+ height: max(var(--icon-height, auto), 16px);
51
+ color: var(--icon-color, currentColor);
52
+ overflow: clip;
53
+ vertical-align: var(--vertical-align, text-bottom);
54
+ cursor: var(--cursor, default);
55
+ }
56
+ img,
57
+ .Icon :global(svg) {
58
+ width: 100%;
59
+ height: auto;
60
+ display: block;
61
+ }
62
+ </style>
@@ -0,0 +1,8 @@
1
+ import type { Component } from "svelte";
2
+ type $$ComponentProps = {
3
+ icon: string | Component;
4
+ alt?: string;
5
+ };
6
+ declare const Icon: Component<$$ComponentProps, {}, "">;
7
+ type Icon = ReturnType<typeof Icon>;
8
+ export default Icon;
@@ -0,0 +1,120 @@
1
+ <script lang="ts">
2
+ import type { Component, Snippet } from "svelte";
3
+ import Icon from './Icon.svelte';
4
+ import { fly } from "svelte/transition";
5
+ import { popBezier, popBezierInverse } from "../util/transitions.js";
6
+
7
+ /**
8
+ * @description
9
+ * A component that displays an icon with a possible label. It can also have a click event.
10
+ * The button has a padding of 0.75em by default to make it easier to tap on mobile devices. The padding can be changed using the `--padding` CSS variable.
11
+ * Icon and text color will be inherited from the parent element.
12
+ * @cssprop --padding - The padding of the icon button. (Default: 0.75em)
13
+ * @example
14
+ * <script>
15
+ * import Copy from 'lutra/Icons/Copy.svelte';
16
+ * import Link from 'lutra/Icons/Link.svelte';
17
+ * <\/script>
18
+ * <IconButton icon={Link} onclick={() => alert('clicked')} />
19
+ * <IconButton icon={Copy} onclick={() => alert('clicked')}>Copy</IconButton>
20
+ */
21
+ let {
22
+ icon,
23
+ disabled,
24
+ children,
25
+ onclick,
26
+ mask = true
27
+ }: {
28
+ /** The icon to display. */
29
+ icon: string | Component;
30
+ /** The children to display. */
31
+ children?: Snippet,
32
+ /** The onclick event. */
33
+ onclick?: (event: MouseEvent) => void,
34
+ /** Whether the button is disabled. */
35
+ disabled?: boolean,
36
+ /** Whether to mask the content. */
37
+ mask?: boolean
38
+ } = $props();
39
+
40
+ </script>
41
+
42
+ {#snippet inside()}
43
+ <span class="IconMask" class:mask>
44
+ {#key icon}
45
+ <span class="IconContent" in:fly={{ duration: 250, y: -32, easing: popBezier }} out:fly={{ duration: 250, y: 32, easing: popBezierInverse }}>
46
+ <Icon {icon} --cursor="pointer" />
47
+ {#if children}
48
+ <span class="Text">
49
+ {@render children()}
50
+ </span>
51
+ {/if}
52
+ </span>
53
+ {/key}
54
+ </span>
55
+ {/snippet}
56
+
57
+ {#if onclick}
58
+ <button type="button" {disabled} class="IconButton" {onclick}>
59
+ {@render inside()}
60
+ </button>
61
+ {:else}
62
+ <span class="IconButton">
63
+ {@render inside()}
64
+ </span>
65
+ {/if}
66
+
67
+ <style>
68
+ .IconButton {
69
+ display: inline-flex;
70
+ align-items: stretch;
71
+ color: inherit;
72
+ opacity: 1;
73
+ background-color: var(--field-background-color, transparent);
74
+ transition: background-color 0.04s;
75
+ border-radius: var(--field-border-radius);
76
+ }
77
+ .IconButton:hover {
78
+ background-color: var(--menu-background-color-hover);
79
+ }
80
+ .IconButton:active {
81
+ scale: 1;
82
+ }
83
+ button.IconButton {
84
+ border: none;
85
+ background: none;
86
+ cursor: pointer;
87
+ color: var(--text-color-p, light-dark(black, white));
88
+ }
89
+ .IconMask {
90
+ height: 100%;
91
+ padding-inline: calc(var(--padding, 0.75em) * 0.8);
92
+ padding-block: calc(var(--padding, 0.75em) * 0.8);
93
+ display: inline-grid;
94
+ gap: 0.5rem;
95
+ grid-template: "icon";
96
+ align-items: center;
97
+ }
98
+ .IconMask.mask {
99
+ -webkit-mask-image: linear-gradient(to bottom, rgba(0, 0, 0, 0), rgba(0, 0, 0, 1) 35%, rgba(0, 0, 0, 1) 65%, rgba(0, 0, 0, 0));
100
+ mask-image: linear-gradient(to bottom, rgba(0, 0, 0, 0), rgba(0, 0, 0, 1) 35%, rgba(0, 0, 0, 1) 65%, rgba(0, 0, 0, 0));
101
+ }
102
+ .IconContent {
103
+ grid-area: icon;
104
+ display: inline-flex;
105
+ gap: 0.5rem;
106
+ align-items: center;
107
+ }
108
+ @media(max-width: 960px) {
109
+ .IconMask {
110
+ padding-inline: calc(var(--padding, 0.75em) * 0.75);
111
+ padding-block: calc(var(--padding, 0.75em) * 0.75);
112
+ }
113
+ }
114
+ @media(max-width: 320px) {
115
+ .IconMask {
116
+ padding-inline: calc(var(--padding, 0.75em) * 0.6);
117
+ padding-block: calc(var(--padding, 0.75em) * 0.6);
118
+ }
119
+ }
120
+ </style>
@@ -0,0 +1,16 @@
1
+ import type { Component, Snippet } from "svelte";
2
+ type $$ComponentProps = {
3
+ /** The icon to display. */
4
+ icon: string | Component;
5
+ /** The children to display. */
6
+ children?: Snippet;
7
+ /** The onclick event. */
8
+ onclick?: (event: MouseEvent) => void;
9
+ /** Whether the button is disabled. */
10
+ disabled?: boolean;
11
+ /** Whether to mask the content. */
12
+ mask?: boolean;
13
+ };
14
+ declare const IconButton: Component<$$ComponentProps, {}, "">;
15
+ type IconButton = ReturnType<typeof IconButton>;
16
+ export default IconButton;
@@ -0,0 +1,172 @@
1
+ <script lang="ts">
2
+ /**
3
+ * @description
4
+ * An image component that can be used to display images with different aspect ratios and fit modes.
5
+ * Can also decode a BlurHash.
6
+ * @cssprop --width - The width of the image.
7
+ * @cssprop --height - The height of the image.
8
+ * @example
9
+ * <Image aspectRatio="16:9" fit="cover" src="https://images.unsplash.com/photo-1712337646541-d0c6f85447f8" alt="An example image" />
10
+ */
11
+
12
+ import { browser } from "$app/environment";
13
+ import { decode } from "blurhash";
14
+ import { fade } from "svelte/transition";
15
+
16
+ let {
17
+ hash,
18
+ width,
19
+ height,
20
+ aspectRatio,
21
+ fit,
22
+ src,
23
+ srcset,
24
+ alt,
25
+ style = '',
26
+ class: className,
27
+ ...rest
28
+ }: {
29
+ /**
30
+ * @description
31
+ * A BlurHash string to decode into an image.
32
+ */
33
+ hash?: string;
34
+ /**
35
+ * @description
36
+ * The aspect ratio of the image.
37
+ */
38
+ aspectRatio?: string;
39
+ /**
40
+ * @description
41
+ * The fit mode of the image.
42
+ * @default 'cover'
43
+ */
44
+ fit?: 'cover' | 'contain' | 'scale-down';
45
+ /**
46
+ * @description
47
+ * The source URL of the image.
48
+ */
49
+ src?: string;
50
+ /**
51
+ * @description
52
+ * The source URL of the image (with srcset).
53
+ */
54
+ srcset?: string;
55
+ /**
56
+ * @description
57
+ * The alt text of the image.
58
+ */
59
+ alt?: string;
60
+ /**
61
+ * @description
62
+ * The width of the image.
63
+ */
64
+ width?: string | number;
65
+ /**
66
+ * @description
67
+ * The height of the image.
68
+ */
69
+ height?: string | number;
70
+ /**
71
+ * @description
72
+ * Additional style attributes for the image.
73
+ */
74
+ style?: string;
75
+ /**
76
+ * @description
77
+ * Additional classes to apply to the image.
78
+ */
79
+ class?: string;
80
+ } = $props();
81
+
82
+ let canvas: HTMLCanvasElement | null = $state(null);
83
+ let el: HTMLImageElement | null = $state(null);
84
+ let elWidth = $state(32);
85
+ let elHeight = $state(32);
86
+
87
+ if(!src && srcset) {
88
+ const [src1] = srcset.split(", ");
89
+ src = src1.split(" ")[0];
90
+ }
91
+
92
+ if(aspectRatio) {
93
+ style += ` aspect-ratio: ${aspectRatio};`;
94
+ }
95
+
96
+ let decoded = $state(false);
97
+ let loaded = $state(browser ? false : true);
98
+
99
+ const onload = () => {
100
+ loaded = true;
101
+ };
102
+
103
+ $effect(() => {
104
+ if(hash) {
105
+ setTimeout(() => {
106
+ const pixels = decode(hash!, elWidth, elHeight);
107
+ const ctx = canvas!.getContext("2d");
108
+ const imageData = ctx!.createImageData(elWidth, elHeight);
109
+ imageData.data.set(pixels);
110
+ ctx!.putImageData(imageData, 0, 0);
111
+ decoded = true;
112
+ }, 0);
113
+ }
114
+ });
115
+
116
+ </script>
117
+
118
+ <div class="Image" class:loaded>
119
+ <img
120
+ bind:this={el}
121
+ bind:clientWidth={elWidth}
122
+ bind:clientHeight={elHeight}
123
+ {onload}
124
+ {src}
125
+ {srcset}
126
+ {alt}
127
+ width={width ? width : '100%'}
128
+ height={height ? height : '100%'}
129
+ {style}
130
+ class="{className} {fit}"
131
+ {...rest}
132
+ />
133
+ {#if !loaded && hash}
134
+ <canvas class:decoded out:fade={{ duration: 100 }} width={elWidth} height={elHeight} bind:this={canvas}></canvas>
135
+ {/if}
136
+ </div>
137
+
138
+ <style>
139
+ .Image {
140
+ position: relative;
141
+ display: grid;
142
+ grid-template-areas: "img";
143
+ width: var(--width, 100%);
144
+ height: var(--height, 100%);
145
+ }
146
+ canvas {
147
+ z-index: 2;
148
+ opacity: 0;
149
+ width: 100%;
150
+ grid-area: img;
151
+ transition: opacity var(--transition-speed-slow);
152
+ }
153
+ canvas.decoded {
154
+ opacity: 1;
155
+ }
156
+ img {
157
+ z-index: 1;
158
+ grid-area: img;
159
+ display: inline-block;
160
+ vertical-align: middle;
161
+ opacity: 0;
162
+ transition: opacity var(--transition-speed-slow);
163
+ width: var(--width, 100%);
164
+ height: var(--height, 100%);
165
+ }
166
+ img.cover { object-fit: cover; }
167
+ img.contain { object-fit: contain; }
168
+ img.scale-down { object-fit: scale-down; }
169
+ .Image.loaded img {
170
+ opacity: 1;
171
+ }
172
+ </style>
@@ -0,0 +1,56 @@
1
+ type $$ComponentProps = {
2
+ /**
3
+ * @description
4
+ * A BlurHash string to decode into an image.
5
+ */
6
+ hash?: string;
7
+ /**
8
+ * @description
9
+ * The aspect ratio of the image.
10
+ */
11
+ aspectRatio?: string;
12
+ /**
13
+ * @description
14
+ * The fit mode of the image.
15
+ * @default 'cover'
16
+ */
17
+ fit?: 'cover' | 'contain' | 'scale-down';
18
+ /**
19
+ * @description
20
+ * The source URL of the image.
21
+ */
22
+ src?: string;
23
+ /**
24
+ * @description
25
+ * The source URL of the image (with srcset).
26
+ */
27
+ srcset?: string;
28
+ /**
29
+ * @description
30
+ * The alt text of the image.
31
+ */
32
+ alt?: string;
33
+ /**
34
+ * @description
35
+ * The width of the image.
36
+ */
37
+ width?: string | number;
38
+ /**
39
+ * @description
40
+ * The height of the image.
41
+ */
42
+ height?: string | number;
43
+ /**
44
+ * @description
45
+ * Additional style attributes for the image.
46
+ */
47
+ style?: string;
48
+ /**
49
+ * @description
50
+ * Additional classes to apply to the image.
51
+ */
52
+ class?: string;
53
+ };
54
+ declare const Image: import("svelte").Component<$$ComponentProps, {}, "">;
55
+ type Image = ReturnType<typeof Image>;
56
+ export default Image;