@dvcol/neo-svelte 0.1.1

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 (163) hide show
  1. package/CHANGELOG.md +58 -0
  2. package/LICENSE +21 -0
  3. package/README.md +89 -0
  4. package/dist/assets/neo-icon-resizer-bottom-right.svg +5 -0
  5. package/dist/buttons/NeoButton.svelte +466 -0
  6. package/dist/buttons/NeoButton.svelte.d.ts +19 -0
  7. package/dist/buttons/NeoButtonGroup.svelte +369 -0
  8. package/dist/buttons/NeoButtonGroup.svelte.d.ts +19 -0
  9. package/dist/buttons/index.d.ts +4 -0
  10. package/dist/buttons/index.js +2 -0
  11. package/dist/buttons/neo-button-group.model.d.ts +71 -0
  12. package/dist/buttons/neo-button-group.model.js +1 -0
  13. package/dist/buttons/neo-button.model.d.ts +110 -0
  14. package/dist/buttons/neo-button.model.js +1 -0
  15. package/dist/cards/NeoCard.svelte +413 -0
  16. package/dist/cards/NeoCard.svelte.d.ts +19 -0
  17. package/dist/cards/index.d.ts +2 -0
  18. package/dist/cards/index.js +1 -0
  19. package/dist/cards/neo-card.model.d.ts +129 -0
  20. package/dist/cards/neo-card.model.js +1 -0
  21. package/dist/containers/NeoTransitionContainer.svelte +34 -0
  22. package/dist/containers/NeoTransitionContainer.svelte.d.ts +19 -0
  23. package/dist/containers/index.d.ts +2 -0
  24. package/dist/containers/index.js +1 -0
  25. package/dist/containers/neo-transition-container.model.d.ts +25 -0
  26. package/dist/containers/neo-transition-container.model.js +1 -0
  27. package/dist/divider/NeoDivider.svelte +47 -0
  28. package/dist/divider/NeoDivider.svelte.d.ts +19 -0
  29. package/dist/divider/index.d.ts +2 -0
  30. package/dist/divider/index.js +1 -0
  31. package/dist/divider/neo-divider.model.d.ts +7 -0
  32. package/dist/divider/neo-divider.model.js +1 -0
  33. package/dist/icons/IconAccount.svelte +10 -0
  34. package/dist/icons/IconAccount.svelte.d.ts +26 -0
  35. package/dist/icons/IconAdd.svelte +10 -0
  36. package/dist/icons/IconAdd.svelte.d.ts +26 -0
  37. package/dist/icons/IconAlert.svelte +14 -0
  38. package/dist/icons/IconAlert.svelte.d.ts +26 -0
  39. package/dist/icons/IconCircleLoading.svelte +16 -0
  40. package/dist/icons/IconCircleLoading.svelte.d.ts +26 -0
  41. package/dist/icons/IconClear.svelte +10 -0
  42. package/dist/icons/IconClear.svelte.d.ts +26 -0
  43. package/dist/icons/IconClose.svelte +14 -0
  44. package/dist/icons/IconClose.svelte.d.ts +26 -0
  45. package/dist/icons/IconConfirm.svelte +10 -0
  46. package/dist/icons/IconConfirm.svelte.d.ts +26 -0
  47. package/dist/icons/IconEmpty.svelte +10 -0
  48. package/dist/icons/IconEmpty.svelte.d.ts +26 -0
  49. package/dist/icons/IconFileUpload.svelte +18 -0
  50. package/dist/icons/IconFileUpload.svelte.d.ts +26 -0
  51. package/dist/icons/IconGithub.svelte +29 -0
  52. package/dist/icons/IconGithub.svelte.d.ts +26 -0
  53. package/dist/icons/IconImage.svelte +18 -0
  54. package/dist/icons/IconImage.svelte.d.ts +26 -0
  55. package/dist/icons/IconMinus.svelte +14 -0
  56. package/dist/icons/IconMinus.svelte.d.ts +26 -0
  57. package/dist/icons/IconMoon.svelte +64 -0
  58. package/dist/icons/IconMoon.svelte.d.ts +26 -0
  59. package/dist/icons/IconSave.svelte +15 -0
  60. package/dist/icons/IconSave.svelte.d.ts +26 -0
  61. package/dist/icons/IconSaveOff.svelte +19 -0
  62. package/dist/icons/IconSaveOff.svelte.d.ts +26 -0
  63. package/dist/icons/IconSearch.svelte +14 -0
  64. package/dist/icons/IconSearch.svelte.d.ts +26 -0
  65. package/dist/icons/IconSun.svelte +54 -0
  66. package/dist/icons/IconSun.svelte.d.ts +26 -0
  67. package/dist/icons/IconSunrise.svelte +24 -0
  68. package/dist/icons/IconSunrise.svelte.d.ts +26 -0
  69. package/dist/icons/IconVideo.svelte +15 -0
  70. package/dist/icons/IconVideo.svelte.d.ts +26 -0
  71. package/dist/icons/IconWatch.svelte +21 -0
  72. package/dist/icons/IconWatch.svelte.d.ts +26 -0
  73. package/dist/icons/IconWatchOff.svelte +26 -0
  74. package/dist/icons/IconWatchOff.svelte.d.ts +26 -0
  75. package/dist/index.d.ts +8 -0
  76. package/dist/index.js +8 -0
  77. package/dist/inputs/NeoInput.svelte +750 -0
  78. package/dist/inputs/NeoInput.svelte.d.ts +27 -0
  79. package/dist/inputs/NeoPassword.svelte +31 -0
  80. package/dist/inputs/NeoPassword.svelte.d.ts +19 -0
  81. package/dist/inputs/NeoTextarea.svelte +768 -0
  82. package/dist/inputs/NeoTextarea.svelte.d.ts +27 -0
  83. package/dist/inputs/NeoValidation.svelte +106 -0
  84. package/dist/inputs/NeoValidation.svelte.d.ts +22 -0
  85. package/dist/inputs/index.d.ts +4 -0
  86. package/dist/inputs/index.js +3 -0
  87. package/dist/inputs/neo-input.model.d.ts +234 -0
  88. package/dist/inputs/neo-input.model.js +10 -0
  89. package/dist/inputs/neo-validation.model.d.ts +40 -0
  90. package/dist/inputs/neo-validation.model.js +1 -0
  91. package/dist/nav/NeoTab.svelte +170 -0
  92. package/dist/nav/NeoTab.svelte.d.ts +19 -0
  93. package/dist/nav/NeoTabPanel.svelte +75 -0
  94. package/dist/nav/NeoTabPanel.svelte.d.ts +19 -0
  95. package/dist/nav/NeoTabs.svelte +288 -0
  96. package/dist/nav/NeoTabs.svelte.d.ts +19 -0
  97. package/dist/nav/NeoTabsCard.svelte +47 -0
  98. package/dist/nav/NeoTabsCard.svelte.d.ts +19 -0
  99. package/dist/nav/index.d.ts +8 -0
  100. package/dist/nav/index.js +4 -0
  101. package/dist/nav/neo-tab-panel.model.d.ts +30 -0
  102. package/dist/nav/neo-tab-panel.model.js +1 -0
  103. package/dist/nav/neo-tab.model.d.ts +43 -0
  104. package/dist/nav/neo-tab.model.js +1 -0
  105. package/dist/nav/neo-tabs-card.model.d.ts +25 -0
  106. package/dist/nav/neo-tabs-card.model.js +5 -0
  107. package/dist/nav/neo-tabs-context.svelte.d.ts +82 -0
  108. package/dist/nav/neo-tabs-context.svelte.js +163 -0
  109. package/dist/nav/neo-tabs.model.d.ts +60 -0
  110. package/dist/nav/neo-tabs.model.js +1 -0
  111. package/dist/providers/NeoThemeProvider.svelte +28 -0
  112. package/dist/providers/NeoThemeProvider.svelte.d.ts +21 -0
  113. package/dist/providers/NeoThemeSelector.svelte +79 -0
  114. package/dist/providers/NeoThemeSelector.svelte.d.ts +19 -0
  115. package/dist/providers/index.d.ts +5 -0
  116. package/dist/providers/index.js +3 -0
  117. package/dist/providers/neo-theme-provider-context.svelte.d.ts +26 -0
  118. package/dist/providers/neo-theme-provider-context.svelte.js +78 -0
  119. package/dist/providers/neo-theme-provider.model.d.ts +35 -0
  120. package/dist/providers/neo-theme-provider.model.js +20 -0
  121. package/dist/providers/neo-theme-selector.model.d.ts +6 -0
  122. package/dist/providers/neo-theme-selector.model.js +1 -0
  123. package/dist/skeletons/NeoSkeletonContainer.svelte +48 -0
  124. package/dist/skeletons/NeoSkeletonContainer.svelte.d.ts +19 -0
  125. package/dist/skeletons/NeoSkeletonMedia.svelte +146 -0
  126. package/dist/skeletons/NeoSkeletonMedia.svelte.d.ts +19 -0
  127. package/dist/skeletons/NeoSkeletonText.svelte +170 -0
  128. package/dist/skeletons/NeoSkeletonText.svelte.d.ts +19 -0
  129. package/dist/skeletons/index.d.ts +4 -0
  130. package/dist/skeletons/index.js +2 -0
  131. package/dist/skeletons/neo-skeleton-container.model.d.ts +29 -0
  132. package/dist/skeletons/neo-skeleton-container.model.js +1 -0
  133. package/dist/skeletons/neo-skeleton-media.model.d.ts +40 -0
  134. package/dist/skeletons/neo-skeleton-media.model.js +1 -0
  135. package/dist/skeletons/neo-skeleton-text.model.d.ts +45 -0
  136. package/dist/skeletons/neo-skeleton-text.model.js +1 -0
  137. package/dist/styles/animation.scss +75 -0
  138. package/dist/styles/common/colors.scss +217 -0
  139. package/dist/styles/common/flex.scss +26 -0
  140. package/dist/styles/common/properties.css +23 -0
  141. package/dist/styles/common/shadows.scss +305 -0
  142. package/dist/styles/common/spacing.scss +27 -0
  143. package/dist/styles/common/typography.scss +13 -0
  144. package/dist/styles/common/utils.scss +8 -0
  145. package/dist/styles/common/z-index.scss +12 -0
  146. package/dist/styles/mixin.scss +225 -0
  147. package/dist/styles/reset.scss +81 -0
  148. package/dist/styles/theme.scss +22 -0
  149. package/dist/utils/action.utils.d.ts +52 -0
  150. package/dist/utils/action.utils.js +30 -0
  151. package/dist/utils/error.utils.d.ts +25 -0
  152. package/dist/utils/error.utils.js +35 -0
  153. package/dist/utils/html-element.utils.d.ts +22 -0
  154. package/dist/utils/html-element.utils.js +1 -0
  155. package/dist/utils/logger.utils.d.ts +14 -0
  156. package/dist/utils/logger.utils.js +31 -0
  157. package/dist/utils/shadow.utils.d.ts +8 -0
  158. package/dist/utils/shadow.utils.js +26 -0
  159. package/dist/utils/timeout.util.d.ts +5 -0
  160. package/dist/utils/timeout.util.js +15 -0
  161. package/dist/utils/transition.utils.d.ts +5 -0
  162. package/dist/utils/transition.utils.js +8 -0
  163. package/package.json +180 -0
