sdocs 0.0.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 (129) hide show
  1. package/README.md +43 -0
  2. package/bin/sdocs.js +2 -0
  3. package/dist/Sdocs.svelte +1210 -0
  4. package/dist/Sdocs.svelte.d.ts +5 -0
  5. package/dist/cli/app-plugin.d.ts +7 -0
  6. package/dist/cli/app-plugin.js +69 -0
  7. package/dist/cli/config.d.ts +12 -0
  8. package/dist/cli/config.js +34 -0
  9. package/dist/cli/index.d.ts +1 -0
  10. package/dist/cli/index.js +72 -0
  11. package/dist/cli/server.d.ts +2 -0
  12. package/dist/cli/server.js +62 -0
  13. package/dist/docgen.d.ts +47 -0
  14. package/dist/docgen.js +463 -0
  15. package/dist/index.d.ts +2 -0
  16. package/dist/index.js +4 -0
  17. package/dist/internal/ComponentPreview.svelte +58 -0
  18. package/dist/internal/ComponentPreview.svelte.d.ts +17 -0
  19. package/dist/internal/CssPropsTable.svelte +239 -0
  20. package/dist/internal/CssPropsTable.svelte.d.ts +11 -0
  21. package/dist/internal/Home.svelte +92 -0
  22. package/dist/internal/Home.svelte.d.ts +9 -0
  23. package/dist/internal/MethodsTable.svelte +72 -0
  24. package/dist/internal/MethodsTable.svelte.d.ts +7 -0
  25. package/dist/internal/PropsTable.svelte +342 -0
  26. package/dist/internal/PropsTable.svelte.d.ts +12 -0
  27. package/dist/internal/Showcase.svelte +130 -0
  28. package/dist/internal/Showcase.svelte.d.ts +21 -0
  29. package/dist/types.d.ts +162 -0
  30. package/dist/types.js +1 -0
  31. package/dist/ui/Badge/Badge.docs.svelte +46 -0
  32. package/dist/ui/Badge/Badge.docs.svelte.d.ts +26 -0
  33. package/dist/ui/Badge/Badge.svelte +59 -0
  34. package/dist/ui/Badge/Badge.svelte.d.ts +17 -0
  35. package/dist/ui/Badge/index.d.ts +1 -0
  36. package/dist/ui/Badge/index.js +1 -0
  37. package/dist/ui/Checkbox/Checkbox.docs.svelte +51 -0
  38. package/dist/ui/Checkbox/Checkbox.docs.svelte.d.ts +27 -0
  39. package/dist/ui/Checkbox/Checkbox.svelte +169 -0
  40. package/dist/ui/Checkbox/Checkbox.svelte.d.ts +18 -0
  41. package/dist/ui/Checkbox/index.d.ts +1 -0
  42. package/dist/ui/Checkbox/index.js +1 -0
  43. package/dist/ui/CodeBlock/CodeBlock.docs.svelte +28 -0
  44. package/dist/ui/CodeBlock/CodeBlock.docs.svelte.d.ts +24 -0
  45. package/dist/ui/CodeBlock/CodeBlock.svelte +101 -0
  46. package/dist/ui/CodeBlock/CodeBlock.svelte.d.ts +7 -0
  47. package/dist/ui/CodeBlock/index.d.ts +1 -0
  48. package/dist/ui/CodeBlock/index.js +1 -0
  49. package/dist/ui/Frame/Frame.docs.svelte +140 -0
  50. package/dist/ui/Frame/Frame.docs.svelte.d.ts +26 -0
  51. package/dist/ui/Frame/Frame.svelte +88 -0
  52. package/dist/ui/Frame/Frame.svelte.d.ts +15 -0
  53. package/dist/ui/Frame/index.d.ts +1 -0
  54. package/dist/ui/Frame/index.js +1 -0
  55. package/dist/ui/InputNumber/InputNumber.docs.svelte +50 -0
  56. package/dist/ui/InputNumber/InputNumber.docs.svelte.d.ts +26 -0
  57. package/dist/ui/InputNumber/InputNumber.svelte +275 -0
  58. package/dist/ui/InputNumber/InputNumber.svelte.d.ts +26 -0
  59. package/dist/ui/InputNumber/index.d.ts +1 -0
  60. package/dist/ui/InputNumber/index.js +1 -0
  61. package/dist/ui/InputText/InputText.docs.svelte +43 -0
  62. package/dist/ui/InputText/InputText.docs.svelte.d.ts +26 -0
  63. package/dist/ui/InputText/InputText.svelte +116 -0
  64. package/dist/ui/InputText/InputText.svelte.d.ts +22 -0
  65. package/dist/ui/InputText/index.d.ts +1 -0
  66. package/dist/ui/InputText/index.js +1 -0
  67. package/dist/ui/Panel/CollapsiblePanel.docs.svelte +45 -0
  68. package/dist/ui/Panel/CollapsiblePanel.docs.svelte.d.ts +25 -0
  69. package/dist/ui/Panel/CollapsiblePanel.svelte +93 -0
  70. package/dist/ui/Panel/CollapsiblePanel.svelte.d.ts +14 -0
  71. package/dist/ui/Panel/index.d.ts +1 -0
  72. package/dist/ui/Panel/index.js +1 -0
  73. package/dist/ui/Placeholder/Placeholder.docs.svelte +49 -0
  74. package/dist/ui/Placeholder/Placeholder.docs.svelte.d.ts +26 -0
  75. package/dist/ui/Placeholder/Placeholder.svelte +99 -0
  76. package/dist/ui/Placeholder/Placeholder.svelte.d.ts +21 -0
  77. package/dist/ui/Placeholder/index.d.ts +1 -0
  78. package/dist/ui/Placeholder/index.js +1 -0
  79. package/dist/ui/Radio/Radio.docs.svelte +67 -0
  80. package/dist/ui/Radio/Radio.docs.svelte.d.ts +27 -0
  81. package/dist/ui/Radio/Radio.svelte +165 -0
  82. package/dist/ui/Radio/Radio.svelte.d.ts +22 -0
  83. package/dist/ui/Radio/RadioGroup.docs.svelte +70 -0
  84. package/dist/ui/Radio/RadioGroup.docs.svelte.d.ts +27 -0
  85. package/dist/ui/Radio/RadioGroup.svelte +98 -0
  86. package/dist/ui/Radio/RadioGroup.svelte.d.ts +27 -0
  87. package/dist/ui/Radio/index.d.ts +2 -0
  88. package/dist/ui/Radio/index.js +2 -0
  89. package/dist/ui/SegmentControl/SegmentControl.docs.svelte +54 -0
  90. package/dist/ui/SegmentControl/SegmentControl.docs.svelte.d.ts +25 -0
  91. package/dist/ui/SegmentControl/SegmentControl.svelte +120 -0
  92. package/dist/ui/SegmentControl/SegmentControl.svelte.d.ts +18 -0
  93. package/dist/ui/SegmentControl/index.d.ts +1 -0
  94. package/dist/ui/SegmentControl/index.js +1 -0
  95. package/dist/ui/Stack/Stack.docs.svelte +63 -0
  96. package/dist/ui/Stack/Stack.docs.svelte.d.ts +26 -0
  97. package/dist/ui/Stack/Stack.svelte +45 -0
  98. package/dist/ui/Stack/Stack.svelte.d.ts +19 -0
  99. package/dist/ui/Stack/index.d.ts +1 -0
  100. package/dist/ui/Stack/index.js +1 -0
  101. package/dist/ui/Table/Body.svelte +17 -0
  102. package/dist/ui/Table/Body.svelte.d.ts +11 -0
  103. package/dist/ui/Table/Caption.svelte +17 -0
  104. package/dist/ui/Table/Caption.svelte.d.ts +11 -0
  105. package/dist/ui/Table/Cell.svelte +24 -0
  106. package/dist/ui/Table/Cell.svelte.d.ts +15 -0
  107. package/dist/ui/Table/Foot.svelte +17 -0
  108. package/dist/ui/Table/Foot.svelte.d.ts +11 -0
  109. package/dist/ui/Table/Head.svelte +17 -0
  110. package/dist/ui/Table/Head.svelte.d.ts +11 -0
  111. package/dist/ui/Table/Header.svelte +27 -0
  112. package/dist/ui/Table/Header.svelte.d.ts +17 -0
  113. package/dist/ui/Table/Row.svelte +19 -0
  114. package/dist/ui/Table/Row.svelte.d.ts +13 -0
  115. package/dist/ui/Table/Table.docs.svelte +197 -0
  116. package/dist/ui/Table/Table.docs.svelte.d.ts +28 -0
  117. package/dist/ui/Table/Table.svelte +140 -0
  118. package/dist/ui/Table/Table.svelte.d.ts +27 -0
  119. package/dist/ui/Table/index.js +10 -0
  120. package/dist/ui/css/colors.css +377 -0
  121. package/dist/ui/css/global.css +10 -0
  122. package/dist/ui/index.d.ts +12 -0
  123. package/dist/ui/index.js +12 -0
  124. package/dist/virtual-sdocs.d.ts +20 -0
  125. package/dist/vite-plugin.d.ts +18 -0
  126. package/dist/vite-plugin.js +206 -0
  127. package/dist/vite.d.ts +2 -0
  128. package/dist/vite.js +2 -0
  129. package/package.json +76 -0
