@streamscloud/kit 0.2.1 → 0.2.3

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 (107) hide show
  1. package/dist/styles/_input.scss +98 -0
  2. package/dist/styles/_mixins.scss +2 -2
  3. package/dist/styles/reset.css +1 -1
  4. package/dist/ui/button/resources/button-base.svelte +2 -2
  5. package/dist/ui/button/resources/button-theme.svelte +18 -15
  6. package/dist/ui/color-picker/cmp.color-picker.svelte +3 -12
  7. package/dist/ui/color-picker/cmp.color-picker.svelte.d.ts +3 -9
  8. package/dist/ui/cropper/img-cropper/cmp.img-cropper-toolbar.svelte +1 -1
  9. package/dist/ui/dialog/cmp.dialog.svelte +1 -1
  10. package/dist/ui/dropdown/cmp.dropdown-item.svelte +93 -0
  11. package/dist/ui/dropdown/cmp.dropdown-item.svelte.d.ts +32 -0
  12. package/dist/ui/dropdown/cmp.dropdown-panel.svelte +29 -0
  13. package/dist/ui/dropdown/cmp.dropdown-panel.svelte.d.ts +18 -0
  14. package/dist/ui/dropdown/cmp.dropdown.svelte +72 -7
  15. package/dist/ui/dropdown/cmp.dropdown.svelte.d.ts +3 -1
  16. package/dist/ui/dropdown/index.d.ts +2 -0
  17. package/dist/ui/dropdown/index.js +2 -0
  18. package/dist/ui/dynamic-component/cmp.dynamic-component.svelte +0 -5
  19. package/dist/ui/dynamic-component/cmp.dynamic-component.svelte.d.ts +2 -8
  20. package/dist/ui/emoji-picker/cmp.emoji-panel.svelte +186 -0
  21. package/dist/ui/emoji-picker/cmp.emoji-panel.svelte.d.ts +21 -0
  22. package/dist/ui/emoji-picker/cmp.emoji-picker.svelte +35 -0
  23. package/dist/ui/emoji-picker/cmp.emoji-picker.svelte.d.ts +15 -0
  24. package/dist/ui/emoji-picker/emoji-list.d.ts +2 -0
  25. package/dist/ui/emoji-picker/emoji-list.js +1754 -0
  26. package/dist/ui/emoji-picker/emoji-picker-localization.d.ts +5 -0
  27. package/dist/ui/emoji-picker/emoji-picker-localization.js +40 -0
  28. package/dist/ui/emoji-picker/index.d.ts +2 -0
  29. package/dist/ui/emoji-picker/index.js +2 -0
  30. package/dist/ui/emoji-picker/types.d.ts +8 -0
  31. package/dist/ui/emoji-picker/types.js +1 -0
  32. package/dist/ui/form-group/cmp.form-group-label.svelte.d.ts +1 -0
  33. package/dist/ui/form-group/cmp.form-group.svelte.d.ts +1 -0
  34. package/dist/ui/icon-text/cmp.icon-text.svelte +34 -22
  35. package/dist/ui/icon-text/cmp.icon-text.svelte.d.ts +14 -13
  36. package/dist/ui/inputs/index.d.ts +6 -0
  37. package/dist/ui/inputs/index.js +5 -0
  38. package/dist/ui/inputs/input/cmp.input-validatable.svelte +57 -0
  39. package/dist/ui/inputs/input/cmp.input-validatable.svelte.d.ts +56 -0
  40. package/dist/ui/inputs/input/cmp.input.svelte +235 -0
  41. package/dist/ui/inputs/input/cmp.input.svelte.d.ts +60 -0
  42. package/dist/ui/inputs/input/index.d.ts +2 -0
  43. package/dist/ui/inputs/input/index.js +2 -0
  44. package/dist/ui/inputs/input-emoji-picker/cmp.input-emoji-picker.svelte +44 -0
  45. package/dist/ui/inputs/input-emoji-picker/cmp.input-emoji-picker.svelte.d.ts +9 -0
  46. package/dist/ui/inputs/input-emoji-picker/index.d.ts +2 -0
  47. package/dist/ui/inputs/input-emoji-picker/index.js +2 -0
  48. package/dist/ui/inputs/input-emoji-picker/input-emoji-picker-container.d.ts +2 -0
  49. package/dist/ui/inputs/input-emoji-picker/input-emoji-picker-container.js +16 -0
  50. package/dist/ui/inputs/numeral-input/cmp.numeral-input-validatable.svelte +55 -0
  51. package/dist/ui/inputs/numeral-input/cmp.numeral-input-validatable.svelte.d.ts +62 -0
  52. package/dist/ui/inputs/numeral-input/cmp.numeral-input.svelte +248 -0
  53. package/dist/ui/inputs/numeral-input/cmp.numeral-input.svelte.d.ts +66 -0
  54. package/dist/ui/inputs/numeral-input/index.d.ts +2 -0
  55. package/dist/ui/inputs/numeral-input/index.js +2 -0
  56. package/dist/ui/inputs/pin-input/cmp.pin-input.svelte +58 -0
  57. package/dist/ui/inputs/pin-input/cmp.pin-input.svelte.d.ts +23 -0
  58. package/dist/ui/inputs/pin-input/index.d.ts +1 -0
  59. package/dist/ui/inputs/pin-input/index.js +1 -0
  60. package/dist/ui/inputs/pin-input/pin-input-generator.d.ts +27 -0
  61. package/dist/ui/inputs/pin-input/pin-input-generator.js +114 -0
  62. package/dist/ui/inputs/rich-text-input/cmp.rich-text-input.svelte +55 -0
  63. package/dist/ui/inputs/rich-text-input/cmp.rich-text-input.svelte.d.ts +43 -0
  64. package/dist/ui/inputs/rich-text-input/index.d.ts +2 -0
  65. package/dist/ui/inputs/rich-text-input/index.js +1 -0
  66. package/dist/ui/inputs/rich-text-input/rich-text-input-localization.d.ts +12 -0
  67. package/dist/ui/inputs/rich-text-input/rich-text-input-localization.js +48 -0
  68. package/dist/ui/inputs/rich-text-input/tinymce-input.svelte +250 -0
  69. package/dist/ui/inputs/rich-text-input/tinymce-input.svelte.d.ts +25 -0
  70. package/dist/ui/inputs/rich-text-input/tinymce.declarations.d.ts +7 -0
  71. package/dist/ui/inputs/rich-text-input/types.d.ts +4 -0
  72. package/dist/ui/inputs/rich-text-input/types.js +1 -0
  73. package/dist/ui/inputs/rich-text-input/validated-link-button.d.ts +3 -0
  74. package/dist/ui/inputs/rich-text-input/validated-link-button.js +78 -0
  75. package/dist/ui/inputs/textarea/cmp.textarea-validatable.svelte +35 -0
  76. package/dist/ui/inputs/textarea/cmp.textarea-validatable.svelte.d.ts +53 -0
  77. package/dist/ui/inputs/textarea/cmp.textarea.svelte +247 -0
  78. package/dist/ui/inputs/textarea/cmp.textarea.svelte.d.ts +57 -0
  79. package/dist/ui/inputs/textarea/index.d.ts +2 -0
  80. package/dist/ui/inputs/textarea/index.js +2 -0
  81. package/dist/ui/media-viewer-dialog/cmp.media-viewer-dialog.svelte.d.ts +2 -0
  82. package/dist/ui/selects/_multiselect.scss +282 -0
  83. package/dist/ui/selects/_singleselect.scss +175 -0
  84. package/dist/ui/selects/cmp.multiselect.svelte +530 -0
  85. package/dist/ui/selects/cmp.multiselect.svelte.d.ts +85 -0
  86. package/dist/ui/selects/cmp.search-multiselect.svelte +532 -0
  87. package/dist/ui/selects/cmp.search-multiselect.svelte.d.ts +67 -0
  88. package/dist/ui/selects/cmp.singleselect.svelte +381 -0
  89. package/dist/ui/selects/cmp.singleselect.svelte.d.ts +78 -0
  90. package/dist/ui/selects/index.d.ts +5 -0
  91. package/dist/ui/selects/index.js +4 -0
  92. package/dist/ui/selects/select-localization.d.ts +6 -0
  93. package/dist/ui/selects/select-localization.js +27 -0
  94. package/dist/ui/selects/types.d.ts +29 -0
  95. package/dist/ui/selects/types.js +1 -0
  96. package/dist/ui/time-ago/cmp.time-ago.svelte +0 -6
  97. package/dist/ui/time-ago/cmp.time-ago.svelte.d.ts +2 -6
  98. package/dist/ui/validatable/_validatable.scss +34 -0
  99. package/dist/ui/validatable/cmp.validatable.svelte +57 -0
  100. package/dist/ui/validatable/cmp.validatable.svelte.d.ts +49 -0
  101. package/dist/ui/validatable/cmp.validation-error.svelte +52 -0
  102. package/dist/ui/validatable/cmp.validation-error.svelte.d.ts +42 -0
  103. package/dist/ui/validatable/index.d.ts +2 -0
  104. package/dist/ui/validatable/index.js +2 -0
  105. package/package.json +31 -5
  106. package/dist/ui/color-picker/cmp.input-stub.svelte +0 -98
  107. package/dist/ui/color-picker/cmp.input-stub.svelte.d.ts +0 -40