@@ -0,0 +1,750 @@
1
+ <script lang="ts">
2
+ import { wait } from '@dvcol/common-utils/common/promise';
3
+ import { fade } from 'svelte/transition';
4
+
5
+ import type { EventHandler, FocusEventHandler, FormEventHandler, MouseEventHandler } from 'svelte/elements';
6
+
7
+ import IconAlert from '../icons/IconAlert.svelte';
8
+ import IconCircleLoading from '../icons/IconCircleLoading.svelte';
9
+ import IconClear from '../icons/IconClear.svelte';
10
+ import IconConfirm from '../icons/IconConfirm.svelte';
11
+ import NeoValidation from './NeoValidation.svelte';
12
+ import {
13
+ type NeoInputContext,
14
+ NeoInputLabelPosition,
15
+ type NeoInputMethods,
16
+ type NeoInputProps,
17
+ type NeoInputState,
18
+ } from './neo-input.model.js';
19
+ import { toAction, toActionProps, toTransition, toTransitionProps } from '../utils/action.utils.js';
20
+ import { computeGlassFilter, computeHoverShadowElevation, computeShadowElevation } from '../utils/shadow.utils.js';
21
+ import { enterDefaultTransition, leaveDefaultTransition } from '../utils/transition.utils.js';
22
+
23
+ /* eslint-disable prefer-const -- necessary for binding checked */
24
+ let {
25
+ // Snippets
26
+ label,
27
+ prefix,
28
+ suffix,
29
+ message,
30
+ error,
31
+
32
+ // States
33
+ id = label ? `neo-input-${crypto.randomUUID()}` : undefined,
34
+ ref = $bindable(),
35
+ value = $bindable(''),
36
+ valid = $bindable(undefined),
37
+ dirty = $bindable(false),
38
+ touched = $bindable(false),
39
+ disabled,
40
+ readonly,
41
+ loading,
42
+ clearable,
43
+ dirtyOnInput,
44
+ validateOnInput,
45
+ position = NeoInputLabelPosition.Inside,
46
+
47
+ // Styles
48
+ elevation = 3,
49
+ hover = -1,
50
+ borderless,
51
+ rounded,
52
+ glass,
53
+ start,
54
+ floating = true,
55
+ skeleton,
56
+ validation,
57
+
58
+ // Transition
59
+ in: inAction,
60
+ out: outAction,
61
+ transition: transitionAction,
62
+
63
+ // Actions
64
+ use,
65
+
66
+ // Events
67
+ oninput,
68
+ onfocus,
69
+ onblur,
70
+ onmark,
71
+ onclear,
72
+ onchange,
73
+ oninvalid,
74
+
75
+ // Other props
76
+ labelRef = $bindable(),
77
+ labelProps,
78
+ suffixProps,
79
+ suffixTag = suffixProps?.onclick ? 'button' : 'span',
80
+ prefixProps,
81
+ prefixTag = prefixProps?.onclick ? 'button' : 'span',
82
+ containerProps,
83
+ containerTag = 'div',
84
+ wrapperProps,
85
+ wrapperTag = 'div',
86
+ messageProps,
87
+ messageTag = 'div',
88
+ ...rest
89
+ }: NeoInputProps = $props();
90
+ /* eslint-enable prefer-const */
91
+
92
+ let initial = $state(value);
93
+ let validationMessage: string | undefined = $state(ref?.validationMessage);
94
+
95
+ const filter = $derived.by(() => computeGlassFilter(elevation, glass));
96
+ const boxShadow = $derived.by(() => computeShadowElevation(elevation, glass));
97
+ const hoverShadow = $derived.by(() => computeHoverShadowElevation(elevation, hover, glass) ?? boxShadow);
98
+
99
+ const hoverFlat = $derived(boxShadow.endsWith('flat') && !hoverShadow.endsWith('flat'));
100
+ const flatHover = $derived(hoverShadow.endsWith('flat') && !boxShadow.endsWith('flat'));
101
+
102
+ let hovered = $state(false);
103
+ const onMouseEnter: MouseEventHandler<HTMLDivElement> = e => {
104
+ hovered = true;
105
+ containerProps?.onmouseenter?.(e);
106
+ };
107
+
108
+ const onMouseLeave: MouseEventHandler<HTMLDivElement> = e => {
109
+ hovered = false;
110
+ containerProps?.onmouseleave?.(e);
111
+ };
112
+
113
+ let focused = $state(false);
114
+ const onFocus: FocusEventHandler<HTMLInputElement> = e => {
115
+ focused = true;
116
+ touched = true;
117
+ onfocus?.(e);
118
+ };
119
+
120
+ const onBlur: FocusEventHandler<HTMLInputElement> = e => {
121
+ focused = false;
122
+ onblur?.(e);
123
+ };
124
+
125
+ const checkValidity = (update: { dirty?: boolean; valid?: boolean } = { dirty: true, valid: true }) => {
126
+ if (update.dirty) dirty = value !== initial;
127
+ if (!update.valid) return;
128
+ valid = ref?.checkValidity();
129
+ validationMessage = ref?.validationMessage;
130
+ };
131
+
132
+ const onInput: FormEventHandler<HTMLInputElement> = e => {
133
+ checkValidity({ dirty: dirtyOnInput, valid: validateOnInput });
134
+ oninput?.(e);
135
+ };
136
+
137
+ const onChange: FormEventHandler<HTMLInputElement> = e => {
138
+ checkValidity();
139
+ onchange?.(e);
140
+ };
141
+
142
+ const onInvalid: EventHandler<Event, HTMLInputElement> = e => {
143
+ valid = false;
144
+ validationMessage = ref?.validationMessage;
145
+ e.preventDefault();
146
+ oninvalid?.(e);
147
+ };
148
+
149
+ /**
150
+ * Change the state of the input
151
+ * @param state
152
+ */
153
+ export const mark: NeoInputMethods<HTMLInputElement>['mark'] = (state: NeoInputState) => {
154
+ if (state.touched !== undefined) touched = state.touched;
155
+ if (state.valid !== undefined) valid = state.valid;
156
+ if (state.dirty === undefined) return onmark?.({ touched, dirty, valid, value });
157
+ dirty = state.dirty;
158
+ if (!dirty) initial = value;
159
+ return onmark?.({ touched, dirty, valid, value });
160
+ };
161
+
162
+ const focus = () => {
163
+ if (focused) return;
164
+ ref?.focus();
165
+ };
166
+
167
+ /**
168
+ * Clear the input state
169
+ */
170
+ export const clear: NeoInputMethods<HTMLInputElement>['clear'] = (state?: NeoInputState) => {
171
+ value = '';
172
+ focus();
173
+ if (!state) {
174
+ setTimeout(() => checkValidity());
175
+ return onclear?.({ touched, dirty, valid, value });
176
+ }
177
+ initial = value;
178
+ setTimeout(() => mark({ touched: false, dirty: false, ...state }));
179
+ return onclear?.({ touched, dirty, valid, value });
180
+ };
181
+
182
+ const affix = $derived(clearable || loading !== undefined || validation);
183
+ const close = $derived(clearable && (focused || hovered) && value?.length && !disabled && !readonly);
184
+ const isFloating = $derived(floating && !focused && !value?.length && !disabled && !readonly);
185
+
186
+ let labelHeight = $state<string>();
187
+ let labelWidth = $state<string>();
188
+
189
+ let first = $state(true);
190
+ // Skip enter transition on first render for floating label
191
+ const waitForTick = async () => {
192
+ if (!first) return;
193
+ await wait();
194
+ first = false;
195
+ };
196
+
197
+ $effect(() => {
198
+ if (first) waitForTick();
199
+ if (!labelRef) return;
200
+ if (position === NeoInputLabelPosition.Inside && !floating) return;
201
+ labelHeight = `${labelRef?.clientHeight ?? 0}px`;
202
+ if (position === NeoInputLabelPosition.Left || position === NeoInputLabelPosition.Right) return;
203
+ labelWidth = `${labelRef?.clientWidth ?? 0}px`;
204
+ });
205
+
206
+ const errorMessage = $derived.by(() => {
207
+ if (valid || valid === undefined) return;
208
+ if (error) return error;
209
+ if (!validation) return;
210
+ return error ?? validationMessage;
211
+ });
212
+
213
+ const showMessage = $derived(message || errorMessage || error || validation);
214
+ const messageId = $derived(showMessage ? (messageProps?.id ?? `neo-input-message-${crypto.randomUUID()}`) : undefined);
215
+
216
+ const context = $derived<NeoInputContext<HTMLInputElement>>({
217
+ // Ref
218
+ ref,
219
+
220
+ // Methods
221
+ mark,
222
+ clear,
223
+
224
+ // State
225
+ value,
226
+ touched,
227
+ dirty,
228
+ valid,
229
+ readonly,
230
+ disabled,
231
+
232
+ // Styles
233
+ elevation,
234
+ hover,
235
+ borderless,
236
+ rounded,
237
+ glass,
238
+ start,
239
+ skeleton,
240
+ });
241
+
242
+ const inFn = $derived(toTransition(inAction ?? transitionAction));
243
+ const inProps = $derived(toTransitionProps(inAction ?? transitionAction));
244
+ const outFn = $derived(toTransition(outAction ?? transitionAction));
245
+ const outProps = $derived(toTransitionProps(outAction ?? transitionAction));
246
+
247
+ const useFn = $derived(toAction(use));
248
+ const useProps = $derived(toActionProps(use));
249
+ </script>
250
+
251
+ {#snippet before()}
252
+ {#if prefix}
253
+ <svelte:element this={prefixTag} class:neo-input-prefix={true} {disabled} {readonly} {...prefixProps}>
254
+ {@render prefix(context)}
255
+ </svelte:element>
256
+ {/if}
257
+ {/snippet}
258
+
259
+ {#snippet after()}
260
+ <!-- Affix (loafing, clear, placeholder) -->
261
+ {#if affix}
262
+ <span class="neo-input-affix" class:suffix role="none" onclick={focus}>
263
+ {#if loading}
264
+ <span out:fade={enterDefaultTransition}>
265
+ <IconCircleLoading width="1.1875rem" height="1.1875rem" />
266
+ </span>
267
+ {:else if close}
268
+ <button class="neo-input-clear" aria-label="clear" in:fade out:fade={enterDefaultTransition} onclick={() => clear()}>
269
+ <IconClear width="1.1875rem" height="1.1875rem" />
270
+ </button>
271
+ {:else}
272
+ <span class="neo-input-affix-validation" in:fade={leaveDefaultTransition}>
273
+ {#if validation && valid === false}
274
+ <IconAlert width="1.1875rem" height="1.1875rem" />
275
+ {:else if validation && valid === true && touched}
276
+ <IconConfirm width="1.1875rem" height="1.1875rem" />
277
+ {/if}
278
+ </span>
279
+ {/if}
280
+ </span>
281
+ {/if}
282
+ <!-- Suffix -->
283
+ {#if suffix}
284
+ <svelte:element this={suffixTag} class:neo-input-suffix={true} {disabled} {readonly} {...suffixProps}>
285
+ {@render suffix(context)}
286
+ </svelte:element>
287
+ {/if}
288
+ {/snippet}
289
+
290
+ {#snippet input()}
291
+ <input
292
+ {id}
293
+ {disabled}
294
+ {readonly}
295
+ aria-invalid={valid === undefined ? undefined : !valid}
296
+ aria-describedby={messageId}
297
+ bind:this={ref}
298
+ bind:value
299
+ class:neo-input={true}
300
+ class:suffix={suffix || affix}
301
+ class:prefix
302
+ onblur={onBlur}
303
+ onfocus={onFocus}
304
+ oninput={onInput}
305
+ onchange={onChange}
306
+ oninvalid={onInvalid}
307
+ use:useFn={useProps}
308
+ {...rest}
309
+ />
310
+ {/snippet}
311
+
312
+ {#snippet inputGroup()}
313
+ <svelte:element
314
+ this={containerTag}
315
+ role="none"
316
+ data-position={position}
317
+ data-touched={touched}
318
+ data-dirty={dirty}
319
+ data-valid={valid}
320
+ class:neo-input-group={true}
321
+ class:readonly
322
+ class:borderless
323
+ class:rounded
324
+ class:glass
325
+ class:hover
326
+ class:start
327
+ class:skeleton
328
+ class:validation
329
+ class:disabled
330
+ class:raised={elevation > 3 || elevation + hover > 3}
331
+ class:inset={elevation < -3 || elevation + hover < -3}
332
+ class:flat={!elevation}
333
+ class:hover-flat={hoverFlat}
334
+ class:flat-hover={flatHover}
335
+ style:--neo-input-glass-blur={filter}
336
+ style:--neo-input-box-shadow={boxShadow}
337
+ style:--neo-input-hover-shadow={hoverShadow}
338
+ style:--neo-input-label-height={labelHeight}
339
+ style:--neo-input-label-width={labelWidth}
340
+ out:outFn={outProps}
341
+ in:inFn={inProps}
342
+ onmouseenter={onMouseEnter}
343
+ onmouseleave={onMouseLeave}
344
+ {...containerProps}
345
+ >
346
+ {@render before()}
347
+ {#if label}
348
+ <div class="neo-input-label-container" class:prefix class:floating={isFloating} role="none" onclick={focus}>
349
+ <label
350
+ bind:this={labelRef}
351
+ for={id}
352
+ class:neo-input-label={true}
353
+ class:first
354
+ class:prefix
355
+ class:rounded
356
+ class:required={rest.required}
357
+ {...labelProps}
358
+ >
359
+ {#if typeof label === 'string'}
360
+ {label}
361
+ {:else}
362
+ {@render label(context)}
363
+ {/if}
364
+ </label>
365
+ {@render input()}
366
+ </div>
367
+ {:else}
368
+ {@render input()}
369
+ {/if}
370
+ {@render after()}
371
+ </svelte:element>
372
+ {/snippet}
373
+
374
+ {#if showMessage}
375
+ <NeoValidation
376
+ tag={wrapperTag}
377
+ error={errorMessage}
378
+ {context}
379
+ {message}
380
+ {messageId}
381
+ {messageTag}
382
+ {messageProps}
383
+ in={inAction}
384
+ out={outAction}
385
+ transition={transitionAction}
386
+ {...wrapperProps}
387
+ >
388
+ {@render inputGroup()}
389
+ </NeoValidation>
390
+ {:else}
391
+ {@render inputGroup()}
392
+ {/if}
393
+
394
+ <style>.neo-input-group,
395
+ .neo-input,
396
+ .neo-input-clear,
397
+ .neo-input-affix,
398
+ .neo-input-prefix,
399
+ .neo-input-suffix {
400
+ display: flex;
401
+ box-sizing: border-box;
402
+ font: inherit;
403
+ text-decoration: none;
404
+ outline: none;
405
+ transition: color 0.3s ease, margin 0.3s ease, padding 0.3s ease, background-color 0.3s ease, backdrop-filter 0.3s ease, border-color 0.3s ease, border-radius 0.3s ease, box-shadow 0.3s ease-out;
406
+ }
407
+
408
+ .neo-input {
409
+ flex: 1 1 auto;
410
+ align-self: center;
411
+ min-width: fit-content;
412
+ max-width: 100%;
413
+ min-height: fit-content;
414
+ padding: 0.75rem;
415
+ color: inherit;
416
+ text-overflow: ellipsis;
417
+ background-color: transparent;
418
+ border: none;
419
+ border-radius: var(--neo-input-border-radius, var(--neo-border-radius));
420
+ outline: none;
421
+ }
422
+ .neo-input-prefix, .neo-input-suffix, .neo-input-affix {
423
+ align-items: center;
424
+ }
425
+ .neo-input-prefix {
426
+ color: var(--neo-input-prefix-color, inherit);
427
+ background-color: var(--neo-input-prefix-bg-color, transparent);
428
+ border: none;
429
+ border-right: var(--neo-border-width, 1px) var(--neo-input-prefix-border-color, transparent) solid;
430
+ border-radius: var(--neo-input-border-radius, var(--neo-border-radius)) 0 0 var(--neo-input-border-radius, var(--neo-border-radius));
431
+ }
432
+ .neo-input.prefix {
433
+ margin-left: -0.9rem;
434
+ }
435
+ .neo-input.suffix {
436
+ margin-right: -0.9rem;
437
+ }
438
+ .neo-input-suffix {
439
+ color: var(--neo-input-suffix-color, inherit);
440
+ background-color: var(--neo-input-suffix-bg-color, transparent);
441
+ border: none;
442
+ border-left: var(--neo-border-width, 1px) var(--neo-input-suffix-border-color, transparent) solid;
443
+ border-radius: 0 var(--neo-input-border-radius, var(--neo-border-radius)) var(--neo-input-border-radius, var(--neo-border-radius)) 0;
444
+ }
445
+ .neo-input-affix {
446
+ display: inline-grid;
447
+ grid-template-areas: "affix";
448
+ min-width: 2.75rem;
449
+ min-height: calc(var(--neo-line-height) + 1rem);
450
+ padding: 0.75rem;
451
+ border: none;
452
+ border-left: var(--neo-border-width, 1px) var(--neo-input-suffix-border-color, transparent) solid;
453
+ }
454
+ .neo-input-affix > * {
455
+ grid-area: affix;
456
+ }
457
+ .neo-input-affix-validation {
458
+ width: 100%;
459
+ height: 100%;
460
+ }
461
+ .neo-input-affix.suffix {
462
+ min-width: 2rem;
463
+ margin-right: -0.25rem;
464
+ padding-right: 0;
465
+ }
466
+ .neo-input::placeholder {
467
+ color: var(--neo-input-placeholder-color, var(--neo-text-color-disabled));
468
+ transition: opacity 0.3s ease;
469
+ }
470
+ .neo-input:read-only {
471
+ cursor: initial;
472
+ }
473
+ .neo-input:disabled {
474
+ color: var(--neo-text-color-disabled);
475
+ cursor: not-allowed;
476
+ }
477
+
478
+ .neo-input-prefix:is(button, a),
479
+ .neo-input-suffix:is(button, a), .neo-input-clear {
480
+ cursor: pointer;
481
+ }
482
+ .neo-input-prefix:focus-visible:is(button, a),
483
+ .neo-input-suffix:focus-visible:is(button, a), .neo-input-clear:focus-visible {
484
+ color: var(--neo-input-focus-color, var(--neo-text-color-focused));
485
+ }
486
+ .neo-input-prefix:hover:is(button, a),
487
+ .neo-input-suffix:hover:is(button, a), .neo-input-clear:hover {
488
+ color: var(--neo-input-hover-color, var(--neo-text-color-hover));
489
+ }
490
+ .neo-input-prefix:active:is(button, a),
491
+ .neo-input-suffix:active:is(button, a), .neo-input-clear:active {
492
+ color: var(--neo-input-active-color, var(--neo-text-color-hover-active));
493
+ scale: 0.9;
494
+ }
495
+ .neo-input-prefix:disabled:is(button, a),
496
+ .neo-input-suffix:disabled:is(button, a), .neo-input-clear:disabled {
497
+ color: var(--neo-text-color-disabled);
498
+ cursor: not-allowed;
499
+ scale: 1;
500
+ }
501
+
502
+ .neo-input-clear {
503
+ align-items: center;
504
+ justify-content: center;
505
+ color: var(--neo-input-clear-color, inherit);
506
+ background-color: var(--neo-background-color-darker);
507
+ border: none;
508
+ border-radius: 50%;
509
+ aspect-ratio: 1;
510
+ }
511
+ .neo-input-clear:focus-visible {
512
+ color: var(--neo-close-color-focused, rgba(255, 0, 0, 0.75));
513
+ background-color: var(--neo-close-bg-color-focused, rgba(255, 0, 0, 0.05));
514
+ }
515
+ .neo-input-clear:hover {
516
+ color: var(--neo-color-warning, rgba(255, 0, 0, 0.75));
517
+ background-color: var(--neo-close-bg-color-focused, rgba(255, 0, 0, 0.05));
518
+ }
519
+ .neo-input-clear:disabled {
520
+ color: var(--neo-text-color-disabled);
521
+ cursor: not-allowed;
522
+ }
523
+
524
+ .neo-input-prefix,
525
+ .neo-input-suffix {
526
+ padding: 0.75rem;
527
+ }
528
+ .neo-input-label-container {
529
+ display: flex;
530
+ flex: 1 1 auto;
531
+ flex-direction: column;
532
+ width: 100%;
533
+ }
534
+ .neo-input-label-container .neo-input-label {
535
+ display: flex;
536
+ min-height: var(--neo-input-label-height);
537
+ padding: 0 0.75rem;
538
+ overflow: hidden;
539
+ color: var(--neo-input-label-color, inherit);
540
+ text-wrap: stable;
541
+ text-overflow: ellipsis;
542
+ cursor: inherit;
543
+ transition: padding 0.3s ease, color 0.3s ease, font-size 0.3s ease, line-height 0.3s ease, top 0.3s ease, left 0.3s ease, right 0.3s ease, translate 0.3s ease;
544
+ }
545
+ .neo-input-label-container .neo-input-label.first {
546
+ transition: none;
547
+ }
548
+ .neo-input-label-container .neo-input-label.required::after {
549
+ margin-left: 0.1rem;
550
+ color: var(--neo-input-required-color, var(--neo-color-error-75));
551
+ font-size: var(--neo-font-size);
552
+ content: "*";
553
+ }
554
+ .neo-input-label-container.floating .neo-input-label {
555
+ color: var(--neo-input-floating-label-color, var(--neo-text-color-disabled));
556
+ translate: 0 calc(50% + 0.7rem - var(--neo-input-label-height) / 2);
557
+ }
558
+ .neo-input-label-container.floating .neo-input-label.required::after {
559
+ color: var(--neo-input-required-color, var(--neo-color-error-50));
560
+ }
561
+ .neo-input-label-container.floating ::placeholder {
562
+ opacity: 0;
563
+ }
564
+
565
+ .neo-input-group {
566
+ position: relative;
567
+ margin: var(--neo-shadow-margin, 0.6rem);
568
+ color: var(--neo-input-text-color, inherit);
569
+ background-color: var(--neo-input-bg-color, inherit);
570
+ border: var(--neo-border-width, 1px) var(--neo-input-border-color, transparent) solid;
571
+ border-radius: var(--neo-input-border-radius, var(--neo-border-radius));
572
+ box-shadow: var(--neo-input-box-shadow, var(--neo-box-shadow-flat));
573
+ cursor: text;
574
+ }
575
+ .neo-input-group.readonly {
576
+ cursor: initial;
577
+ }
578
+ .neo-input-group.borderless {
579
+ border-color: transparent !important;
580
+ }
581
+ .neo-input-group.raised {
582
+ margin: var(--neo-shadow-margin-lg, 1.125rem);
583
+ }
584
+ .neo-input-group.inset {
585
+ padding: 0.25rem;
586
+ }
587
+ .neo-input-group.hover.flat-hover:hover, .neo-input-group.hover.flat-hover:focus-within, .neo-input-group.flat:not(.borderless, .hover-flat:hover, .hover-flat:focus-within) {
588
+ border-color: var(--neo-input-border-color, var(--neo-border-color));
589
+ }
590
+ .neo-input-group:focus-within, .neo-input-group.hover:hover {
591
+ box-shadow: var(--neo-input-hover-shadow, var(--neo-box-shadow-flat));
592
+ }
593
+ .neo-input-group.disabled {
594
+ box-shadow: var(--neo-box-shadow-flat) !important;
595
+ opacity: var(--neo-input-opacity-disabled, var(--neo-opacity-disabled));
596
+ }
597
+ .neo-input-group.disabled:not(.borderless) {
598
+ border-color: var(--neo-btn-border-color-disabled, var(--neo-border-color-disabled)) !important;
599
+ }
600
+ .neo-input-group.disabled .neo-input-label {
601
+ color: unset;
602
+ }
603
+ .neo-input-group.rounded {
604
+ border-radius: var(--neo-input-border-radius-lg, var(--neo-border-radius-lg));
605
+ }
606
+ .neo-input-group.rounded .neo-input {
607
+ padding: 0.75rem 1rem;
608
+ border-radius: var(--neo-input-border-radius-lg, var(--neo-border-radius-lg));
609
+ }
610
+ .neo-input-group.rounded .neo-input.prefix {
611
+ margin-left: -1.25rem;
612
+ }
613
+ .neo-input-group.rounded .neo-input-prefix {
614
+ padding: 0.75rem 0.75rem 0.75rem 1rem;
615
+ }
616
+ .neo-input-group.rounded .neo-input.suffix {
617
+ margin-right: -1.25rem;
618
+ }
619
+ .neo-input-group.rounded .neo-input-suffix {
620
+ padding: 0.75rem 1rem 0.75rem 0.75rem;
621
+ }
622
+ .neo-input-group.rounded .neo-input-affix:not(.suffix) {
623
+ margin-right: 0.25rem;
624
+ }
625
+ .neo-input-group.rounded .neo-input-label-container:not(.prefix) {
626
+ padding-left: 0.5rem;
627
+ }
628
+ .neo-input-group.rounded .neo-input-label-container .neo-input-label {
629
+ padding: 0 1rem;
630
+ }
631
+ .neo-input-group[data-position=top] {
632
+ --neo-input-margin-top: calc(var(--neo-shadow-margin, 0.6rem) + var(--neo-input-label-height, var(--neo-line-height)));
633
+ margin-top: var(--neo-input-margin-top);
634
+ }
635
+ .neo-input-group[data-position=top] .neo-input-label-container .neo-input-label {
636
+ position: absolute;
637
+ top: calc(0% - var(--neo-input-margin-top));
638
+ }
639
+ .neo-input-group[data-position=left] {
640
+ --neo-input-margin-left: calc(var(--neo-shadow-margin, 0.6rem) + var(--neo-input-label-width, auto));
641
+ margin-left: var(--neo-input-margin-left);
642
+ }
643
+ .neo-input-group[data-position=left] .neo-input-label-container .neo-input-label {
644
+ position: absolute;
645
+ top: calc(50% - var(--neo-input-label-height) / 2);
646
+ left: calc(0% - var(--neo-input-margin-left));
647
+ }
648
+ .neo-input-group[data-position=right] {
649
+ --neo-input-margin-right: calc(var(--shadow-margin, 0.6rem) + var(--neo-input-label-width, auto));
650
+ margin-right: var(--neo-input-margin-right);
651
+ }
652
+ .neo-input-group[data-position=right] .neo-input-label-container .neo-input-label {
653
+ position: absolute;
654
+ top: calc(50% - var(--neo-input-label-height) / 2);
655
+ right: calc(0% - var(--neo-input-margin-right));
656
+ }
657
+ .neo-input-group[data-position=inside] .neo-input-label-container .neo-input {
658
+ padding: 0 1rem 0.5rem;
659
+ }
660
+ .neo-input-group[data-position=inside] .neo-input-label-container .neo-input-label {
661
+ padding: 0.75rem 1rem 0.2rem;
662
+ line-height: var(--neo-line-height-xs, 1rem);
663
+ }
664
+ .neo-input-group[data-position=inside] .neo-input-label-container .neo-input-label.prefix {
665
+ padding-left: 0.15rem;
666
+ }
667
+ .neo-input-group[data-position=inside] .neo-input-label-container:not(.floating) .neo-input-label {
668
+ font-size: var(--neo-font-size-sm, 0.875rem);
669
+ }
670
+ .neo-input-group[data-position=top] .neo-input-label-container.floating .neo-input-label, .neo-input-group[data-position=left] .neo-input-label-container.floating .neo-input-label, .neo-input-group[data-position=right] .neo-input-label-container.floating .neo-input-label {
671
+ top: calc(50% - 0.65rem - var(--neo-input-label-height) / 2);
672
+ }
673
+ .neo-input-group[data-position=left] .neo-input-label-container.floating .neo-input-label {
674
+ left: 0.5rem;
675
+ }
676
+ .neo-input-group[data-position=right] .neo-input-label-container.floating .neo-input-label {
677
+ right: calc(100% - var(--neo-input-label-width) - 0.5rem);
678
+ }
679
+ .neo-input-group.glass {
680
+ --neo-skeleton-color: var(--neo-glass-skeleton-color);
681
+ background-color: var(--neo-input-bg-color, var(--neo-glass-background-color));
682
+ border-color: var(--neo-input-border-color, var(--neo-glass-top-border-color) var(--neo-glass-right-border-color) var(--neo-glass-bottom-border-color) var(--neo-glass-left-border-color));
683
+ backdrop-filter: var(--neo-input-glass-blur, var(--neo-blur-4) var(--neo-saturate-2));
684
+ }
685
+ .neo-input-group.validation[data-valid=false] {
686
+ --neo-input-label-color: var(--neo-input-label-color-error, var(--neo-color-error));
687
+ --neo-input-floating-label-color: var(--neo-input-floating-label-color-error, var(--neo-color-error-50));
688
+ }
689
+ .neo-input-group.validation[data-valid=false] .neo-input-affix-validation {
690
+ color: var(--neo-input-validation-color-error, var(--neo-color-error));
691
+ }
692
+ .neo-input-group.validation[data-valid=true] {
693
+ --neo-input-label-color: var(--neo-input-label-color-success, var(--neo-color-success));
694
+ --neo-input-floating-label-color: var(--neo-input-floating-label-color-success, var(--neo-color-success-50));
695
+ }
696
+ .neo-input-group.validation[data-valid=true] .neo-input-affix-validation {
697
+ color: var(--neo-input-validation-color-success, var(--neo-color-success));
698
+ }
699
+ @starting-style {
700
+ .neo-input-group.start {
701
+ box-shadow: var(--neo-box-shadow-flat);
702
+ }
703
+ .neo-input-group.start:not(.borderless) {
704
+ border-color: var(--neo-input-border-color, var(--neo-border-color));
705
+ }
706
+ }
707
+ .neo-input-group.skeleton {
708
+ box-shadow: var(--neo-box-shadow-flat);
709
+ pointer-events: none;
710
+ --neo-skeleton-color-start: var(--neo-skeleton-color);
711
+ --neo-skeleton-color-end: oklch(from var(--neo-skeleton-color) calc(l - var(--neo-skeleton-color-step, 0.05)) c h);
712
+ color: var(--neo-skeleton-color-start);
713
+ background-color: var(--neo-skeleton-color-start);
714
+ border-color: var(--neo-skeleton-color-start);
715
+ transition: background-color 1s ease, color 1s ease, border-color 1s ease;
716
+ animation: skeleton 3s var(--neo-transition-skeleton) infinite;
717
+ animation-delay: 1s;
718
+ }
719
+ .neo-input-group.skeleton::before, .neo-input-group.skeleton::after,
720
+ .neo-input-group.skeleton :global(> *::before),
721
+ .neo-input-group.skeleton :global(> *::after),
722
+ .neo-input-group.skeleton :global(> *) {
723
+ visibility: hidden;
724
+ pointer-events: none;
725
+ }
726
+ @keyframes skeleton {
727
+ 0% {
728
+ color: var(--neo-skeleton-color-start);
729
+ background-color: var(--neo-skeleton-color-start);
730
+ border-color: var(--neo-skeleton-color-start);
731
+ }
732
+ 40% {
733
+ color: var(--neo-skeleton-color-end);
734
+ background-color: var(--neo-skeleton-color-end);
735
+ border-color: var(--neo-skeleton-color-end);
736
+ }
737
+ 80% {
738
+ color: var(--neo-skeleton-color-start);
739
+ background-color: var(--neo-skeleton-color-start);
740
+ border-color: var(--neo-skeleton-color-start);
741
+ }
742
+ 100% {
743
+ color: var(--neo-skeleton-color-start);
744
+ background-color: var(--neo-skeleton-color-start);
745
+ border-color: var(--neo-skeleton-color-start);
746
+ }
747
+ }
748
+ .neo-input-group.skeleton.glass {
749
+ --neo-skeleton-color: var(--neo-glass-skeleton-color);
750
+ }</style>