@@ -0,0 +1,140 @@
1
+ <script lang="ts" module>
2
+ import Frame from './Frame.svelte';
3
+ import type { DocMeta } from '../../types.js';
4
+
5
+ export const meta: DocMeta = {
6
+ title: 'Layout/Frame',
7
+ component: Frame,
8
+ };
9
+
10
+ export { Default, WithSidebars, Nested, AppLayout };
11
+ </script>
12
+
13
+ {#snippet Default()}
14
+ <div style="height: 300px; border: 1px solid #e5e5e5; border-radius: 8px; overflow: hidden;">
15
+ <Frame>
16
+ {#snippet top()}
17
+ <div style="padding: 12px 16px; background: #f5f5f5; border-bottom: 1px solid #e5e5e5;">Header</div>
18
+ {/snippet}
19
+
20
+ <div style="padding: 16px;">Main content goes here. This is the middle area.</div>
21
+
22
+ {#snippet bottom()}
23
+ <div style="padding: 12px 16px; background: #f5f5f5; border-top: 1px solid #e5e5e5;">Footer</div>
24
+ {/snippet}
25
+ </Frame>
26
+ </div>
27
+ {/snippet}
28
+
29
+ {#snippet WithSidebars()}
30
+ <div style="height: 300px; border: 1px solid #e5e5e5; border-radius: 8px; overflow: hidden;">
31
+ <Frame>
32
+ {#snippet top()}
33
+ <div style="padding: 12px 16px; background: #3b82f6; color: white;">App Header</div>
34
+ {/snippet}
35
+
36
+ {#snippet left()}
37
+ <div style="width: 180px; padding: 16px; background: #f8fafc; border-right: 1px solid #e5e5e5;">
38
+ <div style="font-weight: 600; margin-bottom: 12px;">Navigation</div>
39
+ <div style="display: flex; flex-direction: column; gap: 8px; font-size: 14px; color: #666;">
40
+ <span>Dashboard</span>
41
+ <span>Settings</span>
42
+ <span>Profile</span>
43
+ </div>
44
+ </div>
45
+ {/snippet}
46
+
47
+ <div style="padding: 16px;">
48
+ <h2 style="margin: 0 0 8px;">Welcome</h2>
49
+ <p style="color: #666; margin: 0;">This is the main content area with left sidebar.</p>
50
+ </div>
51
+
52
+ {#snippet right()}
53
+ <div style="width: 200px; padding: 16px; background: #fefce8; border-left: 1px solid #e5e5e5;">
54
+ <div style="font-weight: 600; margin-bottom: 8px;">Info Panel</div>
55
+ <p style="font-size: 13px; color: #666; margin: 0;">Additional context or widgets.</p>
56
+ </div>
57
+ {/snippet}
58
+ </Frame>
59
+ </div>
60
+ {/snippet}
61
+
62
+ {#snippet Nested()}
63
+ <div style="height: 400px; border: 1px solid #e5e5e5; border-radius: 8px; overflow: hidden;">
64
+ <Frame>
65
+ {#snippet top()}
66
+ <div style="padding: 12px 16px; background: #1e293b; color: white;">Outer Frame Header</div>
67
+ {/snippet}
68
+
69
+ {#snippet left()}
70
+ <div style="width: 160px; padding: 16px; background: #f1f5f9; border-right: 1px solid #e5e5e5;">Outer Left</div>
71
+ {/snippet}
72
+
73
+ <Frame>
74
+ {#snippet top()}
75
+ <div style="padding: 8px 12px; background: #e0f2fe; border-bottom: 1px solid #bae6fd; font-size: 14px;">Inner Frame Header</div>
76
+ {/snippet}
77
+
78
+ <div style="padding: 16px;">
79
+ <p style="margin: 0; color: #666;">Nested frames allow complex layouts.</p>
80
+ </div>
81
+
82
+ {#snippet bottom()}
83
+ <div style="padding: 8px 12px; background: #e0f2fe; border-top: 1px solid #bae6fd; font-size: 14px;">Inner Frame Footer</div>
84
+ {/snippet}
85
+ </Frame>
86
+ </Frame>
87
+ </div>
88
+ {/snippet}
89
+
90
+ {#snippet AppLayout()}
91
+ <div style="height: 400px; border: 1px solid #e5e5e5; border-radius: 8px; overflow: hidden;">
92
+ <Frame>
93
+ {#snippet top()}
94
+ <div style="padding: 12px 20px; background: linear-gradient(to right, #6366f1, #8b5cf6); color: white; display: flex; justify-content: space-between; align-items: center;">
95
+ <span style="font-weight: 600;">MyApp</span>
96
+ <div style="display: flex; gap: 16px; font-size: 14px;">
97
+ <span>Docs</span>
98
+ <span>Blog</span>
99
+ <span>Login</span>
100
+ </div>
101
+ </div>
102
+ {/snippet}
103
+
104
+ {#snippet left()}
105
+ <div style="width: 220px; padding: 20px; background: #fafafa; border-right: 1px solid #e5e5e5;">
106
+ <div style="font-size: 12px; text-transform: uppercase; color: #999; margin-bottom: 12px;">Menu</div>
107
+ <div style="display: flex; flex-direction: column; gap: 4px;">
108
+ <div style="padding: 8px 12px; background: #6366f1; color: white; border-radius: 6px; font-size: 14px;">Home</div>
109
+ <div style="padding: 8px 12px; color: #666; font-size: 14px;">Projects</div>
110
+ <div style="padding: 8px 12px; color: #666; font-size: 14px;">Team</div>
111
+ <div style="padding: 8px 12px; color: #666; font-size: 14px;">Settings</div>
112
+ </div>
113
+ </div>
114
+ {/snippet}
115
+
116
+ <div style="padding: 24px; background: white;">
117
+ <h1 style="margin: 0 0 8px; font-size: 24px;">Dashboard</h1>
118
+ <p style="color: #666; margin: 0 0 20px;">Welcome back! Here's what's happening.</p>
119
+ <div style="display: grid; grid-template-columns: repeat(3, 1fr); gap: 16px;">
120
+ <div style="padding: 16px; background: #f0fdf4; border-radius: 8px;">
121
+ <div style="font-size: 24px; font-weight: 600; color: #16a34a;">128</div>
122
+ <div style="font-size: 13px; color: #666;">Active Users</div>
123
+ </div>
124
+ <div style="padding: 16px; background: #eff6ff; border-radius: 8px;">
125
+ <div style="font-size: 24px; font-weight: 600; color: #2563eb;">42</div>
126
+ <div style="font-size: 13px; color: #666;">Projects</div>
127
+ </div>
128
+ <div style="padding: 16px; background: #fef3c7; border-radius: 8px;">
129
+ <div style="font-size: 24px; font-weight: 600; color: #d97706;">7</div>
130
+ <div style="font-size: 13px; color: #666;">Pending</div>
131
+ </div>
132
+ </div>
133
+ </div>
134
+
135
+ {#snippet bottom()}
136
+ <div style="padding: 12px 20px; background: #fafafa; border-top: 1px solid #e5e5e5; font-size: 13px; color: #999; text-align: center;">© 2025 MyApp. All rights reserved.</div>
137
+ {/snippet}
138
+ </Frame>
139
+ </div>
140
+ {/snippet}
@@ -0,0 +1,26 @@
1
+ import Frame from './Frame.svelte';
2
+ import type { DocMeta } from '../../types.js';
3
+ declare const Default: () => ReturnType<import("svelte").Snippet>;
4
+ declare const WithSidebars: () => ReturnType<import("svelte").Snippet>;
5
+ declare const Nested: () => ReturnType<import("svelte").Snippet>;
6
+ declare const AppLayout: () => ReturnType<import("svelte").Snippet>;
7
+ export declare const meta: DocMeta;
8
+ export { Default, WithSidebars, Nested, AppLayout };
9
+ 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> {
10
+ new (options: import('svelte').ComponentConstructorOptions<Props>): import('svelte').SvelteComponent<Props, Events, Slots> & {
11
+ $$bindings?: Bindings;
12
+ } & Exports;
13
+ (internal: unknown, props: {
14
+ $$events?: Events;
15
+ $$slots?: Slots;
16
+ }): Exports & {
17
+ $set?: any;
18
+ $on?: any;
19
+ };
20
+ z_$$bindings?: Bindings;
21
+ }
22
+ declare const Frame: $$__sveltets_2_IsomorphicComponent<Record<string, never>, {
23
+ [evt: string]: CustomEvent<any>;
24
+ }, {}, {}, string>;
25
+ type Frame = InstanceType<typeof Frame>;
26
+ export default Frame;
@@ -0,0 +1,88 @@
1
+ <script lang="ts">
2
+ import type { Snippet } from 'svelte';
3
+
4
+ /**
5
+ * A flexible layout component with slots for top, bottom, left, right, and center content.
6
+ */
7
+ interface Props {
8
+ top?: Snippet;
9
+ bottom?: Snippet;
10
+ left?: Snippet;
11
+ right?: Snippet;
12
+ children?: Snippet;
13
+ class?: string;
14
+ }
15
+
16
+ let { top, bottom, left, right, children, class: className = '' }: Props = $props();
17
+ </script>
18
+
19
+ <div class="Frame {className}">
20
+ {#if top}
21
+ <div class="Frame-top">
22
+ {@render top()}
23
+ </div>
24
+ {/if}
25
+
26
+ <div class="Frame-body">
27
+ {#if left}
28
+ <div class="Frame-left">
29
+ {@render left()}
30
+ </div>
31
+ {/if}
32
+
33
+ <div class="Frame-middle">
34
+ {#if children}
35
+ {@render children()}
36
+ {/if}
37
+ </div>
38
+
39
+ {#if right}
40
+ <div class="Frame-right">
41
+ {@render right()}
42
+ </div>
43
+ {/if}
44
+ </div>
45
+
46
+ {#if bottom}
47
+ <div class="Frame-bottom">
48
+ {@render bottom()}
49
+ </div>
50
+ {/if}
51
+ </div>
52
+
53
+ <style>
54
+ .Frame {
55
+ display: flex;
56
+ flex-direction: column;
57
+ height: 100%;
58
+ width: 100%;
59
+ }
60
+
61
+ .Frame-top {
62
+ flex-shrink: 0;
63
+ }
64
+
65
+ .Frame-body {
66
+ display: flex;
67
+ flex: 1;
68
+ min-height: 0;
69
+ }
70
+
71
+ .Frame-left {
72
+ flex-shrink: 0;
73
+ }
74
+
75
+ .Frame-middle {
76
+ flex: 1;
77
+ min-width: 0;
78
+ overflow: auto;
79
+ }
80
+
81
+ .Frame-right {
82
+ flex-shrink: 0;
83
+ }
84
+
85
+ .Frame-bottom {
86
+ flex-shrink: 0;
87
+ }
88
+ </style>
@@ -0,0 +1,15 @@
1
+ import type { Snippet } from 'svelte';
2
+ /**
3
+ * A flexible layout component with slots for top, bottom, left, right, and center content.
4
+ */
5
+ interface Props {
6
+ top?: Snippet;
7
+ bottom?: Snippet;
8
+ left?: Snippet;
9
+ right?: Snippet;
10
+ children?: Snippet;
11
+ class?: string;
12
+ }
13
+ declare const Frame: import("svelte").Component<Props, {}, "">;
14
+ type Frame = ReturnType<typeof Frame>;
15
+ export default Frame;
@@ -0,0 +1 @@
1
+ export { default as Frame } from './Frame.svelte';
@@ -0,0 +1 @@
1
+ export { default as Frame } from './Frame.svelte';
@@ -0,0 +1,50 @@
1
+ <script lang="ts" module>
2
+ import InputNumber from './InputNumber.svelte';
3
+ import type { DocMeta } from '../../types.js';
4
+
5
+ export const meta: DocMeta = {
6
+ title: 'InputNumber',
7
+ component: InputNumber,
8
+ args: {
9
+ value: 0,
10
+ min: undefined,
11
+ max: undefined,
12
+ step: 1,
13
+ placeholder: 'Enter number...',
14
+ size: 'm',
15
+ disabled: false
16
+ }
17
+ };
18
+
19
+ export { Default, Sizes, WithConstraints, Disabled };
20
+ </script>
21
+
22
+ {#snippet Default(args: any)}
23
+ <InputNumber {...args} />
24
+ {/snippet}
25
+
26
+ {#snippet Sizes()}
27
+ <div style="display: flex; flex-direction: column; gap: 12px; max-width: 200px;">
28
+ <InputNumber placeholder="XS size" size="xs" />
29
+ <InputNumber placeholder="S size" size="s" />
30
+ <InputNumber placeholder="M size" size="m" />
31
+ <InputNumber placeholder="L size" size="l" />
32
+ </div>
33
+ {/snippet}
34
+
35
+ {#snippet WithConstraints()}
36
+ <div style="display: flex; flex-direction: column; gap: 12px; max-width: 200px;">
37
+ <label style="font-size: 12px; color: var(--base-500);">
38
+ Min: 0, Max: 100, Step: 10
39
+ <InputNumber value={50} min={0} max={100} step={10} />
40
+ </label>
41
+ <label style="font-size: 12px; color: var(--base-500);">
42
+ Step: 0.1 (decimals)
43
+ <InputNumber value={0.5} step={0.1} />
44
+ </label>
45
+ </div>
46
+ {/snippet}
47
+
48
+ {#snippet Disabled()}
49
+ <InputNumber placeholder="Disabled" value={42} disabled />
50
+ {/snippet}
@@ -0,0 +1,26 @@
1
+ import InputNumber from './InputNumber.svelte';
2
+ import type { DocMeta } from '../../types.js';
3
+ declare const Default: (args: any) => ReturnType<import("svelte").Snippet>;
4
+ declare const Sizes: () => ReturnType<import("svelte").Snippet>;
5
+ declare const WithConstraints: () => ReturnType<import("svelte").Snippet>;
6
+ declare const Disabled: () => ReturnType<import("svelte").Snippet>;
7
+ export declare const meta: DocMeta;
8
+ export { Default, Sizes, WithConstraints, Disabled };
9
+ 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> {
10
+ new (options: import('svelte').ComponentConstructorOptions<Props>): import('svelte').SvelteComponent<Props, Events, Slots> & {
11
+ $$bindings?: Bindings;
12
+ } & Exports;
13
+ (internal: unknown, props: {
14
+ $$events?: Events;
15
+ $$slots?: Slots;
16
+ }): Exports & {
17
+ $set?: any;
18
+ $on?: any;
19
+ };
20
+ z_$$bindings?: Bindings;
21
+ }
22
+ declare const InputNumber: $$__sveltets_2_IsomorphicComponent<Record<string, never>, {
23
+ [evt: string]: CustomEvent<any>;
24
+ }, {}, {}, string>;
25
+ type InputNumber = InstanceType<typeof InputNumber>;
26
+ export default InputNumber;
@@ -0,0 +1,275 @@
1
+ <script lang="ts">
2
+ /**
3
+ * A number input field with custom stepper buttons and size variants.
4
+ */
5
+ interface Props {
6
+ /** Input value */
7
+ value?: number;
8
+ /** Minimum value */
9
+ min?: number;
10
+ /** Maximum value */
11
+ max?: number;
12
+ /** Step increment */
13
+ step?: number;
14
+ /** Placeholder text */
15
+ placeholder?: string;
16
+ /** Callback when value changes */
17
+ onchange?: (value: number) => void;
18
+ /** Callback on input */
19
+ oninput?: (value: number) => void;
20
+ /** Size variant */
21
+ size?: 'xs' | 's' | 'm' | 'l';
22
+ /** Disable the input */
23
+ disabled?: boolean;
24
+ }
25
+
26
+ let {
27
+ value = 0,
28
+ min,
29
+ max,
30
+ step = 1,
31
+ placeholder = '',
32
+ onchange,
33
+ oninput,
34
+ size = 'm',
35
+ disabled = false
36
+ }: Props = $props();
37
+
38
+ // Internal state - sync with prop
39
+ let inputValue = $state(value);
40
+ $effect(() => {
41
+ inputValue = value;
42
+ });
43
+
44
+ function clamp(val: number): number {
45
+ if (min !== undefined && val < min) return min;
46
+ if (max !== undefined && val > max) return max;
47
+ return val;
48
+ }
49
+
50
+ function handleInput(e: Event) {
51
+ const target = e.target as HTMLInputElement;
52
+ const num = parseFloat(target.value);
53
+ if (!isNaN(num)) {
54
+ inputValue = num;
55
+ oninput?.(inputValue);
56
+ }
57
+ }
58
+
59
+ function handleChange(e: Event) {
60
+ const target = e.target as HTMLInputElement;
61
+ const num = parseFloat(target.value);
62
+ if (!isNaN(num)) {
63
+ inputValue = clamp(num);
64
+ onchange?.(inputValue);
65
+ }
66
+ }
67
+
68
+ function increment() {
69
+ if (disabled) return;
70
+ const newValue = clamp(inputValue + step);
71
+ inputValue = newValue;
72
+ onchange?.(newValue);
73
+ }
74
+
75
+ function decrement() {
76
+ if (disabled) return;
77
+ const newValue = clamp(inputValue - step);
78
+ inputValue = newValue;
79
+ onchange?.(newValue);
80
+ }
81
+ </script>
82
+
83
+ <div class="InputNumber {size}" class:disabled>
84
+ <input
85
+ class="InputNumber-input"
86
+ type="number"
87
+ value={inputValue}
88
+ {min}
89
+ {max}
90
+ {step}
91
+ {placeholder}
92
+ {disabled}
93
+ oninput={handleInput}
94
+ onchange={handleChange}
95
+ />
96
+ <div class="InputNumber-steppers">
97
+ <button
98
+ type="button"
99
+ class="InputNumber-stepper"
100
+ onclick={decrement}
101
+ {disabled}
102
+ tabindex={-1}
103
+ aria-label="Decrement"
104
+ >
105
+ <svg viewBox="0 0 12 12" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round">
106
+ <path d="M8 3L5 6L8 9" />
107
+ </svg>
108
+ </button>
109
+ <button
110
+ type="button"
111
+ class="InputNumber-stepper"
112
+ onclick={increment}
113
+ {disabled}
114
+ tabindex={-1}
115
+ aria-label="Increment"
116
+ >
117
+ <svg viewBox="0 0 12 12" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round">
118
+ <path d="M4 3L7 6L4 9" />
119
+ </svg>
120
+ </button>
121
+ </div>
122
+ </div>
123
+
124
+ <style>
125
+ .InputNumber {
126
+ position: relative;
127
+ display: flex;
128
+ align-items: stretch;
129
+ width: 100%;
130
+ background: var(--base-100);
131
+ border-radius: 6px;
132
+ transition: all 0.15s ease;
133
+ }
134
+
135
+ .InputNumber:hover:not(.disabled) {
136
+ background: var(--base-50);
137
+ }
138
+
139
+ .InputNumber:focus-within {
140
+ background: var(--base-50);
141
+ box-shadow: 0 0 0 2px var(--action-200);
142
+ }
143
+
144
+ .InputNumber.disabled {
145
+ opacity: 0.5;
146
+ cursor: not-allowed;
147
+ }
148
+
149
+ .InputNumber-input {
150
+ flex: 1;
151
+ font-family: var(--font-sans);
152
+ font-weight: 400;
153
+ border: none;
154
+ background: transparent;
155
+ color: var(--color-text);
156
+ outline: none;
157
+ min-width: 0;
158
+ }
159
+
160
+ .InputNumber-input::placeholder {
161
+ color: var(--base-400);
162
+ }
163
+
164
+ .InputNumber-input:disabled {
165
+ cursor: not-allowed;
166
+ }
167
+
168
+ /* Hide native spinner buttons */
169
+ .InputNumber-input::-webkit-outer-spin-button,
170
+ .InputNumber-input::-webkit-inner-spin-button {
171
+ -webkit-appearance: none;
172
+ margin: 0;
173
+ }
174
+
175
+ .InputNumber-input[type='number'] {
176
+ -moz-appearance: textfield;
177
+ }
178
+
179
+ .InputNumber-steppers {
180
+ display: flex;
181
+ flex-direction: row;
182
+ border-left: 1px solid var(--base-200);
183
+ }
184
+
185
+ .InputNumber-stepper {
186
+ display: flex;
187
+ align-items: center;
188
+ justify-content: center;
189
+ background: none;
190
+ border: none;
191
+ color: var(--base-400);
192
+ cursor: pointer;
193
+ padding: 0;
194
+ transition: all 0.1s ease;
195
+ }
196
+
197
+ .InputNumber-stepper:hover:not(:disabled) {
198
+ color: var(--color-text);
199
+ background: var(--base-200);
200
+ }
201
+
202
+ .InputNumber-stepper:active:not(:disabled) {
203
+ background: var(--base-300);
204
+ }
205
+
206
+ .InputNumber-stepper:disabled {
207
+ cursor: not-allowed;
208
+ }
209
+
210
+ .InputNumber-stepper:first-child {
211
+ border-right: 1px solid var(--base-200);
212
+ }
213
+
214
+ .InputNumber-stepper:last-child {
215
+ border-radius: 0 6px 6px 0;
216
+ }
217
+
218
+ .InputNumber-stepper svg {
219
+ width: 12px;
220
+ height: 12px;
221
+ }
222
+
223
+ /* Size variants */
224
+ .xs {
225
+ height: 24px;
226
+ }
227
+ .xs .InputNumber-input {
228
+ padding: 0 8px;
229
+ font-size: 11px;
230
+ }
231
+ .xs .InputNumber-stepper {
232
+ width: 20px;
233
+ }
234
+ .xs .InputNumber-stepper svg {
235
+ width: 10px;
236
+ height: 10px;
237
+ }
238
+
239
+ .s {
240
+ height: 28px;
241
+ }
242
+ .s .InputNumber-input {
243
+ padding: 0 10px;
244
+ font-size: 12px;
245
+ }
246
+ .s .InputNumber-stepper {
247
+ width: 24px;
248
+ }
249
+
250
+ .m {
251
+ height: 32px;
252
+ }
253
+ .m .InputNumber-input {
254
+ padding: 0 12px;
255
+ font-size: 13px;
256
+ }
257
+ .m .InputNumber-stepper {
258
+ width: 28px;
259
+ }
260
+
261
+ .l {
262
+ height: 40px;
263
+ }
264
+ .l .InputNumber-input {
265
+ padding: 0 16px;
266
+ font-size: 14px;
267
+ }
268
+ .l .InputNumber-stepper {
269
+ width: 34px;
270
+ }
271
+ .l .InputNumber-stepper svg {
272
+ width: 14px;
273
+ height: 14px;
274
+ }
275
+ </style>
@@ -0,0 +1,26 @@
1
+ /**
2
+ * A number input field with custom stepper buttons and size variants.
3
+ */
4
+ interface Props {
5
+ /** Input value */
6
+ value?: number;
7
+ /** Minimum value */
8
+ min?: number;
9
+ /** Maximum value */
10
+ max?: number;
11
+ /** Step increment */
12
+ step?: number;
13
+ /** Placeholder text */
14
+ placeholder?: string;
15
+ /** Callback when value changes */
16
+ onchange?: (value: number) => void;
17
+ /** Callback on input */
18
+ oninput?: (value: number) => void;
19
+ /** Size variant */
20
+ size?: 'xs' | 's' | 'm' | 'l';
21
+ /** Disable the input */
22
+ disabled?: boolean;
23
+ }
24
+ declare const InputNumber: import("svelte").Component<Props, {}, "">;
25
+ type InputNumber = ReturnType<typeof InputNumber>;
26
+ export default InputNumber;
@@ -0,0 +1 @@
1
+ export { default as InputNumber } from './InputNumber.svelte';
@@ -0,0 +1 @@
1
+ export { default as InputNumber } from './InputNumber.svelte';