@@ -0,0 +1,5 @@
1
+ export declare class EmojiPickerLocalization {
2
+ get searchPlaceholder(): string;
3
+ get notFound(): string;
4
+ get categories(): Record<string, string>;
5
+ }
@@ -0,0 +1,40 @@
1
+ import { AppLocale } from '../../core/locale';
2
+ const loc = {
3
+ searchPlaceholder: { en: 'Search emoji...', no: 'Sok etter emoji...' },
4
+ notFound: { en: 'No emoji found', no: 'Ingen emoji funnet' }
5
+ };
6
+ const categories = {
7
+ emotions: { en: 'Smileys & Emotion', no: 'Smileys og folelser' },
8
+ people: { en: 'People & Body', no: 'Mennesker og kropp' },
9
+ nature: { en: 'Animals & Nature', no: 'Dyr og natur' },
10
+ food: { en: 'Food & Drink', no: 'Mat og drikke' },
11
+ travel: { en: 'Travel & Places', no: 'Reise og steder' },
12
+ activities: { en: 'Activities', no: 'Aktiviteter' },
13
+ objects: { en: 'Objects', no: 'Objekter' },
14
+ symbols: { en: 'Symbols', no: 'Symboler' },
15
+ flags: { en: 'Flags', no: 'Flagg' },
16
+ search: { en: 'Search Results', no: 'Sokeresultater' }
17
+ };
18
+ export class EmojiPickerLocalization {
19
+ get searchPlaceholder() {
20
+ return loc.searchPlaceholder[AppLocale.current];
21
+ }
22
+ get notFound() {
23
+ return loc.notFound[AppLocale.current];
24
+ }
25
+ get categories() {
26
+ const l = AppLocale.current;
27
+ return {
28
+ emotions: categories.emotions[l],
29
+ people: categories.people[l],
30
+ nature: categories.nature[l],
31
+ food: categories.food[l],
32
+ travel: categories.travel[l],
33
+ activities: categories.activities[l],
34
+ objects: categories.objects[l],
35
+ symbols: categories.symbols[l],
36
+ flags: categories.flags[l],
37
+ search: categories.search[l]
38
+ };
39
+ }
40
+ }
@@ -0,0 +1,2 @@
1
+ export { default as EmojiPanel } from './cmp.emoji-panel.svelte';
2
+ export { default as EmojiPicker } from './cmp.emoji-picker.svelte';
@@ -0,0 +1,2 @@
1
+ export { default as EmojiPanel } from './cmp.emoji-panel.svelte';
2
+ export { default as EmojiPicker } from './cmp.emoji-picker.svelte';
@@ -0,0 +1,8 @@
1
+ export type Emoji = {
2
+ text: string;
3
+ unicode: string;
4
+ };
5
+ export type EmojiCategory = {
6
+ name: string;
7
+ emojis: Emoji[];
8
+ };
@@ -0,0 +1 @@
1
+ export {};
@@ -1,5 +1,6 @@
1
1
  import type { Snippet } from 'svelte';
