@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,163 @@
1
+ import { getContext, setContext } from 'svelte';
2
+ import { SvelteMap } from 'svelte/reactivity';
3
+ import { Logger } from '../utils/logger.utils.js';
4
+ export class NeoTabContext {
5
+ #tabs = new SvelteMap();
6
+ #panes = new SvelteMap();
7
+ #onChange;
8
+ #onClose;
9
+ #active = $state();
10
+ #previous = $state();
11
+ #position = $state({});
12
+ #options = $state({});
13
+ get active() {
14
+ return this.#active;
15
+ }
16
+ get value() {
17
+ return this.getValue(this.active);
18
+ }
19
+ get previous() {
20
+ return this.getValue(this.#previous);
21
+ }
22
+ get disabled() {
23
+ return this.#options?.disabled;
24
+ }
25
+ get slide() {
26
+ return this.#options?.slide;
27
+ }
28
+ get toggle() {
29
+ return this.#options?.toggle;
30
+ }
31
+ get close() {
32
+ return this.#options?.close;
33
+ }
34
+ get shallow() {
35
+ return this.#options?.shallow;
36
+ }
37
+ get inset() {
38
+ return this.#options?.inset;
39
+ }
40
+ get glass() {
41
+ return this.#options?.glass;
42
+ }
43
+ get vertical() {
44
+ return this.#options?.vertical;
45
+ }
46
+ get position() {
47
+ return this.#position;
48
+ }
49
+ get text() {
50
+ return this.#options?.text;
51
+ }
52
+ get flat() {
53
+ return this.text || this.#options?.flat;
54
+ }
55
+ get state() {
56
+ return {
57
+ ...this.#options,
58
+ active: this.active,
59
+ value: this.value,
60
+ slide: this.slide,
61
+ close: this.close,
62
+ toggle: this.toggle,
63
+ inset: this.inset,
64
+ glass: this.glass,
65
+ shallow: this.shallow,
66
+ vertical: this.vertical,
67
+ disabled: this.disabled,
68
+ flat: this.flat,
69
+ text: this.text,
70
+ };
71
+ }
72
+ constructor({ onChange, onClose } = {}) {
73
+ this.#onChange = onChange;
74
+ this.#onClose = onClose;
75
+ }
76
+ getValue(tabId) {
77
+ if (!tabId)
78
+ return;
79
+ return this.#tabs.get(tabId);
80
+ }
81
+ getPane(tabId) {
82
+ if (!tabId)
83
+ return;
84
+ return this.#panes.get(tabId);
85
+ }
86
+ onOption(options) {
87
+ Object.assign(this.#options, options);
88
+ if (!options.slide)
89
+ delete this.#position.oldTab;
90
+ }
91
+ #getPosition(tabId) {
92
+ if (!tabId)
93
+ return;
94
+ const _ref = this.getValue(tabId)?.ref;
95
+ const parent = _ref?.parentElement?.getBoundingClientRect();
96
+ const rect = _ref?.getBoundingClientRect();
97
+ if (!parent || !rect)
98
+ return;
99
+ return {
100
+ id: tabId,
101
+ top: rect.top - parent.top,
102
+ left: rect.left - parent.left,
103
+ width: rect.width,
104
+ height: rect.height,
105
+ };
106
+ }
107
+ onPosition(_ref = this.value?.ref) {
108
+ const _new = {
109
+ oldTab: this.#getPosition(this.position?.newTab?.id),
110
+ };
111
+ if (this.active) {
112
+ _new.newTab = this.#getPosition(this.active);
113
+ }
114
+ this.#position = _new;
115
+ return this.position;
116
+ }
117
+ onChange(tabId, emit = true) {
118
+ if (tabId === this.#active) {
119
+ if (this.#active && this.toggle)
120
+ this.onChange();
121
+ return;
122
+ }
123
+ const current = this.value;
124
+ this.#previous = this.active;
125
+ this.#active = tabId;
126
+ this.onPosition();
127
+ if (emit)
128
+ this.#onChange?.(this.active, this.value, current);
129
+ }
130
+ onClose(tabId) {
131
+ this.#onClose?.(tabId, this.value);
132
+ }
133
+ register(tabId, value) {
134
+ if (this.#tabs.has(tabId)) {
135
+ return Logger.warn(`Tab ID '${String(tabId)}' already exists. Tab registration ignored.`, { existing: this.getValue(tabId), ignored: value });
136
+ }
137
+ this.#tabs.set(tabId, { ...value, index: this.#tabs.size });
138
+ }
139
+ remove(tabId) {
140
+ this.#tabs.delete(tabId);
141
+ if (this.#active === tabId)
142
+ this.onChange();
143
+ }
144
+ registerPane(tabId, panelId) {
145
+ if (this.#panes.has(tabId)) {
146
+ return Logger.warn(`Tab ID '${String(tabId)}' already exists. Pane registration ignored.`, {
147
+ existing: this.#panes.get(tabId),
148
+ ignored: panelId,
149
+ });
150
+ }
151
+ this.#panes.set(tabId, panelId);
152
+ }
153
+ removePane(tabId) {
154
+ this.#panes.delete(tabId);
155
+ }
156
+ }
157
+ const NeoTabsContextSymbol = Symbol('NeoTabsContext');
158
+ export const getTabContext = () => {
159
+ return getContext(NeoTabsContextSymbol);
160
+ };
161
+ export const setTabContext = (callback) => {
162
+ return setContext(NeoTabsContextSymbol, new NeoTabContext(callback));
163
+ };
@@ -0,0 +1,60 @@
1
+ import type { Snippet } from 'svelte';
2
+ import type { NeoButtonGroupProps } from '../buttons/neo-button-group.model.js';
3
+ import type { NeoTabProps, TabId } from './neo-tab.model.js';
4
+ import type { NeoTabsContext } from './neo-tabs-context.svelte.js';
5
+ import type { HTMLActionProps } from '../utils/action.utils.js';
6
+ import type { HTMLNeoBaseElement, HTMLRefProps } from '../utils/html-element.utils.js';
7
+ export type NeoTabContextValue<T = unknown> = {
8
+ index: number;
9
+ value?: T;
10
+ ref: HTMLElement;
11
+ };
12
+ export type OnChange<T = unknown> = (tabId?: TabId, newValue?: NeoTabContextValue<T>, oldValue?: NeoTabContextValue) => unknown;
13
+ export type OnClose<T = unknown> = (tabId?: TabId, value?: NeoTabContextValue<T>) => unknown;
14
+ export type NeoTabsContainerProps = HTMLNeoBaseElement & HTMLActionProps;
15
+ export type NeoTabsProps<T = unknown> = {
16
+ /**
17
+ * Snippet to display as the tabs content.
18
+ */
19
+ children?: Snippet<[NeoTabsContext]>;
20
+ /**
21
+ * Optional snippet to expose context to other components.
22
+ */
23
+ panes?: Snippet<[NeoTabsContext]>;
24
+ /**
25
+ * The HTML tag to use for the tabs.
26
+ * @default 'div'
27
+ */
28
+ tag?: keyof HTMLElementTagNameMap;
29
+ /**
30
+ * Display the active tab with a line.
31
+ * Only applies when `slide` is `true`.
32
+ */
33
+ line?: boolean;
34
+ /**
35
+ * If `true`, the panes will be displayed before the tabs.
36
+ * @default false
37
+ */
38
+ before?: boolean;
39
+ /**
40
+ * Event handler that fires when the active tab changes.
41
+ */
42
+ onchange?: OnChange<T>;
43
+ /**
44
+ * Event handler that fires when any close button is clicked.
45
+ */
46
+ onclose?: OnClose<T>;
47
+ /**
48
+ * Event handler that fires when the add button is clicked.
49
+ */
50
+ onadd?: NeoTabProps['onclick'];
51
+ /**
52
+ * The HTML tag to use for the element.
53
+ * @default 'div'
54
+ */
55
+ containerTag?: keyof HTMLElementTagNameMap;
56
+ /**
57
+ * Optional props to pass to the tabs container.
58
+ */
59
+ containerProps?: NeoTabsContainerProps;
60
+ } & NeoTabsContext & Omit<NeoButtonGroupProps, 'onchange' | 'children' | 'vertical' | 'ref'> & HTMLRefProps;
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,28 @@
1
+ <script lang="ts">
2
+ import '~/styles/reset.scss';
3
+ import '~/styles/theme.scss';
4
+
5
+ import { onDestroy } from 'svelte';
6
+
7
+ import { setNeoThemeContext } from './neo-theme-provider-context.svelte.js';
8
+ import { type NeoThemeProviderProps } from './neo-theme-provider.model.js';
9
+
10
+ /* eslint-disable prefer-const -- necessary for binding checked */
11
+ let {
12
+ // Snippets
13
+ children,
14
+
15
+ // States
16
+ theme = $bindable(),
17
+ source = $bindable(),
18
+ remember = $bindable(),
19
+ target,
20
+ }: NeoThemeProviderProps = $props();
21
+ /* eslint-enable prefer-const */
22
+
23
+ const context = setNeoThemeContext({ theme, source, remember, root: target });
24
+ $effect(() => context.update({ theme, source, remember, root: target }));
25
+ onDestroy(() => context.destroy());
26
+ </script>
27
+
28
+ {@render children?.(context.state)}
@@ -0,0 +1,21 @@
1
+ import '~/styles/reset.scss';
2
+ import '~/styles/theme.scss';
3
+ import { type NeoThemeProviderProps } from './neo-theme-provider.model.js';
4
+ interface $$__sveltets_2_IsomorphicComponent<Props extends Record<string, any> = any, Events extends Record<string, any> = any, Slots extends Record<string, any> = any, Exports = {}, Bindings = string> {
5
+ new (options: import('svelte').ComponentConstructorOptions<Props>): import('svelte').SvelteComponent<Props, Events, Slots> & {
6
+ $$bindings?: Bindings;
7
+ } & Exports;
8
+ (internal: unknown, props: Props & {
9
+ $$events?: Events;
10
+ $$slots?: Slots;
11
+ }): Exports & {
12
+ $set?: any;
13
+ $on?: any;
14
+ };
15
+ z_$$bindings?: Bindings;
16
+ }
17
+ declare const NeoThemeProvider: $$__sveltets_2_IsomorphicComponent<NeoThemeProviderProps, {
18
+ [evt: string]: CustomEvent<any>;
19
+ }, {}, {}, "source" | "theme" | "remember">;
20
+ type NeoThemeProvider = InstanceType<typeof NeoThemeProvider>;
21
+ export default NeoThemeProvider;
@@ -0,0 +1,79 @@
1
+ <script lang="ts">
2
+ import type { NeoThemeSelectorProps } from './neo-theme-selector.model.js';
3
+
4
+ import NeoButton from '../buttons/NeoButton.svelte';
5
+ import NeoButtonGroup from '../buttons/NeoButtonGroup.svelte';
6
+ import IconMoon from '../icons/IconMoon.svelte';
7
+ import IconSave from '../icons/IconSave.svelte';
8
+ import IconSaveOff from '../icons/IconSaveOff.svelte';
9
+ import IconSun from '../icons/IconSun.svelte';
10
+ import IconSunrise from '../icons/IconSunrise.svelte';
11
+ import { useNeoThemeContext } from './neo-theme-provider-context.svelte.js';
12
+ import { NeoSource, NeoTheme } from './neo-theme-provider.model.js';
13
+
14
+ const { children, ...rest }: NeoThemeSelectorProps = $props();
15
+
16
+ const context = useNeoThemeContext();
17
+
18
+ let dark = $state(context.theme === NeoTheme.Dark);
19
+ const theme = $derived(dark ? NeoTheme.Dark : NeoTheme.Light);
20
+
21
+ let source = $state(context.source);
22
+ let remember = $state(context.remember);
23
+
24
+ const sources = Object.values(NeoSource);
25
+ const sourceMap = { ...sources };
26
+ const sourceIndexMap = Object.fromEntries(Object.entries(sourceMap).map(([k, v]) => [v, Number(k)]));
27
+
28
+ let angle = $state(sourceIndexMap[context.source] * 90);
29
+ const onCycleSource = () => {
30
+ source = sourceMap[(sourceIndexMap[source] + 1) % sources.length];
31
+ angle += 90;
32
+ };
33
+
34
+ $effect(() => context.update({ theme, source, remember }));
35
+ </script>
36
+
37
+ <NeoButtonGroup {...rest}>
38
+ <NeoButton aria-label="Cycle light source origin" title="Cycle light source origin" checked onclick={onCycleSource}>
39
+ {#snippet icon()}
40
+ <span class="source-icon" style:--neo-source-rotate={`${angle}deg`}>
41
+ <IconSunrise />
42
+ </span>
43
+ {/snippet}
44
+ <span>Source</span>
45
+ </NeoButton>
46
+ <NeoButton aria-label={`Toggle ${dark ? 'light' : 'dark'} theme`} title={`Toggle ${dark ? 'light' : 'dark'} theme`} toggle bind:checked={dark}>
47
+ {#snippet icon()}
48
+ {#if dark}
49
+ <IconMoon />
50
+ {:else}
51
+ <IconSun />
52
+ {/if}
53
+ {/snippet}
54
+ <span>Theme</span>
55
+ </NeoButton>
56
+
57
+ <NeoButton aria-label="Remember theme settings" title="Remember theme settings" toggle bind:checked={remember}>
58
+ {#snippet icon()}
59
+ {#if remember}
60
+ <IconSave />
61
+ {:else}
62
+ <IconSaveOff />
63
+ {/if}
64
+ {/snippet}
65
+ </NeoButton>
66
+ {@render children?.(context.state)}
67
+ </NeoButtonGroup>
68
+
69
+ <style>.source-icon {
70
+ overflow: hidden;
71
+ border-radius: var(--neo-theme-selector-border-radius, var(--neo-border-radius-lg));
72
+ rotate: var(--neo-source-rotate, 0);
73
+ transition: rotate 0.5s ease;
74
+ }
75
+ .source-icon :global(svg) {
76
+ width: 1.25rem;
77
+ height: 1.25rem;
78
+ translate: -30% -30%;
79
+ }</style>
@@ -0,0 +1,19 @@
1
+ import type { NeoThemeSelectorProps } from './neo-theme-selector.model.js';
2
+ interface $$__sveltets_2_IsomorphicComponent<Props extends Record<string, any> = any, Events extends Record<string, any> = any, Slots extends Record<string, any> = any, Exports = {}, Bindings = string> {
3
+ new (options: import('svelte').ComponentConstructorOptions<Props>): import('svelte').SvelteComponent<Props, Events, Slots> & {
4
+ $$bindings?: Bindings;
5
+ } & Exports;
6
+ (internal: unknown, props: Props & {
7
+ $$events?: Events;
8
+ $$slots?: Slots;
9
+ }): Exports & {
10
+ $set?: any;
11
+ $on?: any;
12
+ };
13
+ z_$$bindings?: Bindings;
14
+ }
15
+ declare const NeoThemeSelector: $$__sveltets_2_IsomorphicComponent<NeoThemeSelectorProps, {
16
+ [evt: string]: CustomEvent<any>;
17
+ }, {}, {}, "">;
18
+ type NeoThemeSelector = InstanceType<typeof NeoThemeSelector>;
19
+ export default NeoThemeSelector;
@@ -0,0 +1,5 @@
1
+ export { default as NeoThemeProvider } from './NeoThemeProvider.svelte';
2
+ export { default as NeoThemeSelector } from './NeoThemeSelector.svelte';
3
+ export { NeoTheme, NeoSource } from './neo-theme-provider.model.js';
4
+ export type { NeoThemeProviderProps, NeoThemes, NeoSources, INeoThemeProviderContext } from './neo-theme-provider.model.js';
5
+ export type { NeoThemeSelectorProps } from './neo-theme-selector.model.js';
@@ -0,0 +1,3 @@
1
+ export { default as NeoThemeProvider } from './NeoThemeProvider.svelte';
2
+ export { default as NeoThemeSelector } from './NeoThemeSelector.svelte';
3
+ export { NeoTheme, NeoSource } from './neo-theme-provider.model.js';
@@ -0,0 +1,26 @@
1
+ import { type INeoThemeProviderContext } from './neo-theme-provider.model.js';
2
+ type NeoThemeProviderRoot = INeoThemeProviderContext['root'] | (() => INeoThemeProviderContext['root']);
3
+ export type NeoThemeProviderContextState = Partial<Omit<INeoThemeProviderContext, 'root'>> & {
4
+ root?: NeoThemeProviderRoot;
5
+ };
6
+ export declare class NeoThemeProviderContext implements INeoThemeProviderContext {
7
+ #private;
8
+ get theme(): import("./neo-theme-provider.model.js").NeoThemes;
9
+ get source(): import("./neo-theme-provider.model.js").NeoSources;
10
+ get remember(): boolean;
11
+ get root(): HTMLElement | ShadowRoot | undefined;
12
+ get state(): {
13
+ theme: import("./neo-theme-provider.model.js").NeoThemes;
14
+ source: import("./neo-theme-provider.model.js").NeoSources;
15
+ remember: boolean;
16
+ root: HTMLElement | ShadowRoot | undefined;
17
+ };
18
+ constructor({ theme, source, remember, root }: NeoThemeProviderContextState);
19
+ update(partial: Partial<NeoThemeProviderContextState>): void;
20
+ sync(): void;
21
+ destroy(): void;
22
+ }
23
+ export declare const setNeoThemeContext: (context: NeoThemeProviderContextState) => NeoThemeProviderContext;
24
+ export declare const getNeoThemeContext: () => NeoThemeProviderContext;
25
+ export declare const useNeoThemeContext: () => NeoThemeProviderContext;
26
+ export {};
@@ -0,0 +1,78 @@
1
+ import { getContext, setContext } from 'svelte';
2
+ import { getSource, getTheme, hasSaved, NeoSourceKey, NeoThemeKey, NeoThemeRoot, } from './neo-theme-provider.model.js';
3
+ import { NeoErrorThemeContextNotFound, NeoErrorThemeInvalidTarget, NeoErrorThemeTargetNotFound } from '../utils/error.utils.js';
4
+ export class NeoThemeProviderContext {
5
+ #theme = $state(getTheme());
6
+ #source = $state(getSource());
7
+ #remember = $state(hasSaved());
8
+ #root = $state(document?.documentElement);
9
+ get theme() {
10
+ return this.#theme;
11
+ }
12
+ get source() {
13
+ return this.#source;
14
+ }
15
+ get remember() {
16
+ return this.#remember;
17
+ }
18
+ get root() {
19
+ return typeof this.#root === 'function' ? this.#root() : this.#root;
20
+ }
21
+ get state() {
22
+ return {
23
+ theme: this.theme,
24
+ source: this.source,
25
+ remember: this.remember,
26
+ root: this.root,
27
+ };
28
+ }
29
+ constructor({ theme, source, remember, root }) {
30
+ this.#theme = theme ?? this.theme;
31
+ this.#source = source ?? this.source;
32
+ this.#remember = remember ?? this.remember;
33
+ this.#root = root ?? this.root;
34
+ this.sync();
35
+ }
36
+ update(partial) {
37
+ this.#theme = partial.theme ?? this.#theme;
38
+ this.#source = partial.source ?? this.#source;
39
+ this.#remember = partial.remember ?? this.#remember;
40
+ this.#root = partial.root ?? this.#root;
41
+ this.sync();
42
+ }
43
+ sync() {
44
+ if (!this.root)
45
+ throw new NeoErrorThemeTargetNotFound();
46
+ if (!('setAttribute' in this.root))
47
+ throw new NeoErrorThemeInvalidTarget();
48
+ this.root.setAttribute(NeoThemeRoot, '');
49
+ this.root.setAttribute(NeoThemeKey, this.theme);
50
+ this.root.setAttribute(NeoSourceKey, this.source);
51
+ if (this.remember) {
52
+ localStorage.setItem(NeoThemeKey, this.theme);
53
+ localStorage.setItem(NeoSourceKey, this.source);
54
+ }
55
+ else {
56
+ localStorage.removeItem(NeoThemeKey);
57
+ localStorage.removeItem(NeoSourceKey);
58
+ }
59
+ }
60
+ destroy() {
61
+ if (!this.root)
62
+ return;
63
+ if (!('removeAttribute' in this.root))
64
+ return;
65
+ this.root.removeAttribute(NeoThemeRoot);
66
+ this.root.removeAttribute(NeoThemeKey);
67
+ this.root.removeAttribute(NeoSourceKey);
68
+ }
69
+ }
70
+ const NeoContextKey = Symbol('NeoThemeProviderContext');
71
+ export const setNeoThemeContext = (context) => setContext(NeoContextKey, new NeoThemeProviderContext(context));
72
+ export const getNeoThemeContext = () => getContext(NeoContextKey);
73
+ export const useNeoThemeContext = () => {
74
+ const context = getNeoThemeContext();
75
+ if (!context)
76
+ throw new NeoErrorThemeContextNotFound();
77
+ return context;
78
+ };
@@ -0,0 +1,35 @@
1
+ import { type Snippet } from 'svelte';
2
+ export declare const NeoTheme: {
3
+ readonly Light: "light";
4
+ readonly Dark: "dark";
5
+ };
6
+ export type NeoThemes = (typeof NeoTheme)[keyof typeof NeoTheme];
7
+ export declare const NeoSource: {
8
+ readonly TopLeft: "top-left";
9
+ readonly TopRight: "top-right";
10
+ readonly BottomRight: "bottom-right";
11
+ readonly BottomLeft: "bottom-left";
12
+ };
13
+ export type NeoSources = (typeof NeoSource)[keyof typeof NeoSource];
14
+ export type INeoThemeProviderContext = {
15
+ readonly theme: NeoThemes;
16
+ readonly source: NeoSources;
17
+ readonly remember: boolean;
18
+ readonly root?: HTMLElement | ShadowRoot;
19
+ };
20
+ export type NeoThemeProviderProps = {
21
+ children?: Snippet<[INeoThemeProviderContext]>;
22
+ theme?: NeoThemes;
23
+ source?: NeoSources;
24
+ remember?: boolean;
25
+ target?: HTMLElement | ShadowRoot | (() => HTMLElement | ShadowRoot);
26
+ };
27
+ export declare const NeoThemeRoot = "neo-theme-root";
28
+ export declare const NeoThemeKey = "neo-theme";
29
+ export declare const NeoSourceKey = "neo-source";
30
+ export declare const getSavedTheme: () => NeoThemes | null;
31
+ export declare const getPreferTheme: () => NeoThemes;
32
+ export declare const getTheme: () => NeoThemes;
33
+ export declare const getSavedSource: () => NeoSources | null;
34
+ export declare const getSource: () => NeoSources;
35
+ export declare const hasSaved: () => boolean;
@@ -0,0 +1,20 @@
1
+ import {} from 'svelte';
2
+ export const NeoTheme = {
3
+ Light: 'light',
4
+ Dark: 'dark',
5
+ };
6
+ export const NeoSource = {
7
+ TopLeft: 'top-left',
8
+ TopRight: 'top-right',
9
+ BottomRight: 'bottom-right',
10
+ BottomLeft: 'bottom-left',
11
+ };
12
+ export const NeoThemeRoot = 'neo-theme-root';
13
+ export const NeoThemeKey = 'neo-theme';
14
+ export const NeoSourceKey = 'neo-source';
15
+ export const getSavedTheme = () => localStorage?.getItem(NeoThemeKey);
16
+ export const getPreferTheme = () => (window.matchMedia('(prefers-color-scheme: dark)').matches ? NeoTheme.Dark : NeoTheme.Light);
17
+ export const getTheme = () => getSavedTheme() ?? getPreferTheme();
18
+ export const getSavedSource = () => localStorage?.getItem(NeoSourceKey);
19
+ export const getSource = () => getSavedSource() ?? NeoSource.TopLeft;
20
+ export const hasSaved = () => !!getSavedTheme() || !!getSavedSource();
@@ -0,0 +1,6 @@
1
+ import type { Snippet } from 'svelte';
2
+ import type { NeoButtonGroupProps } from '../buttons/neo-button-group.model.js';
3
+ import type { INeoThemeProviderContext } from './neo-theme-provider.model.js';
4
+ export type NeoThemeSelectorProps = {
5
+ children: Snippet<[INeoThemeProviderContext]>;
6
+ } & Omit<NeoButtonGroupProps, 'children'>;
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,48 @@
1
+ <script lang="ts">
2
+ import { fade } from 'svelte/transition';
3
+
4
+ import type { NeoSkeletonContainerProps } from './neo-skeleton-container.model.js';
5
+
6
+ import NeoTransitionContainer from '../containers/NeoTransitionContainer.svelte';
7
+ import { toTransition, toTransitionProps } from '../utils/action.utils.js';
8
+ import { enterDefaultTransition, leaveDefaultTransition } from '../utils/transition.utils.js';
9
+
10
+ const {
11
+ // Snippets
12
+ content,
13
+ children: skeleton,
14
+
15
+ // State
16
+ loading = true,
17
+
18
+ // Styles
19
+ width,
20
+ height,
21
+
22
+ // Transition
23
+ in: inAction,
24
+ out: outAction,
25
+
26
+ // Other props
27
+ containerProps,
28
+ }: NeoSkeletonContainerProps = $props();
29
+
30
+ const inFn = $derived(toTransition(inAction, fade));
31
+ const inProps = $derived(toTransitionProps(inAction, leaveDefaultTransition));
32
+ const outFn = $derived(toTransition(outAction, fade));
33
+ const outProps = $derived(toTransitionProps(outAction, enterDefaultTransition));
34
+ </script>
35
+
36
+ {#if content}
37
+ <NeoTransitionContainer {width} {height} {...containerProps}>
38
+ {#if loading}
39
+ {@render skeleton?.()}
40
+ {:else}
41
+ <div class="neo-skeleton-content-container" in:inFn={inProps} out:outFn={outProps}>
42
+ {@render content?.()}
43
+ </div>
44
+ {/if}
45
+ </NeoTransitionContainer>
46
+ {:else if loading}
47
+ {@render skeleton?.()}
48
+ {/if}
@@ -0,0 +1,19 @@
1
+ import type { NeoSkeletonContainerProps } from './neo-skeleton-container.model.js';
2
+ interface $$__sveltets_2_IsomorphicComponent<Props extends Record<string, any> = any, Events extends Record<string, any> = any, Slots extends Record<string, any> = any, Exports = {}, Bindings = string> {
3
+ new (options: import('svelte').ComponentConstructorOptions<Props>): import('svelte').SvelteComponent<Props, Events, Slots> & {
4
+ $$bindings?: Bindings;
5
+ } & Exports;
6
+ (internal: unknown, props: Props & {
7
+ $$events?: Events;
8
+ $$slots?: Slots;
9
+ }): Exports & {
10
+ $set?: any;
11
+ $on?: any;
12
+ };
13
+ z_$$bindings?: Bindings;
14
+ }
15
+ declare const NeoSkeletonContainer: $$__sveltets_2_IsomorphicComponent<NeoSkeletonContainerProps, {
16
+ [evt: string]: CustomEvent<any>;
17
+ }, {}, {}, "">;
18
+ type NeoSkeletonContainer = InstanceType<typeof NeoSkeletonContainer>;
19
+ export default NeoSkeletonContainer;