@svgrid/ui 0.2.0

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 (77) hide show
  1. package/LICENSE +46 -0
  2. package/README.md +53 -0
  3. package/index.mjs +303 -0
  4. package/package.json +49 -0
  5. package/recipes/buttons/button-group.svelte +18 -0
  6. package/recipes/buttons/button.svelte +17 -0
  7. package/recipes/buttons/check-box.svelte +10 -0
  8. package/recipes/buttons/radio-group.svelte +18 -0
  9. package/recipes/buttons/rating.svelte +8 -0
  10. package/recipes/buttons/repeat-button.svelte +10 -0
  11. package/recipes/buttons/switch-button.svelte +12 -0
  12. package/recipes/buttons/toggle-button.svelte +8 -0
  13. package/recipes/date-time/calendar.svelte +15 -0
  14. package/recipes/date-time/date-range-input.svelte +15 -0
  15. package/recipes/date-time/date-time-picker.svelte +16 -0
  16. package/recipes/date-time/time-picker.svelte +15 -0
  17. package/recipes/feedback/alert.svelte +11 -0
  18. package/recipes/feedback/avatar-group.svelte +11 -0
  19. package/recipes/feedback/avatar.svelte +8 -0
  20. package/recipes/feedback/badge.svelte +8 -0
  21. package/recipes/feedback/carousel.svelte +10 -0
  22. package/recipes/feedback/chip.svelte +10 -0
  23. package/recipes/feedback/empty-state.svelte +8 -0
  24. package/recipes/feedback/skeleton.svelte +8 -0
  25. package/recipes/feedback/timeline.svelte +10 -0
  26. package/recipes/inputs/color-input.svelte +8 -0
  27. package/recipes/inputs/duration-input.svelte +9 -0
  28. package/recipes/inputs/masked-input.svelte +14 -0
  29. package/recipes/inputs/number-input.svelte +17 -0
  30. package/recipes/inputs/otp-input.svelte +13 -0
  31. package/recipes/inputs/password-input.svelte +14 -0
  32. package/recipes/inputs/phone-input.svelte +8 -0
  33. package/recipes/inputs/tags-input.svelte +8 -0
  34. package/recipes/inputs/text-area.svelte +15 -0
  35. package/recipes/inputs/text-input.svelte +14 -0
  36. package/recipes/layout/accordion.svelte +17 -0
  37. package/recipes/layout/card.svelte +9 -0
  38. package/recipes/layout/divider.svelte +9 -0
  39. package/recipes/layout/dock-layout.svelte +22 -0
  40. package/recipes/layout/dock-manager.svelte +24 -0
  41. package/recipes/layout/field.svelte +15 -0
  42. package/recipes/layout/file-upload.svelte +16 -0
  43. package/recipes/layout/form.svelte +14 -0
  44. package/recipes/layout/grid-chart.svelte +16 -0
  45. package/recipes/layout/scroll-area.svelte +10 -0
  46. package/recipes/layout/splitter.svelte +13 -0
  47. package/recipes/layout/tabs.svelte +17 -0
  48. package/recipes/navigation/breadcrumb.svelte +12 -0
  49. package/recipes/navigation/command.svelte +12 -0
  50. package/recipes/navigation/nav-pane.svelte +14 -0
  51. package/recipes/navigation/pagination.svelte +13 -0
  52. package/recipes/navigation/rich-text.svelte +8 -0
  53. package/recipes/navigation/stepper.svelte +13 -0
  54. package/recipes/navigation/tour.svelte +14 -0
  55. package/recipes/navigation/tree.svelte +14 -0
  56. package/recipes/overlays/context-menu.svelte +15 -0
  57. package/recipes/overlays/drawer.svelte +13 -0
  58. package/recipes/overlays/menu.svelte +15 -0
  59. package/recipes/overlays/modal.svelte +13 -0
  60. package/recipes/overlays/popover.svelte +9 -0
  61. package/recipes/overlays/toaster.svelte +9 -0
  62. package/recipes/overlays/tooltip.svelte +8 -0
  63. package/recipes/range/circular-progress.svelte +9 -0
  64. package/recipes/range/gauge.svelte +16 -0
  65. package/recipes/range/progress.svelte +10 -0
  66. package/recipes/range/slider.svelte +19 -0
  67. package/recipes/range/sparkline.svelte +9 -0
  68. package/recipes/range/stat.svelte +9 -0
  69. package/recipes/registry.json +1462 -0
  70. package/recipes/selection/auto-complete.svelte +10 -0
  71. package/recipes/selection/combo-box.svelte +13 -0
  72. package/recipes/selection/country-input.svelte +8 -0
  73. package/recipes/selection/drop-down-list.svelte +13 -0
  74. package/recipes/selection/grid-select.svelte +17 -0
  75. package/recipes/selection/list-box.svelte +13 -0
  76. package/recipes/selection/multi-select.svelte +13 -0
  77. package/recipes/selection/tree-select.svelte +15 -0