2
2
  type Props = {
3
+ /** Enable flex layout for label + tooltip icon */
3
4
  withTooltip?: boolean;
4
5
  children: Snippet;
5
6
  };
@@ -1,5 +1,6 @@
1
1
  import type { Snippet } from 'svelte';
2
2
  type Props = {
3
+ /** Remove bottom margin */
3
4
  noMargin?: boolean;
4
5
  children: Snippet;
5
6
  };
@@ -1,7 +1,9 @@
1
1
  <script lang="ts">import { Icon } from '../icon';
2
- let { icon, iconColor, iconPosition = 'left', trimText = false, children } = $props();
2
+ let { icon, iconColor, iconPosition = 'left', secondaryIcon, secondaryIconColor, trimText = false, children } = $props();
3
3
  const iconAsSnippet = $derived(icon && typeof icon === 'function' ? icon : null);
4
4
  const iconAsString = $derived(icon && typeof icon !== 'function' ? icon : null);
5
+ const secondaryIconAsSnippet = $derived(secondaryIcon && typeof secondaryIcon === 'function' ? secondaryIcon : null);
6
+ const secondaryIconAsString = $derived(secondaryIcon && typeof secondaryIcon !== 'function' ? secondaryIcon : null);
5
7
  </script>
6
8
 
7
9
  <span class="icon-text" class:icon-text--right={iconPosition === 'right'}>
@@ -17,53 +19,56 @@ const iconAsString = $derived(icon && typeof icon !== 'function' ? icon : null);
17
19
  {@render children()}
18
20
  </span>
19
21
  {/if}
22
+ {#if secondaryIconAsSnippet || secondaryIconAsString}
23
+ <span class="icon-text__secondary-icon">
24
+ {#if secondaryIconAsSnippet}
25
+ {@render secondaryIconAsSnippet()}
26
+ {:else if secondaryIconAsString}
27
+ <Icon src={secondaryIconAsString} color={secondaryIconColor} />
28
+ {/if}
29
+ </span>
30
+ {/if}
20
31
  </span>
21
32
 
22
33
  <!--
23
34
  @component
24
35
  Displays an icon alongside text in a horizontal layout. The icon can be an SVG string
25
36
  (rendered via `Icon`) or a custom `Snippet`. Use `iconPosition` to place the icon
26
- before or after the text.
27
-
28
- ### Props
29
- | Prop | Type | Default | Description |
30
- |---|---|---|---|
31
- | `icon` | `string \| Snippet` | — | SVG string or custom snippet for the icon |
32
- | `iconColor` | `IconColor \| null` | `null` | Color preset passed to `Icon` |
33
- | `iconPosition` | `'left' \| 'right'` | `'left'` | Icon placement relative to text |
34
- | `trimText` | `boolean` | `false` | Truncate text with ellipsis when overflowing |
35
- | `children` | `Snippet` | — | Text content |
37
+ before or after the text. The optional `secondaryIcon` is always rendered on the
38
+ opposite side of the text from the primary icon.
36
39
 
37
40
  ### CSS Custom Properties
38
41
  | Property | Description | Default |
39
42
  |---|---|---|
40
- | `--sc-kit--icon-text--global--font-size` | Root font size of the component | `1em` |
41
- | `--sc-kit--icon-text--global--color` | Root text color | — |
43
+ | `--sc-kit--icon-text--root--font-size` | Root font size of the component | `1em` |
44
+ | `--sc-kit--icon-text--root--color` | Root text color | — |
42
45
  | `--sc-kit--icon-text--text--display` | Display mode of the text slot (set `none` to hide) | `flex` |
43
46
  | `--sc-kit--icon-text--text--font-size` | Font size of the text slot | `1em` |
44
47
  | `--sc-kit--icon-text--icon--font-size` | Font size of the icon slot | text font size |
45
48
  | `--sc-kit--icon-text--icon--color` | Icon color (also forwarded to `Icon`) | global color |
46
- | `--sc-kit--icon-text--icon--size` | Icon width/height (forwarded to `Icon`) | `1em` |
47
49
  | `--sc-kit--icon-text--gap` | Gap between icon and text | `0.5em` |
48
50
  | `--sc-kit--icon-text--justify-content` | Flex justify-content | `normal` |
51
+ | `--sc-kit--icon-text--secondary-icon--font-size` | Font size of the secondary icon | text font size |
52
+ | `--sc-kit--icon-text--secondary-icon--color` | Secondary icon color | global color |
49
53
  -->
50
54
 
51
55
  <style>.icon-text {
52
- --_icon-text--global--font-size: var(--sc-kit--icon-text--global--font-size, 1em);
53
- --_icon-text--global--color: var(--sc-kit--icon-text--global--color);
56
+ --_icon-text--root--font-size: var(--sc-kit--icon-text--root--font-size, 1em);
57
+ --_icon-text--root--color: var(--sc-kit--icon-text--root--color);
54
58
  --_icon-text--text--display: var(--sc-kit--icon-text--text--display, flex);
55
59
  --_icon-text--text--font-size: var(--sc-kit--icon-text--text--font-size, 1em);
56
60
  --_icon-text--icon--font-size: var(--sc-kit--icon-text--icon--font-size, var(--_icon-text--text--font-size));
57
- --_icon-text--icon--color: var(--sc-kit--icon-text--icon--color);
61
+ --_icon-text--icon--color: var(--sc-kit--icon-text--icon--color, var(--_icon-text--root--color));
58
62
  --_icon-text--gap: var(--sc-kit--icon-text--gap, 0.5em);
59
63
  --_icon-text--justify-content: var(--sc-kit--icon-text--justify-content, normal);
60
- --_icon-text--icon--size: var(--sc-kit--icon-text--icon--size, 1em);
64
+ --_icon-text--secondary-icon--font-size: var(--sc-kit--icon-text--secondary-icon--font-size, var(--_icon-text--text--font-size));
65
+ --_icon-text--secondary-icon--color: var(--sc-kit--icon-text--secondary-icon--color, var(--_icon-text--root--color));
61
66
  display: flex;
62
67
  align-items: center;
63
68
  justify-content: var(--_icon-text--justify-content);
64
- font-size: var(--_icon-text--global--font-size);
69
+ font-size: var(--_icon-text--root--font-size);
65
70
  gap: var(--_icon-text--gap);
66
- color: var(--_icon-text--global--color);
71
+ color: var(--_icon-text--root--color);
67
72
  min-width: 0;
68
73
  }
69
74
  .icon-text--right {
@@ -85,6 +90,13 @@ before or after the text.
85
90
  font-size: var(--_icon-text--icon--font-size);
86
91
  color: var(--_icon-text--icon--color);
87
92
  display: flex;
88
- --sc-kit--icon--color: var(--_icon-text--icon--color, var(--_icon-text--global--color));
89
- --sc-kit--icon--size: var(--_icon-text--icon--size);
93
+ --sc-kit--icon--color: var(--_icon-text--icon--color);
94
+ --sc-kit--icon--size: 1em;
95
+ }
96
+ .icon-text__secondary-icon {
97
+ font-size: var(--_icon-text--secondary-icon--font-size);
98
+ color: var(--_icon-text--secondary-icon--color);
99
+ display: flex;
100
+ --sc-kit--icon--color: var(--_icon-text--secondary-icon--color);
101
+ --sc-kit--icon--size: 1em;
90
102
  }</style>
@@ -1,38 +1,39 @@
1
1
  import { type IconColor } from '../icon';
2
2
  import type { Snippet } from 'svelte';
3
3
  type Props = {
4
+ /** SVG string or custom snippet for the icon */
4
5
  icon?: string | Snippet;
6
+ /** Color preset passed to the Icon component */
5
7
  iconColor?: IconColor | null;
8
+ /** Icon placement relative to text @default 'left' */
6
9
  iconPosition?: 'left' | 'right';
10
+ /** SVG string or custom snippet for a secondary icon on the opposite side of text from the primary icon */
11
+ secondaryIcon?: string | Snippet;
12
+ /** Color preset for the secondary icon */
13
+ secondaryIconColor?: IconColor | null;
14
+ /** Truncate text with ellipsis on overflow */
7
15
  trimText?: boolean;
8
16
  children?: Snippet;
9
17
  };
10
18
  /**
11
19
  * Displays an icon alongside text in a horizontal layout. The icon can be an SVG string
12
20
  * (rendered via `Icon`) or a custom `Snippet`. Use `iconPosition` to place the icon
13
- * before or after the text.
14
- *
15
- * ### Props
16
- * | Prop | Type | Default | Description |
17
- * |---|---|---|---|
18
- * | `icon` | `string \| Snippet` | — | SVG string or custom snippet for the icon |
19
- * | `iconColor` | `IconColor \| null` | `null` | Color preset passed to `Icon` |
20
- * | `iconPosition` | `'left' \| 'right'` | `'left'` | Icon placement relative to text |
21
- * | `trimText` | `boolean` | `false` | Truncate text with ellipsis when overflowing |
22
- * | `children` | `Snippet` | — | Text content |
21
+ * before or after the text. The optional `secondaryIcon` is always rendered on the
22
+ * opposite side of the text from the primary icon.
23
23
  *
24
24
  * ### CSS Custom Properties
25
25
  * | Property | Description | Default |
26
26
  * |---|---|---|
27
- * | `--sc-kit--icon-text--global--font-size` | Root font size of the component | `1em` |
28
- * | `--sc-kit--icon-text--global--color` | Root text color | — |
27
+ * | `--sc-kit--icon-text--root--font-size` | Root font size of the component | `1em` |
28
+ * | `--sc-kit--icon-text--root--color` | Root text color | — |
29
29
  * | `--sc-kit--icon-text--text--display` | Display mode of the text slot (set `none` to hide) | `flex` |
30
30
  * | `--sc-kit--icon-text--text--font-size` | Font size of the text slot | `1em` |
31
31
  * | `--sc-kit--icon-text--icon--font-size` | Font size of the icon slot | text font size |
32
32
  * | `--sc-kit--icon-text--icon--color` | Icon color (also forwarded to `Icon`) | global color |
33
- * | `--sc-kit--icon-text--icon--size` | Icon width/height (forwarded to `Icon`) | `1em` |
34
33
  * | `--sc-kit--icon-text--gap` | Gap between icon and text | `0.5em` |
35
34
  * | `--sc-kit--icon-text--justify-content` | Flex justify-content | `normal` |
35
+ * | `--sc-kit--icon-text--secondary-icon--font-size` | Font size of the secondary icon | text font size |
36
+ * | `--sc-kit--icon-text--secondary-icon--color` | Secondary icon color | global color |
36
37
  */
37
38
  declare const Cmp: import("svelte").Component<Props, {}, "">;
38
39
  type Cmp = ReturnType<typeof Cmp>;
@@ -0,0 +1,6 @@
1
+ export { Input, InputValidatable } from './input';
2
+ export { NumeralInput, NumeralInputValidatable } from './numeral-input';
3
+ export { PinInput } from './pin-input';
4
+ export { RichTextInput } from './rich-text-input';
5
+ export type { RichTextInputOptions } from './rich-text-input';
6
+ export { Textarea, TextareaValidatable } from './textarea';
@@ -0,0 +1,5 @@
1
+ export { Input, InputValidatable } from './input';
2
+ export { NumeralInput, NumeralInputValidatable } from './numeral-input';
3
+ export { PinInput } from './pin-input';
4
+ export { RichTextInput } from './rich-text-input';
5
+ export { Textarea, TextareaValidatable } from './textarea';
@@ -0,0 +1,57 @@
1
+ <script lang="ts" generics="T extends Record<string, unknown>">import { Utils } from '../../../core/utils';
2
+ import { FormValidationHandler } from '../../../core/validation';
3
+ import { Validatable } from '../../validatable';
4
+ import { default as Input } from './cmp.input.svelte';
5
+ let { name, handler, id = undefined, type = 'text', debounce = 0, disabled = false, autofocus = false, placeholder = '', maxLength = null, clearable = false, validateOnChange = false, on, icon, clearButton } = $props();
6
+ let inputRef = null;
7
+ const onInput = (value) => {
8
+ if (!validateOnChange) {
9
+ handler.updateValidateField(name, value);
10
+ }
11
+ on?.input?.(value);
12
+ };
13
+ const onChange = (value) => {
14
+ if (validateOnChange) {
15
+ handler.updateValidateField(name, value);
16
+ }
17
+ on?.change?.(value);
18
+ };
19
+ const onBlur = () => {
20
+ if (inputRef) {
21
+ handler.updateValidateField(name, inputRef.value);
22
+ }
23
+ };
24
+ const handleMounted = (input) => {
25
+ inputRef = input;
26
+ on?.mounted?.({ input });
27
+ };
28
+ const handleInputDebounced = $derived(debounce ? Utils.debounce(onInput, debounce) : onInput);
29
+ const handleChangeDebounced = $derived(debounce ? Utils.debounce(onChange, debounce) : onChange);
30
+ </script>
31
+
32
+ <Validatable handler={handler} name={name}>
33
+ <Input
34
+ value={handler.form[name] as string | null | undefined}
35
+ name={name}
36
+ id={id}
37
+ type={type}
38
+ autofocus={autofocus}
39
+ disabled={disabled}
40
+ placeholder={placeholder}
41
+ maxLength={maxLength}
42
+ clearable={clearable}
43
+ title={!!handler.touched[name] && !!handler.errors[name] ? handler.errors[name] : ''}
44
+ on={{
45
+ input: handleInputDebounced,
46
+ change: handleChangeDebounced,
47
+ mounted: (e) => handleMounted(e.input),
48
+ blur: onBlur
49
+ }}
50
+ icon={icon}
51
+ clearButton={clearButton} />
52
+ </Validatable>
53
+
54
+ <!--
55
+ @component
56
+ Input wrapped with form validation. Validates on input or change based on `validateOnChange` prop.
57
+ -->
@@ -0,0 +1,56 @@
1
+ import { FormValidationHandler } from '../../../core/validation';
2
+ import type { Snippet } from 'svelte';
3
+ declare function $$render<T extends Record<string, unknown>>(): {
4
+ props: {
5
+ /** Field name in the form handler */
6
+ name: keyof T & string;
7
+ /** Form validation handler instance */
8
+ handler: FormValidationHandler<T>;
9
+ id?: string;
10
+ type?: "text" | "password";
11
+ /** Debounce delay in ms for input/change handlers @default 0 */
12
+ debounce?: number;
13
+ disabled?: boolean;
14
+ autofocus?: boolean;
15
+ placeholder?: string;
16
+ maxLength?: number | null;
17
+ /** Show clear button when value is present */
18
+ clearable?: boolean;
19
+ /** Validate on change event instead of input @default false */
20
+ validateOnChange?: boolean;
21
+ on?: {
22
+ input?: (value: string) => void;
23
+ change?: (value: string) => void;
24
+ /** Fires after mount, provides the input element ref */
25
+ mounted?: (data: {
26
+ input: HTMLInputElement;
27
+ }) => void;
28
+ };
29
+ /** Left icon snippet */
30
+ icon?: Snippet;
31
+ /** Custom clear button snippet */
32
+ clearButton?: Snippet;
33
+ };
34
+ exports: {};
35
+ bindings: "";
36
+ slots: {};
37
+ events: {};
38
+ };
39
+ declare class __sveltets_Render<T extends Record<string, unknown>> {
40
+ props(): ReturnType<typeof $$render<T>>['props'];
41
+ events(): ReturnType<typeof $$render<T>>['events'];
42
+ slots(): ReturnType<typeof $$render<T>>['slots'];
43
+ bindings(): "";
44
+ exports(): {};
45
+ }
46
+ interface $$IsomorphicComponent {
47
+ new <T extends Record<string, unknown>>(options: import('svelte').ComponentConstructorOptions<ReturnType<__sveltets_Render<T>['props']>>): import('svelte').SvelteComponent<ReturnType<__sveltets_Render<T>['props']>, ReturnType<__sveltets_Render<T>['events']>, ReturnType<__sveltets_Render<T>['slots']>> & {
48
+ $$bindings?: ReturnType<__sveltets_Render<T>['bindings']>;
49
+ } & ReturnType<__sveltets_Render<T>['exports']>;
50
+ <T extends Record<string, unknown>>(internal: unknown, props: ReturnType<__sveltets_Render<T>['props']> & {}): ReturnType<__sveltets_Render<T>['exports']>;
51
+ z_$$bindings?: ReturnType<__sveltets_Render<any>['bindings']>;
52
+ }
53
+ /** Input wrapped with form validation. Validates on input or change based on `validateOnChange` prop. */
54
+ declare const Cmp: $$IsomorphicComponent;
55
+ type Cmp<T extends Record<string, unknown>> = InstanceType<typeof Cmp<T>>;
56
+ export default Cmp;
@@ -0,0 +1,235 @@
1
+ <script lang="ts">import { HtmlHelper } from '../../../core/utils';
2
+ import { Icon } from '../../icon';
3
+ import { InputEmojiPicker, inputEmojiPickerContainer } from '../input-emoji-picker';
4
+ import IconDismiss from '@fluentui/svg-icons/icons/dismiss_20_regular.svg?raw';
5
+ let { value, type = 'text', name = '', id = null, placeholder = '', disabled = false, inert = false, autofocus = false, clearable = false, emoji = false, borderless = false, title = '', maxLength = null, on, icon, clearButton } = $props();
6
+ let inputRef = $state(undefined);
7
+ const valueParsed = $derived(value ?? '');
8
+ const initInput = (node) => {
9
+ inputRef = node;
10
+ if (autofocus) {
11
+ node.focus();
12
+ }
13
+ on?.mounted?.({ input: node });
14
+ };
15
+ const clearButtonVisible = $derived(!disabled && clearable && !!value);
16
+ const onInput = () => {
17
+ if (!inputRef) {
18
+ return;
19
+ }
20
+ on?.input?.(inputRef.value);
21
+ };
22
+ const onChange = () => {
23
+ if (!inputRef) {
24
+ return;
25
+ }
26
+ on?.change?.(inputRef.value);
27
+ };
28
+ const clearValue = (e) => {
29
+ e.preventDefault();
30
+ if (!inputRef) {
31
+ return;
32
+ }
33
+ inputRef.value = '';
34
+ onInput();
35
+ onChange();
36
+ inputRef.focus();
37
+ };
38
+ const focusInput = () => {
39
+ inputRef?.focus();
40
+ };
41
+ const onBlur = () => {
42
+ if (inputRef) {
43
+ inputRef.value = valueParsed;
44
+ }
45
+ on?.blur?.();
46
+ };
47
+ const onEmojiSelected = (emojiChar) => {
48
+ if (inputRef) {
49
+ HtmlHelper.pasteIntoInput(emojiChar, inputRef);
50
+ }
51
+ };
52
+ </script>
53
+
54
+ <div
55
+ class="input"
56
+ class:input--disabled={disabled}
57
+ class:input--inert={inert}
58
+ class:input--borderless={borderless}
59
+ class:input--has-emoji={emoji}
60
+ use:inputEmojiPickerContainer
61
+ onclick={focusInput}
62
+ onkeydown={() => ({})}
63
+ role="none">
64
+ {#if icon}
65
+ <div class="input__icon">
66
+ {@render icon()}
67
+ </div>
68
+ {/if}
69
+ <input
70
+ use:initInput
71
+ inert={inert}
72
+ type={type}
73
+ name={name}
74
+ id={id}
75
+ placeholder={placeholder}
76
+ class="input__input"
77
+ value={valueParsed}
78
+ disabled={disabled}
79
+ title={title}
80
+ maxlength={maxLength}
81
+ oninput={onInput}
82
+ onchange={onChange}
83
+ onblur={() => onBlur()}
84
+ onfocus={() => on?.focus?.()} />
85
+ {#if clearButtonVisible || clearButton}
86
+ <div class="input__clear-wrapper">
87
+ {#if clearButton}
88
+ {@render clearButton()}
89
+ {:else}
90
+ <button type="button" class="input__clear-button" onclick={clearValue}>
91
+ <Icon src={IconDismiss} />
92
+ </button>
93
+ {/if}
94
+ </div>
95
+ {/if}
96
+ {#if emoji}
97
+ <InputEmojiPicker on={{ select: onEmojiSelected }} />
98
+ {/if}
99
+ </div>
100
+
101
+ <!--
102
+ @component
103
+ Text/password input with optional clear button, icon, and emoji picker.
104
+
105
+ ### CSS Custom Properties
106
+ | Property | Description | Default |
107
+ |---|---|---|
108
+ | `--sc-kit--input--root--font-size` | Root font size for em scaling | `1rem` |
109
+ | `--sc-kit--input--height` | Container height | `2em` |
110
+ | `--sc-kit--input--width` | Container width | `100%` |
111
+ | `--sc-kit--input--padding--block` | Block (vertical) padding | `0` |
112
+ | `--sc-kit--input--padding--inline` | Inline (horizontal) padding | `0.5em` |
113
+ | `--sc-kit--input--accent-color` | Focus accent color | light-dark primary-500/primary-400 |
114
+ | `--sc-kit--input--background` | Background color | light-dark white/gray-900 |
115
+ | `--sc-kit--input--background--disabled` | Disabled background | light-dark neutral-50/neutral-800 |
116
+ | `--sc-kit--input--border-color` | Border color | light-dark neutral-300/neutral-600 |
117
+ | `--sc-kit--input--border-radius` | Border radius | `0.25em` |
118
+ | `--sc-kit--input--text--font-size` | Text font size | `0.875em` |
119
+ | `--sc-kit--input--text--color` | Text color | light-dark gray-800/white |
120
+ | `--sc-kit--input--placeholder--color` | Placeholder text color | inherited from border |
121
+ | `--sc-kit--input--icon--size` | Icon size | `1em` |
122
+ | `--sc-kit--input--icon--color` | Icon color | inherited from border |
123
+ | `--sc-kit--input--cursor--inert` | Cursor when inert | `default` |
124
+ -->
125
+
126
+ <style>.input {
127
+ --_--input--root--font-size: var(--sc-kit--input--root--font-size);
128
+ --_--input--height: var(--sc-kit--input--height);
129
+ --_--input--width: var(--sc-kit--input--width);
130
+ --_--input--background: var(--sc-kit--input--background);
131
+ --_--input--background--disabled: var(--sc-kit--input--background--disabled);
132
+ --_--input--border-color: var(--sc-kit--input--border-color);
133
+ --_--input--border-radius: var(--sc-kit--input--border-radius);
134
+ --_--input--icon--size: var(--sc-kit--input--icon--size);
135
+ --_--input--icon--color: var(--sc-kit--input--icon--color);
136
+ --_--input--text--font-size: var(--sc-kit--input--text--font-size);
137
+ --_--input--text--color: var(--sc-kit--input--text--color);
138
+ --_--input--placeholder--color: var(--sc-kit--input--placeholder--color);
139
+ --_--input--accent-color: var(--sc-kit--input--accent-color);
140
+ --_--input--padding--inline: var(--sc-kit--input--padding--inline);
141
+ --_--input--padding--block: var(--sc-kit--input--padding--block);
142
+ --_input--cursor--inert: var(--sc-kit--input--cursor--inert);
143
+ --_input--root--font-size: var(--_--input--root--font-size, 1rem);
144
+ --_input--height: var(--_--input--height, 2em);
145
+ --_input--width: var(--_--input--width, 100%);
146
+ --_input--background: var(--_--input--background, light-dark(#ffffff, #1c1c1c));
147
+ --_input--background--disabled: var(--_--input--background--disabled, light-dark(#f9fafb, #1f2937));
148
+ --_input--border-color: var(--_--input--border-color, light-dark(#d1d5db, #4b5563));
149
+ --_input--border-radius: var(--_--input--border-radius, 0.25em);
150
+ --_input--icon--size: var(--_--input--icon--size, 1em);
151
+ --_input--icon--color: var(--_--input--icon--color, var(--_input--border-color));
152
+ --_input--text--font-size: var(--_--input--text--font-size, 0.875em);
153
+ --_input--text--color: var(--_--input--text--color, light-dark(#2e2e2e, #ffffff));
154
+ --_input--placeholder--color: var(--_--input--placeholder--color, var(--_input--border-color));
155
+ --_input--accent-color: var(--_--input--accent-color, light-dark(#144ab0, #5a8dec));
156
+ --_input--padding--inline: var(--_--input--padding--inline, 0.5em);
157
+ --_input--padding--block: var(--_--input--padding--block, 0);
158
+ --_input--padding-top: var(--_--input--padding-top, var(--_input--padding--block));
159
+ --_input--padding-right: var(--_--input--padding-right, var(--_input--padding--inline));
160
+ --_input--padding-bottom: var(--_--input--padding-bottom, var(--_input--padding--block));
161
+ --_input--padding-left: var(--_--input--padding-left, var(--_input--padding--inline));
162
+ font-size: var(--_input--root--font-size);
163
+ height: var(--_input--height);
164
+ color: var(--_input--text--color);
165
+ border: 1px solid var(--_input--border-color);
166
+ border-radius: var(--_input--border-radius);
167
+ width: var(--_input--width);
168
+ min-width: var(--_input--width);
169
+ background: var(--_input--background);
170
+ padding-top: var(--_input--padding-top);
171
+ padding-right: var(--_input--padding-right);
172
+ padding-bottom: var(--_input--padding-bottom);
173
+ padding-left: var(--_input--padding-left);
174
+ --_input--default-shadow-color: transparent;
175
+ --_input--accent-shadow: var(--_input--explicit-shadow-color, var(--_input--default-shadow-color));
176
+ position: relative;
177
+ box-shadow: inset 0 -0.13em var(--_input--accent-shadow);
178
+ transition: box-shadow 250ms cubic-bezier(0.4, 0, 0.2, 1);
179
+ }
180
+ .input:focus, .input:focus-within {
181
+ --_input--default-shadow-color: var(--_input--accent-color);
182
+ }
183
+ .input--disabled {
184
+ background-color: var(--_input--background--disabled);
185
+ cursor: default;
186
+ }
187
+ .input {
188
+ display: flex;
189
+ align-items: center;
190
+ cursor: text;
191
+ position: relative;
192
+ }
193
+ .input--inert {
194
+ cursor: var(--_input--cursor--inert);
195
+ }
196
+ .input--borderless {
197
+ --_input--border-color: transparent;
198
+ --_input--background: transparent;
199
+ --_input--background--disabled: transparent;
200
+ --_input--accent-color: transparent;
201
+ --_input--explicit-shadow-color: transparent;
202
+ --_input--height: auto;
203
+ }
204
+ .input--has-emoji {
205
+ --_--input--padding-right: 1.5em;
206
+ }
207
+ .input__input {
208
+ flex: 1;
209
+ height: 100%;
210
+ padding: 0;
211
+ background-color: transparent !important;
212
+ font-size: var(--_input--text--font-size);
213
+ color: var(--_input--text--color);
214
+ }
215
+ .input__input::placeholder {
216
+ color: var(--_input--placeholder--color);
217
+ }
218
+ .input__input:-webkit-autofill {
219
+ -webkit-text-fill-color: var(--_input--text--color) !important;
220
+ }
221
+ .input__icon {
222
+ margin-right: 0.625em;
223
+ color: var(--_input--icon--color);
224
+ font-size: var(--_input--icon--size);
225
+ }
226
+ :global(.input__icon:not(:has(*))) {
227
+ margin-right: 0 !important;
228
+ }
229
+ .input__clear-wrapper {
230
+ color: var(--_input--icon--color);
231
+ font-size: var(--_input--icon--size);
232
+ }
233
+ .input__clear-button {
234
+ margin-left: 0.625em;
235
+ }</style>
@@ -0,0 +1,60 @@
1
+ import type { Snippet } from 'svelte';
2
+ type Props = {
3
+ value: string | null | undefined;
4
+ type?: 'text' | 'password';
5
+ name?: string;
6
+ id?: string | null;
7
+ placeholder?: string;
8
+ disabled?: boolean;
9
+ /** Make input non-interactive (visual display only) */
10
+ inert?: boolean;
11
+ autofocus?: boolean;
12
+ /** Show clear button when value is present */
13
+ clearable?: boolean;
14
+ /** Show emoji picker trigger */
15
+ emoji?: boolean;
16
+ /** Remove border, background, and accent shadow */
17
+ borderless?: boolean;
18
+ title?: string;
19
+ maxLength?: number | null;
20
+ on: {
21
+ input?: (value: string) => void;
22
+ change?: (value: string) => void;
23
+ /** Fires after mount, provides the input element ref */
24
+ mounted?: (data: {
25
+ input: HTMLInputElement;
26
+ }) => void;
27
+ blur?: () => void;
28
+ focus?: () => void;
29
+ };
30
+ /** Left icon snippet */
31
+ icon?: Snippet;
32
+ /** Custom clear button snippet */
33
+ clearButton?: Snippet;
34
+ };
35
+ /**
36
+ * Text/password input with optional clear button, icon, and emoji picker.
37
+ *
38
+ * ### CSS Custom Properties
39
+ * | Property | Description | Default |
40
+ * |---|---|---|
41
+ * | `--sc-kit--input--root--font-size` | Root font size for em scaling | `1rem` |
42
+ * | `--sc-kit--input--height` | Container height | `2em` |
43
+ * | `--sc-kit--input--width` | Container width | `100%` |
44
+ * | `--sc-kit--input--padding--block` | Block (vertical) padding | `0` |
45
+ * | `--sc-kit--input--padding--inline` | Inline (horizontal) padding | `0.5em` |
46
+ * | `--sc-kit--input--accent-color` | Focus accent color | light-dark primary-500/primary-400 |
47
+ * | `--sc-kit--input--background` | Background color | light-dark white/gray-900 |
48
+ * | `--sc-kit--input--background--disabled` | Disabled background | light-dark neutral-50/neutral-800 |
49
+ * | `--sc-kit--input--border-color` | Border color | light-dark neutral-300/neutral-600 |
50
+ * | `--sc-kit--input--border-radius` | Border radius | `0.25em` |
51
+ * | `--sc-kit--input--text--font-size` | Text font size | `0.875em` |
52
+ * | `--sc-kit--input--text--color` | Text color | light-dark gray-800/white |
53
+ * | `--sc-kit--input--placeholder--color` | Placeholder text color | inherited from border |
54
+ * | `--sc-kit--input--icon--size` | Icon size | `1em` |
55
+ * | `--sc-kit--input--icon--color` | Icon color | inherited from border |
56
+ * | `--sc-kit--input--cursor--inert` | Cursor when inert | `default` |
57
+ */
58
+ declare const Cmp: import("svelte").Component<Props, {}, "">;
59
+ type Cmp = ReturnType<typeof Cmp>;
60
+ export default Cmp;
@@ -0,0 +1,2 @@
1
+ export { default as Input } from './cmp.input.svelte';
2
+ export { default as InputValidatable } from './cmp.input-validatable.svelte';