package/LICENSE ADDED
@@ -0,0 +1,46 @@
1
+ This repository is a multi-package workspace. Different packages ship
2
+ under different licenses. The authoritative license for each package is
3
+ the LICENSE file inside that package directory.
4
+
5
+ ------------------------------------------------------------------------
6
+ Per-package licensing
7
+ ------------------------------------------------------------------------
8
+
9
+ packages/grid/ MIT (terms below, and packages/grid/LICENSE)
10
+ packages/enterprise/ Commercial - see packages/enterprise/LICENSE
11
+ packages/mcp/ Commercial - see packages/mcp/LICENSE
12
+ website/ Proprietary - see website/LICENSE
13
+
14
+ Only `@svgrid/grid` is open source. Everything else in this
15
+ repository (the Pro feature pack, the MCP server, the marketing +
16
+ docs website, and any other package added later that does not ship
17
+ its own MIT LICENSE) is proprietary and may not be copied, modified,
18
+ or redistributed without a paid license. Source is visible in this
19
+ repository for transparency and for paying customers; visibility does
20
+ not grant a license.
21
+
22
+ ------------------------------------------------------------------------
23
+ MIT License (applies ONLY to packages/grid)
24
+ ------------------------------------------------------------------------
25
+
26
+ MIT License
27
+
28
+ Copyright (c) 2026 jQWidgets Ltd
29
+
30
+ Permission is hereby granted, free of charge, to any person obtaining a copy
31
+ of this software and associated documentation files (the "Software"), to deal
32
+ in the Software without restriction, including without limitation the rights
33
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
34
+ copies of the Software, and to permit persons to whom the Software is
35
+ furnished to do so, subject to the following conditions:
36
+
37
+ The above copyright notice and this permission notice shall be included in all
38
+ copies or substantial portions of the Software.
39
+
40
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
41
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
42
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
43
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
44
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
45
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
46
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,53 @@
1
+ # @svgrid/ui
2
+
3
+ Add [SvGrid UI](https://www.svgrid.com/docs/help/ui-components) components to your
4
+ app in one command:
5
+
6
+ ```sh
7
+ npx @svgrid/ui add calendar
8
+ ```
9
+
10
+ ## How it works
11
+
12
+ `@svgrid/ui` is a **recipe scaffolder**, not a component library. `add` writes a
13
+ minimal, ready-to-edit `.svelte` starter into your project that imports from
14
+ [`@svgrid/grid`](https://www.npmjs.com/package/@svgrid/grid), then makes sure the
15
+ package is a dependency and prints the install command. The components themselves
16
+ live in `@svgrid/grid` - you get bug fixes and new features by bumping the package,
17
+ while the file `add` drops in is yours to style and wire however you like.
18
+
19
+ ## Usage
20
+
21
+ ```sh
22
+ # add one component
23
+ npx @svgrid/ui add calendar
24
+
25
+ # add several, into a custom folder
26
+ npx @svgrid/ui add calendar time-picker --dir src/lib/ui
27
+
28
+ # add a whole family
29
+ npx @svgrid/ui add date-time
30
+
31
+ # install the dependency automatically (default: just prints the command)
32
+ npx @svgrid/ui add calendar --install
33
+
34
+ # see what you can add
35
+ npx @svgrid/ui list
36
+ ```
37
+
38
+ ### Options
39
+
40
+ | Flag | Description |
41
+ | ---------------- | ------------------------------------------------------------------------ |
42
+ | `--dir <path>` | Where to write files. Default: `src/lib/components/ui` (or `componentsDir` in a project `svgrid.json`). |
43
+ | `--force` | Overwrite files that already exist. |
44
+ | `--install` | Run your package manager to install deps instead of just printing it. |
45
+
46
+ ## Components
47
+
48
+ The whole SvGrid UI catalogue is available - 70+ components across `date-time`,
49
+ `buttons`, `inputs`, `selection`, `range`, `overlays`, `layout`, `feedback` and
50
+ `navigation`. Add one by id, several at once, or a whole family by its group id
51
+ (e.g. `npx @svgrid/ui add inputs`). Run `npx @svgrid/ui list` for the full set.
52
+
53
+ MIT © jQWidgets Ltd
package/index.mjs ADDED
@@ -0,0 +1,303 @@
1
+ #!/usr/bin/env node
2
+ // @svgrid/ui - add SvGrid UI components to your app, one command at a time.
3
+ //
4
+ // npx @svgrid/ui add calendar
5
+ // npx @svgrid/ui add calendar time-picker --dir src/lib/ui
6
+ // npx @svgrid/ui add date-time # the whole date/time family
7
+ // npx @svgrid/ui list
8
+ //
9
+ // Recipe-scaffolder model: `add` writes a minimal, ready-to-EDIT .svelte starter
10
+ // that imports from `@svgrid/grid` (which you own and can change), then makes
11
+ // sure the package is a dependency and prints the install command. It does NOT
12
+ // vendor library source - the components live in `@svgrid/grid`.
13
+ //
14
+ // Zero runtime dependencies - Node built-ins only.
15
+
16
+ import { cp, mkdir, readFile, writeFile } from 'node:fs/promises'
17
+ import { existsSync } from 'node:fs'
18
+ import { dirname, join, resolve } from 'node:path'
19
+ import { fileURLToPath } from 'node:url'
20
+ import { stdin, stdout } from 'node:process'
21
+ import { spawnSync } from 'node:child_process'
22
+
23
+ const __dirname = dirname(fileURLToPath(import.meta.url))
24
+ const RECIPES_DIR = join(__dirname, 'recipes')
25
+
26
+ const c = {
27
+ reset: '\x1b[0m',
28
+ bold: '\x1b[1m',
29
+ dim: '\x1b[2m',
30
+ green: '\x1b[32m',
31
+ cyan: '\x1b[36m',
32
+ red: '\x1b[31m',
33
+ yellow: '\x1b[33m',
34
+ }
35
+ const color = stdout.isTTY ? (k, s) => `${c[k]}${s}${c.reset}` : (_k, s) => s
36
+
37
+ // Package managers: lockfile -> add command. Order matters (first match wins).
38
+ const PMS = [
39
+ { id: 'pnpm', lock: 'pnpm-lock.yaml', add: 'pnpm add' },
40
+ { id: 'bun', lock: 'bun.lockb', add: 'bun add' },
41
+ { id: 'yarn', lock: 'yarn.lock', add: 'yarn add' },
42
+ { id: 'npm', lock: 'package-lock.json', add: 'npm install' },
43
+ ]
44
+
45
+ function parseArgs(argv) {
46
+ const args = { _: [], dir: null, force: false, install: false, help: false, js: false }
47
+ for (let i = 0; i < argv.length; i++) {
48
+ const a = argv[i]
49
+ if (a === '--help' || a === '-h') args.help = true
50
+ else if (a === '--force' || a === '-f') args.force = true
51
+ else if (a === '--install') args.install = true
52
+ else if (a === '--js') args.js = true
53
+ else if (a === '--ts') args.js = false
54
+ else if (a === '--dir' || a === '-d') args.dir = argv[++i]
55
+ else if (a.startsWith('--dir=')) args.dir = a.slice('--dir='.length)
56
+ else if (!a.startsWith('-')) args._.push(a)
57
+ }
58
+ return args
59
+ }
60
+
61
+ async function loadRegistry() {
62
+ const raw = await readFile(join(RECIPES_DIR, 'registry.json'), 'utf8')
63
+ return JSON.parse(raw)
64
+ }
65
+
66
+ /** Match a user-typed id against the registry: exact id, alias, or lowercase. */
67
+ function resolveItem(registry, token) {
68
+ const key = token.trim().toLowerCase()
69
+ return (
70
+ registry.items.find((it) => it.id === key) ??
71
+ registry.items.find((it) => (it.aliases ?? []).includes(key)) ??
72
+ null
73
+ )
74
+ }
75
+
76
+ /** Expand a token into one or more recipe items (a group alias -> its members). */
77
+ function expand(registry, token) {
78
+ const key = token.trim().toLowerCase()
79
+ const group = registry.groups?.[key]
80
+ if (group) return group.items.map((id) => resolveItem(registry, id)).filter(Boolean)
81
+ const item = resolveItem(registry, key)
82
+ return item ? [item] : []
83
+ }
84
+
85
+ /** Walk up from `start` to the nearest directory containing a package.json. */
86
+ function findProjectRoot(start) {
87
+ let dir = start
88
+ while (true) {
89
+ if (existsSync(join(dir, 'package.json'))) return dir
90
+ const parent = dirname(dir)
91
+ if (parent === dir) return null
92
+ dir = parent
93
+ }
94
+ }
95
+
96
+ function detectPm(root) {
97
+ if (!root) return PMS.find((p) => p.id === 'npm')
98
+ return PMS.find((p) => existsSync(join(root, p.lock))) ?? PMS.find((p) => p.id === 'npm')
99
+ }
100
+
101
+ async function targetDir(args, projectRoot, cwd) {
102
+ if (args.dir) return resolve(cwd, args.dir)
103
+ // Optional project config: svgrid.json { componentsDir }
104
+ if (projectRoot) {
105
+ const cfgPath = join(projectRoot, 'svgrid.json')
106
+ if (existsSync(cfgPath)) {
107
+ try {
108
+ const cfg = JSON.parse(await readFile(cfgPath, 'utf8'))
109
+ if (cfg.componentsDir) return resolve(projectRoot, cfg.componentsDir)
110
+ } catch {
111
+ /* fall through to the default */
112
+ }
113
+ }
114
+ return join(projectRoot, 'src', 'lib', 'components', 'ui')
115
+ }
116
+ return join(cwd, 'src', 'lib', 'components', 'ui')
117
+ }
118
+
119
+ /** Add any missing deps to the nearest package.json (dependencies). Returns the
120
+ * list actually added so the caller can report / install them. */
121
+ async function ensureDeps(projectRoot, deps) {
122
+ if (!projectRoot) return deps
123
+ const pkgPath = join(projectRoot, 'package.json')
124
+ let pkg
125
+ try {
126
+ pkg = JSON.parse(await readFile(pkgPath, 'utf8'))
127
+ } catch {
128
+ return deps
129
+ }
130
+ pkg.dependencies ??= {}
131
+ const added = []
132
+ for (const dep of deps) {
133
+ const known = pkg.dependencies[dep] || pkg.devDependencies?.[dep]
134
+ if (!known) {
135
+ pkg.dependencies[dep] = 'latest'
136
+ added.push(dep)
137
+ }
138
+ }
139
+ if (added.length) await writeFile(pkgPath, JSON.stringify(pkg, null, 2) + '\n')
140
+ return added
141
+ }
142
+
143
+ function printHelp() {
144
+ stdout.write(`
145
+ ${color('bold', '@svgrid/ui')} - add SvGrid UI components to your app
146
+
147
+ ${color('bold', 'Usage')}
148
+ npx @svgrid/ui add <component...> [--dir <path>] [--force] [--install]
149
+ npx @svgrid/ui list
150
+
151
+ ${color('bold', 'Commands')}
152
+ ${color('cyan', 'add')} Write a ready-to-edit recipe for each component into your project.
153
+ ${color('cyan', 'list')} Show the components you can add.
154
+
155
+ ${color('bold', 'Options')}
156
+ --dir <path> Where to write files (default: src/lib/components/ui, or the
157
+ "componentsDir" in a project svgrid.json).
158
+ --force Overwrite files that already exist.
159
+ --install Run the package manager to install deps (default: just print it).
160
+
161
+ ${color('bold', 'Examples')}
162
+ npx @svgrid/ui add calendar
163
+ npx @svgrid/ui add calendar time-picker --dir src/lib/ui
164
+ npx @svgrid/ui add date-time --install
165
+ `)
166
+ }
167
+
168
+ async function cmdList(registry) {
169
+ stdout.write(`\n${color('bold', 'Available components')}\n`)
170
+ for (const it of registry.items) {
171
+ stdout.write(` ${color('cyan', it.id.padEnd(18))} ${color('dim', it.description)}\n`)
172
+ }
173
+ const groups = Object.entries(registry.groups ?? {})
174
+ if (groups.length) {
175
+ stdout.write(`\n${color('bold', 'Groups')}\n`)
176
+ for (const [id, g] of groups) {
177
+ stdout.write(` ${color('cyan', id.padEnd(18))} ${color('dim', g.items.join(', '))}\n`)
178
+ }
179
+ }
180
+ stdout.write(`\n${color('dim', 'Add one with:')} npx @svgrid/ui add ${registry.items[0]?.id ?? 'calendar'}\n\n`)
181
+ }
182
+
183
+ async function cmdAdd(registry, tokens, args) {
184
+ if (!tokens.length) {
185
+ stdout.write(`${color('red', '✖')} Nothing to add. Try: ${color('cyan', 'npx @svgrid/ui list')}\n`)
186
+ process.exit(1)
187
+ }
188
+
189
+ // Resolve tokens -> unique items.
190
+ const items = new Map()
191
+ const unknown = []
192
+ for (const tok of tokens) {
193
+ const matched = expand(registry, tok)
194
+ if (!matched.length) unknown.push(tok)
195
+ for (const it of matched) items.set(it.id, it)
196
+ }
197
+ if (unknown.length) {
198
+ stdout.write(
199
+ `${color('red', '✖')} Unknown component(s): ${unknown.join(', ')}\n` +
200
+ ` See ${color('cyan', 'npx @svgrid/ui list')} for the available set.\n`,
201
+ )
202
+ process.exit(1)
203
+ }
204
+
205
+ const cwd = process.cwd()
206
+ const projectRoot = findProjectRoot(cwd)
207
+ const dest = await targetDir(args, projectRoot, cwd)
208
+ await mkdir(dest, { recursive: true })
209
+
210
+ const written = []
211
+ const skipped = []
212
+ const deps = new Set()
213
+ for (const it of items.values()) {
214
+ for (const d of it.deps ?? []) deps.add(d)
215
+ for (const file of it.files ?? []) {
216
+ const outPath = join(dest, file.write)
217
+ if (existsSync(outPath) && !args.force) {
218
+ skipped.push(file.write)
219
+ continue
220
+ }
221
+ await cp(join(RECIPES_DIR, file.from), outPath)
222
+ written.push(file.write)
223
+ }
224
+ }
225
+
226
+ // Report writes.
227
+ stdout.write(`\n`)
228
+ for (const f of written) stdout.write(` ${color('green', '+')} ${relFromCwd(cwd, join(dest, f))}\n`)
229
+ for (const f of skipped)
230
+ stdout.write(` ${color('yellow', '•')} ${f} ${color('dim', 'already exists (use --force to overwrite)')}\n`)
231
+ if (!written.length) {
232
+ stdout.write(`\n${color('yellow', '!')} No files written.\n\n`)
233
+ return
234
+ }
235
+
236
+ // Ensure deps + install.
237
+ const added = await ensureDeps(projectRoot, [...deps])
238
+ const pm = detectPm(projectRoot)
239
+ if (added.length) {
240
+ if (args.install && projectRoot) {
241
+ stdout.write(`\n${color('dim', `Installing with ${pm.id}...`)}\n`)
242
+ const [bin, ...rest] = pm.add.split(' ')
243
+ const res = spawnSync(bin, [...rest, ...added], { cwd: projectRoot, stdio: 'inherit', shell: true })
244
+ if (res.status !== 0) {
245
+ stdout.write(`${color('yellow', '!')} Install failed - run it yourself: ${color('cyan', `${pm.add} ${added.join(' ')}`)}\n`)
246
+ }
247
+ } else {
248
+ stdout.write(`\n${color('bold', 'Install the dependency')}\n ${color('cyan', `${pm.add} ${added.join(' ')}`)}\n`)
249
+ }
250
+ } else if ([...deps].length) {
251
+ stdout.write(`\n${color('dim', `${[...deps].join(', ')} already in package.json.`)}\n`)
252
+ }
253
+
254
+ // Usage hint.
255
+ const first = [...items.values()][0]
256
+ stdout.write(`\n${color('green', '✔')} Added ${written.length} file(s). They're yours - edit away.\n`)
257
+ if (first) {
258
+ stdout.write(` ${color('dim', 'Use it:')} import { ${exportName(first.id)} } from '@svgrid/grid'\n`)
259
+ }
260
+ stdout.write(`\n${color('dim', 'Docs:')} https://www.svgrid.com/docs/help/ui-components\n\n`)
261
+ }
262
+
263
+ /** Component export name from its id (calendar -> SvCalendar, time-picker ->
264
+ * SvTimePicker). */
265
+ function exportName(id) {
266
+ return 'Sv' + id.split('-').map((s) => s[0].toUpperCase() + s.slice(1)).join('')
267
+ }
268
+
269
+ function relFromCwd(cwd, p) {
270
+ const r = p.startsWith(cwd) ? p.slice(cwd.length).replace(/^[\\/]/, '') : p
271
+ return r.split('\\').join('/')
272
+ }
273
+
274
+ async function main() {
275
+ const argv = process.argv.slice(2)
276
+ const args = parseArgs(argv)
277
+ const [command, ...rest] = args._
278
+
279
+ if (args.help || !command) return printHelp()
280
+
281
+ const registry = await loadRegistry()
282
+ stdout.write(`${color('bold', '◆ @svgrid/ui')}\n`)
283
+
284
+ switch (command) {
285
+ case 'list':
286
+ case 'ls':
287
+ return cmdList(registry)
288
+ case 'add':
289
+ return cmdAdd(registry, rest, args)
290
+ default:
291
+ // Treat a bare component id as `add <id>` for convenience.
292
+ if (resolveItem(registry, command) || registry.groups?.[command]) {
293
+ return cmdAdd(registry, [command, ...rest], args)
294
+ }
295
+ stdout.write(`${color('red', '✖')} Unknown command "${command}". Try ${color('cyan', 'add')} or ${color('cyan', 'list')}.\n`)
296
+ process.exit(1)
297
+ }
298
+ }
299
+
300
+ main().catch((err) => {
301
+ console.error(err)
302
+ process.exit(1)
303
+ })
package/package.json ADDED
@@ -0,0 +1,49 @@
1
+ {
2
+ "name": "@svgrid/ui",
3
+ "funding": {
4
+ "type": "commercial",
5
+ "url": "https://svgrid.com/pricing"
6
+ },
7
+ "version": "0.2.0",
8
+ "description": "Add SvGrid UI components to your app in one command: npx @svgrid/ui add calendar",
9
+ "type": "module",
10
+ "license": "MIT",
11
+ "author": "jQWidgets Ltd",
12
+ "homepage": "https://www.svgrid.com",
13
+ "repository": {
14
+ "type": "git",
15
+ "url": "https://github.com/sv-grid/sv-grid.git",
16
+ "directory": "packages/svgrid-ui"
17
+ },
18
+ "keywords": [
19
+ "svelte",
20
+ "sveltekit",
21
+ "svgrid",
22
+ "sv-grid",
23
+ "ui",
24
+ "components",
25
+ "cli",
26
+ "scaffold",
27
+ "add",
28
+ "recipe"
29
+ ],
30
+ "bin": "index.mjs",
31
+ "files": [
32
+ "index.mjs",
33
+ "recipes"
34
+ ],
35
+ "engines": {
36
+ "node": ">=18"
37
+ },
38
+ "devDependencies": {
39
+ "@sveltejs/vite-plugin-svelte": "^7.0.0",
40
+ "@tsconfig/svelte": "^5.0.8",
41
+ "svelte": "^5.55.5",
42
+ "svelte-check": "^4.4.6",
43
+ "typescript": "6.0.3",
44
+ "@svgrid/grid": "2.2.0"
45
+ },
46
+ "scripts": {
47
+ "test:types": "svelte-check --tsconfig ./tsconfig.json"
48
+ }
49
+ }
@@ -0,0 +1,18 @@
1
+ <script lang="ts">
2
+ // SvButtonGroup - a segmented single/multi-select button bar. Your copy, edit freely. Docs: https://www.svgrid.com/docs/help/ui-components/sv-button-group
3
+ import { SvButtonGroup, type ButtonGroupItem } from '@svgrid/grid'
4
+
5
+ const views: ButtonGroupItem[] = [
6
+ { value: 'day', label: 'Day' },
7
+ { value: 'week', label: 'Week' },
8
+ { value: 'month', label: 'Month' },
9
+ ]
10
+ let view = $state<string | number>('week')
11
+ </script>
12
+
13
+ <SvButtonGroup
14
+ items={views}
15
+ value={view}
16
+ mode="single"
17
+ onChange={(v) => (view = v as string)}
18
+ />
@@ -0,0 +1,17 @@
1
+ <script lang="ts">
2
+ // SvButton - the themeable press primitive with variants and loading. Your copy, edit freely. Docs: https://www.svgrid.com/docs/help/ui-components/sv-button
3
+ import { SvButton } from '@svgrid/grid'
4
+
5
+ let saving = $state(false)
6
+ async function save() {
7
+ saving = true
8
+ try {
9
+ await new Promise((r) => setTimeout(r, 800))
10
+ } finally {
11
+ saving = false
12
+ }
13
+ }
14
+ </script>
15
+
16
+ <SvButton variant="primary" loading={saving} onclick={save}>Save</SvButton>
17
+ <SvButton variant="outline">Cancel</SvButton>
@@ -0,0 +1,10 @@
1
+ <script lang="ts">
2
+ // SvCheckBox - a themed checkbox with a true indeterminate state. Your copy, edit freely. Docs: https://www.svgrid.com/docs/help/ui-components/sv-check-box
3
+ import { SvCheckBox } from '@svgrid/grid'
4
+
5
+ let agree = $state(false)
6
+ </script>
7
+
8
+ <SvCheckBox checked={agree} onChange={(v) => (agree = v)}>
9
+ I accept the terms
10
+ </SvCheckBox>
@@ -0,0 +1,18 @@
1
+ <script lang="ts">
2
+ // SvRadioGroup - an accessible single-select radio group. Your copy, edit freely. Docs: https://www.svgrid.com/docs/help/ui-components/sv-radio-group
3
+ import { SvRadioGroup, type RadioOption } from '@svgrid/grid'
4
+
5
+ const plans: RadioOption[] = [
6
+ { value: 'free', label: 'Free' },
7
+ { value: 'pro', label: 'Pro' },
8
+ { value: 'team', label: 'Team' },
9
+ ]
10
+ let plan = $state<string | number | null>('pro')
11
+ </script>
12
+
13
+ <SvRadioGroup
14
+ label="Plan"
15
+ options={plans}
16
+ value={plan}
17
+ onChange={(v) => (plan = v)}
18
+ />
@@ -0,0 +1,8 @@
1
+ <script lang="ts">
2
+ // SvRating - a star rating control on the ARIA slider pattern. Your copy, edit freely. Docs: https://www.svgrid.com/docs/help/ui-components/sv-rating
3
+ import { SvRating } from '@svgrid/grid'
4
+
5
+ let score = $state(4)
6
+ </script>
7
+
8
+ <SvRating label="Your rating" value={score} onChange={(v) => (score = v)} />
@@ -0,0 +1,10 @@
1
+ <script lang="ts">
2
+ // SvRepeatButton - a button that keeps firing while held. Your copy, edit freely. Docs: https://www.svgrid.com/docs/help/ui-components/sv-repeat-button
3
+ import { SvRepeatButton } from '@svgrid/grid'
4
+
5
+ let qty = $state(1)
6
+ </script>
7
+
8
+ <SvRepeatButton ariaLabel="Decrease" onclick={() => (qty = Math.max(0, qty - 1))}>-</SvRepeatButton>
9
+ <span>{qty}</span>
10
+ <SvRepeatButton ariaLabel="Increase" onclick={() => (qty += 1)}>+</SvRepeatButton>
@@ -0,0 +1,12 @@
1
+ <script lang="ts">
2
+ // SvSwitchButton - an on/off sliding switch for immediate settings. Your copy, edit freely. Docs: https://www.svgrid.com/docs/help/ui-components/sv-switch-button
3
+ import { SvSwitchButton } from '@svgrid/grid'
4
+
5
+ let notify = $state(true)
6
+ </script>
7
+
8
+ <SvSwitchButton
9
+ label="Email notifications"
10
+ checked={notify}
11
+ onChange={(v) => (notify = v)}
12
+ />
@@ -0,0 +1,8 @@
1
+ <script lang="ts">
2
+ // SvToggleButton - a button with a sticky pressed on/off state. Your copy, edit freely. Docs: https://www.svgrid.com/docs/help/ui-components/sv-toggle-button
3
+ import { SvToggleButton } from '@svgrid/grid'
4
+
5
+ let bold = $state(false)
6
+ </script>
7
+
8
+ <SvToggleButton pressed={bold} onChange={(v) => (bold = v)}>Bold</SvToggleButton>
@@ -0,0 +1,15 @@
1
+ <script lang="ts">
2
+ // SvCalendar - a themeable month / year / decade calendar. This is your copy,
3
+ // edit it freely. Docs: https://www.svgrid.com/docs/help/ui-components/sv-calendar
4
+ import { SvCalendar } from '@svgrid/grid'
5
+
6
+ let value = $state<Date[]>([new Date()])
7
+ </script>
8
+
9
+ <SvCalendar
10
+ {value}
11
+ selectionMode="range"
12
+ firstDayOfWeek={1}
13
+ footer
14
+ onChange={(dates) => (value = dates)}
15
+ />
@@ -0,0 +1,15 @@
1
+ <script lang="ts">
2
+ // SvDateRangeInput - a compact start/end field over a two-month range calendar.
3
+ // This is your copy, edit it freely.
4
+ // Docs: https://www.svgrid.com/docs/help/ui-components/sv-date-range-input
5
+ import { SvDateRangeInput } from '@svgrid/grid'
6
+
7
+ let range = $state<[Date, Date] | null>(null)
8
+ </script>
9
+
10
+ <SvDateRangeInput
11
+ value={range}
12
+ formatString="yyyy-MM-dd"
13
+ months={2}
14
+ onChange={(v) => (range = v)}
15
+ />
@@ -0,0 +1,16 @@
1
+ <script lang="ts">
2
+ // SvDateTimePicker - a masked text field with DATE / TIME dropdown tabs. This
3
+ // is your copy, edit it freely.
4
+ // Docs: https://www.svgrid.com/docs/help/ui-components/sv-date-time-picker
5
+ import { SvDateTimePicker } from '@svgrid/grid'
6
+
7
+ let value = $state<Date | null>(new Date())
8
+ </script>
9
+
10
+ <SvDateTimePicker
11
+ {value}
12
+ formatString="yyyy-MM-dd HH:mm"
13
+ dropDownDisplayMode="both"
14
+ nullable
15
+ onChange={(d) => (value = d)}
16
+ />
@@ -0,0 +1,15 @@
1
+ <script lang="ts">
2
+ // SvTimePicker - an analog clock-dial time picker. This is your copy, edit it
3
+ // freely. Docs: https://www.svgrid.com/docs/help/ui-components/sv-time-picker
4
+ import { SvTimePicker } from '@svgrid/grid'
5
+
6
+ let time = $state(new Date())
7
+ </script>
8
+
9
+ <SvTimePicker
10
+ value={time}
11
+ format="12-hour"
12
+ minuteInterval={5}
13
+ footer
14
+ onChange={(d) => (time = d)}
15
+ />
@@ -0,0 +1,11 @@
1
+ <script lang="ts">
2
+ // SvAlert - an inline contextual message with icon and actions. Your copy, edit freely. Docs: https://www.svgrid.com/docs/help/ui-components/sv-alert
3
+ import { SvAlert, SvButton } from '@svgrid/grid'
4
+ </script>
5
+
6
+ <SvAlert variant="success" title="Saved">Your changes are live.</SvAlert>
7
+
8
+ <SvAlert variant="danger" title="Payment failed" dismissible>
9
+ We could not charge your card.
10
+ {#snippet actions()}<SvButton size="sm" variant="danger">Retry</SvButton>{/snippet}
11
+ </SvAlert>
@@ -0,0 +1,11 @@
1
+ <script lang="ts">
2
+ // SvAvatarGroup - overlapping avatars with a "+N" overflow pill. Your copy, edit freely. Docs: https://www.svgrid.com/docs/help/ui-components/sv-avatar-group
3
+ import { SvAvatarGroup } from '@svgrid/grid'
4
+ </script>
5
+
6
+ <SvAvatarGroup max={3} avatars={[
7
+ { name: 'Ada Lovelace' },
8
+ { name: 'Alan Turing' },
9
+ { name: 'Grace Hopper' },
10
+ { name: 'Ken Thompson' },
11
+ ]} />
@@ -0,0 +1,8 @@
1
+ <script lang="ts">
2
+ // SvAvatar - a user avatar with initials-and-color fallback. Your copy, edit freely. Docs: https://www.svgrid.com/docs/help/ui-components/sv-avatar
3
+ import { SvAvatar } from '@svgrid/grid'
4
+ </script>
5
+
6
+ <SvAvatar name="Ada Lovelace" src="/ada.jpg" status="online" />
7
+ <SvAvatar name="Grace Hopper" />
8
+ <SvAvatar name="Alan Turing" size="lg" shape="square" />