@svgrid/create 2.2.2 → 2.4.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.
- package/README.md +81 -77
- package/index.mjs +60 -17
- package/package.json +2 -2
- package/templates/admin-dashboard/src/app.css +180 -180
- package/templates/minimal/README.md +19 -0
- package/templates/minimal/index.html +11 -1
- package/templates/minimal/src/App.svelte +143 -54
- package/templates/minimal/src/app.css +36 -0
- package/templates/minimal/src/main.js +1 -0
package/README.md
CHANGED
|
@@ -1,77 +1,81 @@
|
|
|
1
|
-
<p align="center">
|
|
2
|
-
<img src="https://svgrid.com/brand/svgrid-logo-icon-1200.png" alt="SvGrid" width="100" height="100" />
|
|
3
|
-
</p>
|
|
4
|
-
|
|
5
|
-
<h1 align="center">@svgrid/create</h1>
|
|
6
|
-
|
|
7
|
-
<p align="center"><strong>Scaffold a Svelte 5 app with a data grid already wired up.</strong></p>
|
|
8
|
-
|
|
9
|
-
<p align="center">
|
|
10
|
-
<a href="https://www.npmjs.com/package/@svgrid/create"><img src="https://img.shields.io/npm/v/%40svgrid%2Fcreate.svg?label=%40svgrid%2Fcreate" alt="npm version" /></a>
|
|
11
|
-
<a href="./LICENSE"><img src="https://img.shields.io/badge/license-MIT-brightgreen.svg" alt="MIT License" /></a>
|
|
12
|
-
</p>
|
|
13
|
-
|
|
14
|
-
<p align="center">
|
|
15
|
-
<a href="https://svgrid.com">Website</a> ·
|
|
16
|
-
<a href="https://svgrid.com/docs/">Docs</a> ·
|
|
17
|
-
<a href="https://svgrid.com/demos/">Demos</a>
|
|
18
|
-
</p>
|
|
19
|
-
|
|
20
|
-
---
|
|
21
|
-
|
|
22
|
-
Scaffold a [Svelte](https://svelte.dev) app powered by
|
|
23
|
-
[SvGrid](https://svgrid.com) - the Svelte 5 data grid and data table - in one
|
|
24
|
-
command.
|
|
25
|
-
|
|
26
|
-
```bash
|
|
27
|
-
npm create @svgrid@latest
|
|
28
|
-
# or
|
|
29
|
-
pnpm create @svgrid
|
|
30
|
-
# or
|
|
31
|
-
yarn create @svgrid
|
|
32
|
-
```
|
|
33
|
-
|
|
34
|
-
Interactive by default. Or pass a directory and template directly:
|
|
35
|
-
|
|
36
|
-
```bash
|
|
37
|
-
npm create @svgrid@latest my-app -- --template admin-dashboard
|
|
38
|
-
pnpm create @svgrid my-app -t minimal
|
|
39
|
-
```
|
|
40
|
-
|
|
41
|
-
## Templates
|
|
42
|
-
|
|
43
|
-
| Template | Stack | Best for |
|
|
44
|
-
| --- | --- | --- |
|
|
45
|
-
| `minimal` | Vite + Svelte 5 + SvGrid | Dropping a grid into something quickly |
|
|
46
|
-
| `admin-dashboard` | SvelteKit + Tailwind + SvGrid, deploy to Vercel | A real dashboard / internal tool |
|
|
47
|
-
|
|
48
|
-
## Options
|
|
49
|
-
|
|
50
|
-
| Flag | Description |
|
|
51
|
-
| --- | --- |
|
|
52
|
-
| `--template`, `-t` | `minimal` or `admin-dashboard` |
|
|
53
|
-
| `--theme <id>` |
|
|
54
|
-
| `--dark` / `--light` |
|
|
55
|
-
| `--force`, `-f` | Scaffold into a non-empty directory |
|
|
56
|
-
| `--help`, `-h` | Show usage |
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
```bash
|
|
62
|
-
npm create @svgrid@latest my-app -- -t admin-dashboard --theme material --light
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
```
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
1
|
+
<p align="center">
|
|
2
|
+
<img src="https://svgrid.com/brand/svgrid-logo-icon-1200.png" alt="SvGrid" width="100" height="100" />
|
|
3
|
+
</p>
|
|
4
|
+
|
|
5
|
+
<h1 align="center">@svgrid/create</h1>
|
|
6
|
+
|
|
7
|
+
<p align="center"><strong>Scaffold a Svelte 5 app with a data grid already wired up.</strong></p>
|
|
8
|
+
|
|
9
|
+
<p align="center">
|
|
10
|
+
<a href="https://www.npmjs.com/package/@svgrid/create"><img src="https://img.shields.io/npm/v/%40svgrid%2Fcreate.svg?label=%40svgrid%2Fcreate" alt="npm version" /></a>
|
|
11
|
+
<a href="./LICENSE"><img src="https://img.shields.io/badge/license-MIT-brightgreen.svg" alt="MIT License" /></a>
|
|
12
|
+
</p>
|
|
13
|
+
|
|
14
|
+
<p align="center">
|
|
15
|
+
<a href="https://svgrid.com">Website</a> ·
|
|
16
|
+
<a href="https://svgrid.com/docs/">Docs</a> ·
|
|
17
|
+
<a href="https://svgrid.com/demos/">Demos</a>
|
|
18
|
+
</p>
|
|
19
|
+
|
|
20
|
+
---
|
|
21
|
+
|
|
22
|
+
Scaffold a [Svelte](https://svelte.dev) app powered by
|
|
23
|
+
[SvGrid](https://svgrid.com) - the Svelte 5 data grid and data table - in one
|
|
24
|
+
command.
|
|
25
|
+
|
|
26
|
+
```bash
|
|
27
|
+
npm create @svgrid@latest
|
|
28
|
+
# or
|
|
29
|
+
pnpm create @svgrid
|
|
30
|
+
# or
|
|
31
|
+
yarn create @svgrid
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
Interactive by default. Or pass a directory and template directly:
|
|
35
|
+
|
|
36
|
+
```bash
|
|
37
|
+
npm create @svgrid@latest my-app -- --template admin-dashboard
|
|
38
|
+
pnpm create @svgrid my-app -t minimal
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
## Templates
|
|
42
|
+
|
|
43
|
+
| Template | Stack | Best for |
|
|
44
|
+
| --- | --- | --- |
|
|
45
|
+
| `minimal` | Vite + Svelte 5 + SvGrid | Dropping a grid into something quickly |
|
|
46
|
+
| `admin-dashboard` | SvelteKit + Tailwind + SvGrid, deploy to Vercel | A real dashboard / internal tool |
|
|
47
|
+
|
|
48
|
+
## Options
|
|
49
|
+
|
|
50
|
+
| Flag | Description |
|
|
51
|
+
| --- | --- |
|
|
52
|
+
| `--template`, `-t` | `minimal` or `admin-dashboard` |
|
|
53
|
+
| `--theme <id>` | One of `@svgrid/grid`'s 20 built-in presets - shadcn, Tailwind, Material, Excel, Fluent, and more (default: `tailwind`) |
|
|
54
|
+
| `--dark` / `--light` | Start in dark or light mode. `minimal` follows the OS when neither is given |
|
|
55
|
+
| `--force`, `-f` | Scaffold into a non-empty directory |
|
|
56
|
+
| `--help`, `-h` | Show usage |
|
|
57
|
+
|
|
58
|
+
Either template prompts for a theme and a light/dark mode when run
|
|
59
|
+
interactively, or takes them as flags:
|
|
60
|
+
|
|
61
|
+
```bash
|
|
62
|
+
npm create @svgrid@latest my-app -- -t admin-dashboard --theme material --light
|
|
63
|
+
npm create @svgrid@latest my-app -- -t minimal --theme nord --dark
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
Both ship a working toggle, so the choice is a starting point rather than
|
|
67
|
+
something you are stuck with.
|
|
68
|
+
|
|
69
|
+
Then:
|
|
70
|
+
|
|
71
|
+
```bash
|
|
72
|
+
cd my-app
|
|
73
|
+
npm install
|
|
74
|
+
npm run dev
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
All templates use the free MIT `@svgrid/grid` core. Add
|
|
78
|
+
[`@svgrid/enterprise`](https://svgrid.com/pricing/) for Excel/PDF export, import,
|
|
79
|
+
print, pivot, and AI helpers.
|
|
80
|
+
|
|
81
|
+
SvGrid(TM) is a trademark of jQWidgets Ltd. This package is MIT-licensed.
|
package/index.mjs
CHANGED
|
@@ -80,8 +80,8 @@ ${Object.entries(TEMPLATES)
|
|
|
80
80
|
.join('\n')}
|
|
81
81
|
|
|
82
82
|
${color('bold', 'Options')}
|
|
83
|
-
--theme <id>
|
|
84
|
-
--dark / --light
|
|
83
|
+
--theme <id> One of: ${THEME_IDS.join(', ')} (default: tailwind).
|
|
84
|
+
--dark / --light Start in dark or light mode (default: dark).
|
|
85
85
|
--force Scaffold into a non-empty directory.
|
|
86
86
|
|
|
87
87
|
${color('bold', 'Examples')}
|
|
@@ -93,17 +93,16 @@ ${color('bold', 'Examples')}
|
|
|
93
93
|
}
|
|
94
94
|
|
|
95
95
|
const THEME_IDS = [
|
|
96
|
-
'shadcn', 'tailwind', 'material', 'excel', 'fluent', 'carbon', 'sap',
|
|
96
|
+
'ember', 'shadcn', 'tailwind', 'material', 'excel', 'fluent', 'carbon', 'sap',
|
|
97
97
|
'salesforce', 'atlassian', 'github', 'antd', 'ag-alpine', 'bootstrap',
|
|
98
98
|
'vercel', 'linear', 'notion', 'nord', 'dracula', 'catppuccin',
|
|
99
99
|
]
|
|
100
100
|
|
|
101
101
|
/**
|
|
102
|
-
* Resolve the theme + mode to apply
|
|
103
|
-
*
|
|
104
|
-
*
|
|
105
|
-
*
|
|
106
|
-
* the canonical set.
|
|
102
|
+
* Resolve the theme + mode to apply: from `--theme` / `--dark` / `--light`
|
|
103
|
+
* flags, or (in a TTY) an interactive prompt; otherwise left untouched (the
|
|
104
|
+
* template's own default look stands). Reuses `@svgrid/grid/themes`'s own
|
|
105
|
+
* preset list, so the picker can never drift from the canonical set.
|
|
107
106
|
*/
|
|
108
107
|
async function promptTheme(args, ask, interactive) {
|
|
109
108
|
let themes
|
|
@@ -132,13 +131,24 @@ async function promptTheme(args, ask, interactive) {
|
|
|
132
131
|
const a = await ask('Light or dark mode? (light/dark)', 'dark')
|
|
133
132
|
mode = /^l(ight)?$/i.test(a) ? 'light' : 'dark'
|
|
134
133
|
}
|
|
134
|
+
// Whether the mode was actually chosen, as opposed to falling through to the
|
|
135
|
+
// default below. minimal follows the OS when nobody said, which is only
|
|
136
|
+
// distinguishable from an explicit --dark by keeping this flag.
|
|
137
|
+
const explicitMode = mode != null
|
|
135
138
|
mode ??= 'dark' // matches the template's existing default - only patched when 'light' is chosen.
|
|
136
139
|
|
|
137
|
-
return { themeId, mode, name: themes.getThemePreset(themeId)?.name ?? themeId, themes }
|
|
140
|
+
return { themeId, mode, explicitMode, name: themes.getThemePreset(themeId)?.name ?? themeId, themes }
|
|
138
141
|
}
|
|
139
142
|
|
|
140
|
-
/** Rewrite `app.css`
|
|
141
|
-
*
|
|
143
|
+
/** Rewrite `app.css` between the `svgrid-theme` markers.
|
|
144
|
+
*
|
|
145
|
+
* The two templates want different things there. admin-dashboard inlines the
|
|
146
|
+
* resolved `--sg-*` tokens, because its Tailwind layer aliases them to
|
|
147
|
+
* `--app-*` and having the values on the page makes them editable. minimal
|
|
148
|
+
* just points at the preset stylesheet the package already ships, which keeps
|
|
149
|
+
* its `app.css` short enough to read in one go. Which one we are looking at
|
|
150
|
+
* is decided by what the marked block currently holds, not by the template
|
|
151
|
+
* name, so a hand-edited project still round-trips. */
|
|
142
152
|
async function applyTheme(destDir, choice) {
|
|
143
153
|
if (!choice) return
|
|
144
154
|
const { themeId, themes } = choice
|
|
@@ -149,12 +159,19 @@ async function applyTheme(destDir, choice) {
|
|
|
149
159
|
const lines = Object.entries(tokens).map(([k, v]) => ` ${k}: ${v};`).join('\n')
|
|
150
160
|
return `${selector} {\n${lines}\n color-scheme: ${scheme};\n}`
|
|
151
161
|
}
|
|
152
|
-
const
|
|
162
|
+
const tokenBlocks = `${block(':root', light, 'light')}\n${block(":root[data-theme='dark']", dark, 'dark')}`
|
|
153
163
|
|
|
154
164
|
const cssPath = join(destDir, 'src', 'app.css')
|
|
155
165
|
const cssText = await readFile(cssPath, 'utf8').catch(() => null)
|
|
156
166
|
if (cssText == null) return
|
|
157
|
-
const marked = /\/\* svgrid-theme:start \*\/[\s\S]
|
|
167
|
+
const marked = /\/\* svgrid-theme:start \*\/([\s\S]*?)\/\* svgrid-theme:end \*\//
|
|
168
|
+
// A CSS @import has to stay at the top of the file, so where the block holds
|
|
169
|
+
// one we swap the preset id in place instead of replacing it with token
|
|
170
|
+
// declarations that would then sit above the import.
|
|
171
|
+
const held = cssText.match(marked)
|
|
172
|
+
const css = held && /@import\s/.test(held[1])
|
|
173
|
+
? `@import '@svgrid/grid/themes/${preset.id}.css';`
|
|
174
|
+
: tokenBlocks
|
|
158
175
|
if (marked.test(cssText)) {
|
|
159
176
|
await writeFile(cssPath, cssText.replace(marked, `/* svgrid-theme:start */\n${css}\n/* svgrid-theme:end */`))
|
|
160
177
|
}
|
|
@@ -204,6 +221,27 @@ async function applyMode(destDir, choice) {
|
|
|
204
221
|
}
|
|
205
222
|
}
|
|
206
223
|
|
|
224
|
+
/** minimal picks its start mode in an inline script in `index.html`, falling
|
|
225
|
+
* back to the OS preference. Pin it only when light or dark was actually asked
|
|
226
|
+
* for - following the OS is the better default for anyone who did not say. */
|
|
227
|
+
async function applyModeMinimal(destDir, choice) {
|
|
228
|
+
if (!choice || !choice.explicitMode) return
|
|
229
|
+
|
|
230
|
+
const htmlPath = join(destDir, 'index.html')
|
|
231
|
+
const html = await readFile(htmlPath, 'utf8').catch(() => null)
|
|
232
|
+
if (html == null) return
|
|
233
|
+
|
|
234
|
+
await writeFile(
|
|
235
|
+
htmlPath,
|
|
236
|
+
html
|
|
237
|
+
.replace(`<html lang="en" data-theme="light">`, `<html lang="en" data-theme="${choice.mode}">`)
|
|
238
|
+
.replace(
|
|
239
|
+
`var fallback = matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light'`,
|
|
240
|
+
`var fallback = '${choice.mode}'`,
|
|
241
|
+
),
|
|
242
|
+
)
|
|
243
|
+
}
|
|
244
|
+
|
|
207
245
|
function sanitizeName(name) {
|
|
208
246
|
return (
|
|
209
247
|
name
|
|
@@ -328,9 +366,9 @@ async function main() {
|
|
|
328
366
|
process.exit(1)
|
|
329
367
|
}
|
|
330
368
|
|
|
331
|
-
// 3b. Theme + light/dark mode
|
|
332
|
-
//
|
|
333
|
-
const themeChoice =
|
|
369
|
+
// 3b. Theme + light/dark mode. Both templates carry the full --sg-* palette
|
|
370
|
+
// and a toggle, so both get asked.
|
|
371
|
+
const themeChoice = await promptTheme(args, ask, interactive)
|
|
334
372
|
if (rl) rl.close()
|
|
335
373
|
|
|
336
374
|
// 4. Scaffold.
|
|
@@ -339,12 +377,17 @@ async function main() {
|
|
|
339
377
|
await setProjectName(destDir, projectName)
|
|
340
378
|
await applyTheme(destDir, themeChoice)
|
|
341
379
|
await applyMode(destDir, themeChoice)
|
|
380
|
+
await applyModeMinimal(destDir, themeChoice)
|
|
342
381
|
|
|
343
382
|
// 5. Next steps.
|
|
344
383
|
const rel = isAbsolute(target) || target.startsWith('.') ? target : `./${target}`
|
|
345
384
|
stdout.write(`\n${color('green', '✔')} Scaffolded ${color('bold', projectName)} (${template}) into ${rel}\n`)
|
|
346
385
|
if (themeChoice) {
|
|
347
|
-
|
|
386
|
+
// minimal only pins a mode when one was asked for; otherwise it reads the
|
|
387
|
+
// OS preference at load, so reporting "dark" here would be a lie.
|
|
388
|
+
const pinned = themeChoice.explicitMode || template === 'admin-dashboard'
|
|
389
|
+
const mode = pinned ? themeChoice.mode : 'follows your OS'
|
|
390
|
+
stdout.write(` ${color('dim', 'theme')} ${themeChoice.name} (${mode})\n`)
|
|
348
391
|
}
|
|
349
392
|
stdout.write(`\n`)
|
|
350
393
|
stdout.write(`${color('bold', 'Next steps')}\n`)
|
package/package.json
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
"type": "commercial",
|
|
5
5
|
"url": "https://svgrid.com/pricing"
|
|
6
6
|
},
|
|
7
|
-
"version": "2.
|
|
7
|
+
"version": "2.4.0",
|
|
8
8
|
"description": "Scaffold a Svelte app powered by SvGrid in one command: npm create @svgrid@latest",
|
|
9
9
|
"type": "module",
|
|
10
10
|
"license": "MIT",
|
|
@@ -36,7 +36,7 @@
|
|
|
36
36
|
"node": ">=18"
|
|
37
37
|
},
|
|
38
38
|
"dependencies": {
|
|
39
|
-
"@svgrid/grid": "^2.
|
|
39
|
+
"@svgrid/grid": "^2.4.0"
|
|
40
40
|
},
|
|
41
41
|
"scripts": {
|
|
42
42
|
"sync-templates": "node sync-templates.mjs"
|
|
@@ -1,180 +1,180 @@
|
|
|
1
|
-
@import 'tailwindcss';
|
|
2
|
-
|
|
3
|
-
/* Tailwind's `dark:` modifier follows the `data-theme` attribute the layout
|
|
4
|
-
* writes onto <html>, so utility classes and the grid's own --sg-* tokens
|
|
5
|
-
* switch together. */
|
|
6
|
-
@custom-variant dark (&:where(html[data-theme='dark'], html[data-theme='dark'] *));
|
|
7
|
-
|
|
8
|
-
/* ---------------------------------------------------------------------------
|
|
9
|
-
* SvGrid theme tokens - one of @svgrid/grid's
|
|
10
|
-
* with `npm create @svgrid@latest -- --theme <id> [--dark]`, or edit the
|
|
11
|
-
* values below by hand). The dashboard shell reads them through the --app-*
|
|
12
|
-
* aliases further down, so the whole app themes as one, not just the grid.
|
|
13
|
-
* ------------------------------------------------------------------------- */
|
|
14
|
-
/* svgrid-theme:start */
|
|
15
|
-
:root {
|
|
16
|
-
--sg-bg: #ffffff;
|
|
17
|
-
--sg-fg: #0f172a;
|
|
18
|
-
--sg-muted: #64748b;
|
|
19
|
-
--sg-border: #e2e8f0;
|
|
20
|
-
--sg-header-bg: #f8fafc;
|
|
21
|
-
--sg-header-fg: #0f172a;
|
|
22
|
-
--sg-bg-subtle: #f8fafc;
|
|
23
|
-
--sg-row-alt-bg: #ffffff;
|
|
24
|
-
--sg-row-hover-bg: #f1f5f9;
|
|
25
|
-
--sg-selection-bg: #e0e7ff;
|
|
26
|
-
--sg-input-bg: #ffffff;
|
|
27
|
-
--sg-input-border: #e2e8f0;
|
|
28
|
-
--sg-accent: #4f46e5;
|
|
29
|
-
--sg-on-accent: #ffffff;
|
|
30
|
-
--sg-radius: 6px;
|
|
31
|
-
--sg-font: Inter, ui-sans-serif, system-ui, -apple-system, "Segoe UI", sans-serif;
|
|
32
|
-
--sg-scrollbar-bg: #f8fafc;
|
|
33
|
-
--sg-scrollbar-border: #e2e8f0;
|
|
34
|
-
--sg-scrollbar-thumb: #64748b;
|
|
35
|
-
--sg-scrollbar-thumb-hover: #0f172a;
|
|
36
|
-
--sg-scrollbar-thumb-active: #0f172a;
|
|
37
|
-
--sg-scrollbar-arrow: #64748b;
|
|
38
|
-
--sg-scrollbar-arrow-hover: #0f172a;
|
|
39
|
-
--sg-scrollbar-arrow-hover-bg: #f1f5f9;
|
|
40
|
-
--sg-scrollbar-arrow-active: #0f172a;
|
|
41
|
-
--sg-scrollbar-arrow-active-bg: #e0e7ff;
|
|
42
|
-
--sg-scrollbar-arrow-disabled: #e2e8f0;
|
|
43
|
-
color-scheme: light;
|
|
44
|
-
}
|
|
45
|
-
:root[data-theme='dark'] {
|
|
46
|
-
--sg-bg: #0f172a;
|
|
47
|
-
--sg-fg: #f8fafc;
|
|
48
|
-
--sg-muted: #94a3b8;
|
|
49
|
-
--sg-border: #334155;
|
|
50
|
-
--sg-header-bg: #1e293b;
|
|
51
|
-
--sg-header-fg: #f8fafc;
|
|
52
|
-
--sg-bg-subtle: #1e293b;
|
|
53
|
-
--sg-row-alt-bg: #0f172a;
|
|
54
|
-
--sg-row-hover-bg: #1e293b;
|
|
55
|
-
--sg-selection-bg: #312e81;
|
|
56
|
-
--sg-input-bg: #0f172a;
|
|
57
|
-
--sg-input-border: #334155;
|
|
58
|
-
--sg-accent: #818cf8;
|
|
59
|
-
--sg-on-accent: #18181b;
|
|
60
|
-
--sg-radius: 6px;
|
|
61
|
-
--sg-font: Inter, ui-sans-serif, system-ui, -apple-system, "Segoe UI", sans-serif;
|
|
62
|
-
--sg-scrollbar-bg: #1e293b;
|
|
63
|
-
--sg-scrollbar-border: #334155;
|
|
64
|
-
--sg-scrollbar-thumb: #94a3b8;
|
|
65
|
-
--sg-scrollbar-thumb-hover: #f8fafc;
|
|
66
|
-
--sg-scrollbar-thumb-active: #f8fafc;
|
|
67
|
-
--sg-scrollbar-arrow: #94a3b8;
|
|
68
|
-
--sg-scrollbar-arrow-hover: #f8fafc;
|
|
69
|
-
--sg-scrollbar-arrow-hover-bg: #1e293b;
|
|
70
|
-
--sg-scrollbar-arrow-active: #f8fafc;
|
|
71
|
-
--sg-scrollbar-arrow-active-bg: #312e81;
|
|
72
|
-
--sg-scrollbar-arrow-disabled: #334155;
|
|
73
|
-
color-scheme: dark;
|
|
74
|
-
}
|
|
75
|
-
/* svgrid-theme:end */
|
|
76
|
-
|
|
77
|
-
/* Dashboard shell aliases - map the chrome to the grid tokens above, so any
|
|
78
|
-
* theme (light or dark, whichever preset) reskins the whole page as one. */
|
|
79
|
-
:root {
|
|
80
|
-
--app-bg: var(--sg-row-alt-bg);
|
|
81
|
-
--app-panel: var(--sg-bg);
|
|
82
|
-
--app-border: var(--sg-border);
|
|
83
|
-
--app-fg: var(--sg-fg);
|
|
84
|
-
--app-muted: var(--sg-muted);
|
|
85
|
-
--app-accent: var(--sg-accent);
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
html,
|
|
89
|
-
body {
|
|
90
|
-
background: var(--app-bg);
|
|
91
|
-
color: var(--app-fg);
|
|
92
|
-
font-family: var(--sg-font, Inter, ui-sans-serif, system-ui, -apple-system, Segoe UI, Roboto, Helvetica, Arial,
|
|
93
|
-
sans-serif);
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
/* ---------------------------------------------------------------------------
|
|
97
|
-
* Optional polish. The grid themes itself from the --sg-* tokens above, so it
|
|
98
|
-
* already follows light / dark with no extra CSS. These rules only add table
|
|
99
|
-
* niceties the grid leaves to the app: cell gridlines, zebra striping, row
|
|
100
|
-
* hover, and a styled pagination bar. Delete any you don't want.
|
|
101
|
-
* ------------------------------------------------------------------------- */
|
|
102
|
-
|
|
103
|
-
/* Cell gridlines (the grid draws none by default). */
|
|
104
|
-
.sv-grid-column,
|
|
105
|
-
.sv-grid-cell {
|
|
106
|
-
border-right: 1px solid var(--sg-border) !important;
|
|
107
|
-
border-bottom: 1px solid var(--sg-border) !important;
|
|
108
|
-
}
|
|
109
|
-
.sv-grid-table tr > :last-child.sv-grid-column,
|
|
110
|
-
.sv-grid-table tr > :last-child.sv-grid-cell {
|
|
111
|
-
border-right: 0 !important;
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
/* Zebra striping + row hover. */
|
|
115
|
-
.sv-grid-table tbody tr:nth-child(even) .sv-grid-cell {
|
|
116
|
-
background: var(--sg-row-alt-bg) !important;
|
|
117
|
-
}
|
|
118
|
-
.sv-grid-table tbody tr:hover .sv-grid-cell {
|
|
119
|
-
background: var(--sg-row-hover-bg) !important;
|
|
120
|
-
}
|
|
121
|
-
/* Selection wins over zebra (declared after, same specificity). */
|
|
122
|
-
.sv-grid-table tbody tr.sv-grid-row-selected .sv-grid-cell,
|
|
123
|
-
.sv-grid-table tbody tr .sv-grid-cell[data-selected-range='true'] {
|
|
124
|
-
background: var(--sg-selection-bg) !important;
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
/* Pagination bar: layout + theme (the grid ships the markup, not the styling). */
|
|
128
|
-
.sv-grid-pagination {
|
|
129
|
-
display: flex !important;
|
|
130
|
-
align-items: center !important;
|
|
131
|
-
justify-content: flex-end !important;
|
|
132
|
-
gap: 24px !important;
|
|
133
|
-
padding: 12px 16px !important;
|
|
134
|
-
border: 1px solid var(--sg-border) !important;
|
|
135
|
-
border-top: 0 !important;
|
|
136
|
-
border-radius: 0 0 6px 6px !important;
|
|
137
|
-
background: var(--sg-header-bg) !important;
|
|
138
|
-
color: var(--sg-fg) !important;
|
|
139
|
-
font-size: 13px !important;
|
|
140
|
-
}
|
|
141
|
-
.sv-grid-pagination-pagesize {
|
|
142
|
-
display: inline-flex;
|
|
143
|
-
align-items: center;
|
|
144
|
-
gap: 8px;
|
|
145
|
-
color: var(--sg-muted);
|
|
146
|
-
}
|
|
147
|
-
.sv-grid-pagination-pagesize select,
|
|
148
|
-
.sv-grid-pagination-btn {
|
|
149
|
-
border: 1px solid var(--sg-input-border);
|
|
150
|
-
background: var(--sg-input-bg);
|
|
151
|
-
color: var(--sg-fg);
|
|
152
|
-
border-radius: 5px;
|
|
153
|
-
height: 28px;
|
|
154
|
-
font: inherit;
|
|
155
|
-
font-size: 13px;
|
|
156
|
-
cursor: pointer;
|
|
157
|
-
}
|
|
158
|
-
.sv-grid-pagination-btn {
|
|
159
|
-
display: inline-flex !important;
|
|
160
|
-
align-items: center !important;
|
|
161
|
-
justify-content: center !important;
|
|
162
|
-
width: 28px !important;
|
|
163
|
-
border: 0 !important;
|
|
164
|
-
background: transparent !important;
|
|
165
|
-
font-size: 16px !important;
|
|
166
|
-
}
|
|
167
|
-
.sv-grid-pagination-btn:hover:not(:disabled) {
|
|
168
|
-
background: var(--sg-input-bg) !important;
|
|
169
|
-
color: var(--sg-accent) !important;
|
|
170
|
-
}
|
|
171
|
-
.sv-grid-pagination-btn:disabled {
|
|
172
|
-
color: var(--sg-muted) !important;
|
|
173
|
-
opacity: 0.4 !important;
|
|
174
|
-
cursor: default !important;
|
|
175
|
-
}
|
|
176
|
-
.sv-grid-pagination-nav {
|
|
177
|
-
display: inline-flex;
|
|
178
|
-
align-items: center;
|
|
179
|
-
gap: 4px;
|
|
180
|
-
}
|
|
1
|
+
@import 'tailwindcss';
|
|
2
|
+
|
|
3
|
+
/* Tailwind's `dark:` modifier follows the `data-theme` attribute the layout
|
|
4
|
+
* writes onto <html>, so utility classes and the grid's own --sg-* tokens
|
|
5
|
+
* switch together. */
|
|
6
|
+
@custom-variant dark (&:where(html[data-theme='dark'], html[data-theme='dark'] *));
|
|
7
|
+
|
|
8
|
+
/* ---------------------------------------------------------------------------
|
|
9
|
+
* SvGrid theme tokens - one of @svgrid/grid's 20 built-in presets (pick one
|
|
10
|
+
* with `npm create @svgrid@latest -- --theme <id> [--dark]`, or edit the
|
|
11
|
+
* values below by hand). The dashboard shell reads them through the --app-*
|
|
12
|
+
* aliases further down, so the whole app themes as one, not just the grid.
|
|
13
|
+
* ------------------------------------------------------------------------- */
|
|
14
|
+
/* svgrid-theme:start */
|
|
15
|
+
:root {
|
|
16
|
+
--sg-bg: #ffffff;
|
|
17
|
+
--sg-fg: #0f172a;
|
|
18
|
+
--sg-muted: #64748b;
|
|
19
|
+
--sg-border: #e2e8f0;
|
|
20
|
+
--sg-header-bg: #f8fafc;
|
|
21
|
+
--sg-header-fg: #0f172a;
|
|
22
|
+
--sg-bg-subtle: #f8fafc;
|
|
23
|
+
--sg-row-alt-bg: #ffffff;
|
|
24
|
+
--sg-row-hover-bg: #f1f5f9;
|
|
25
|
+
--sg-selection-bg: #e0e7ff;
|
|
26
|
+
--sg-input-bg: #ffffff;
|
|
27
|
+
--sg-input-border: #e2e8f0;
|
|
28
|
+
--sg-accent: #4f46e5;
|
|
29
|
+
--sg-on-accent: #ffffff;
|
|
30
|
+
--sg-radius: 6px;
|
|
31
|
+
--sg-font: Inter, ui-sans-serif, system-ui, -apple-system, "Segoe UI", sans-serif;
|
|
32
|
+
--sg-scrollbar-bg: #f8fafc;
|
|
33
|
+
--sg-scrollbar-border: #e2e8f0;
|
|
34
|
+
--sg-scrollbar-thumb: #64748b;
|
|
35
|
+
--sg-scrollbar-thumb-hover: #0f172a;
|
|
36
|
+
--sg-scrollbar-thumb-active: #0f172a;
|
|
37
|
+
--sg-scrollbar-arrow: #64748b;
|
|
38
|
+
--sg-scrollbar-arrow-hover: #0f172a;
|
|
39
|
+
--sg-scrollbar-arrow-hover-bg: #f1f5f9;
|
|
40
|
+
--sg-scrollbar-arrow-active: #0f172a;
|
|
41
|
+
--sg-scrollbar-arrow-active-bg: #e0e7ff;
|
|
42
|
+
--sg-scrollbar-arrow-disabled: #e2e8f0;
|
|
43
|
+
color-scheme: light;
|
|
44
|
+
}
|
|
45
|
+
:root[data-theme='dark'] {
|
|
46
|
+
--sg-bg: #0f172a;
|
|
47
|
+
--sg-fg: #f8fafc;
|
|
48
|
+
--sg-muted: #94a3b8;
|
|
49
|
+
--sg-border: #334155;
|
|
50
|
+
--sg-header-bg: #1e293b;
|
|
51
|
+
--sg-header-fg: #f8fafc;
|
|
52
|
+
--sg-bg-subtle: #1e293b;
|
|
53
|
+
--sg-row-alt-bg: #0f172a;
|
|
54
|
+
--sg-row-hover-bg: #1e293b;
|
|
55
|
+
--sg-selection-bg: #312e81;
|
|
56
|
+
--sg-input-bg: #0f172a;
|
|
57
|
+
--sg-input-border: #334155;
|
|
58
|
+
--sg-accent: #818cf8;
|
|
59
|
+
--sg-on-accent: #18181b;
|
|
60
|
+
--sg-radius: 6px;
|
|
61
|
+
--sg-font: Inter, ui-sans-serif, system-ui, -apple-system, "Segoe UI", sans-serif;
|
|
62
|
+
--sg-scrollbar-bg: #1e293b;
|
|
63
|
+
--sg-scrollbar-border: #334155;
|
|
64
|
+
--sg-scrollbar-thumb: #94a3b8;
|
|
65
|
+
--sg-scrollbar-thumb-hover: #f8fafc;
|
|
66
|
+
--sg-scrollbar-thumb-active: #f8fafc;
|
|
67
|
+
--sg-scrollbar-arrow: #94a3b8;
|
|
68
|
+
--sg-scrollbar-arrow-hover: #f8fafc;
|
|
69
|
+
--sg-scrollbar-arrow-hover-bg: #1e293b;
|
|
70
|
+
--sg-scrollbar-arrow-active: #f8fafc;
|
|
71
|
+
--sg-scrollbar-arrow-active-bg: #312e81;
|
|
72
|
+
--sg-scrollbar-arrow-disabled: #334155;
|
|
73
|
+
color-scheme: dark;
|
|
74
|
+
}
|
|
75
|
+
/* svgrid-theme:end */
|
|
76
|
+
|
|
77
|
+
/* Dashboard shell aliases - map the chrome to the grid tokens above, so any
|
|
78
|
+
* theme (light or dark, whichever preset) reskins the whole page as one. */
|
|
79
|
+
:root {
|
|
80
|
+
--app-bg: var(--sg-row-alt-bg);
|
|
81
|
+
--app-panel: var(--sg-bg);
|
|
82
|
+
--app-border: var(--sg-border);
|
|
83
|
+
--app-fg: var(--sg-fg);
|
|
84
|
+
--app-muted: var(--sg-muted);
|
|
85
|
+
--app-accent: var(--sg-accent);
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
html,
|
|
89
|
+
body {
|
|
90
|
+
background: var(--app-bg);
|
|
91
|
+
color: var(--app-fg);
|
|
92
|
+
font-family: var(--sg-font, Inter, ui-sans-serif, system-ui, -apple-system, Segoe UI, Roboto, Helvetica, Arial,
|
|
93
|
+
sans-serif);
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/* ---------------------------------------------------------------------------
|
|
97
|
+
* Optional polish. The grid themes itself from the --sg-* tokens above, so it
|
|
98
|
+
* already follows light / dark with no extra CSS. These rules only add table
|
|
99
|
+
* niceties the grid leaves to the app: cell gridlines, zebra striping, row
|
|
100
|
+
* hover, and a styled pagination bar. Delete any you don't want.
|
|
101
|
+
* ------------------------------------------------------------------------- */
|
|
102
|
+
|
|
103
|
+
/* Cell gridlines (the grid draws none by default). */
|
|
104
|
+
.sv-grid-column,
|
|
105
|
+
.sv-grid-cell {
|
|
106
|
+
border-right: 1px solid var(--sg-border) !important;
|
|
107
|
+
border-bottom: 1px solid var(--sg-border) !important;
|
|
108
|
+
}
|
|
109
|
+
.sv-grid-table tr > :last-child.sv-grid-column,
|
|
110
|
+
.sv-grid-table tr > :last-child.sv-grid-cell {
|
|
111
|
+
border-right: 0 !important;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/* Zebra striping + row hover. */
|
|
115
|
+
.sv-grid-table tbody tr:nth-child(even) .sv-grid-cell {
|
|
116
|
+
background: var(--sg-row-alt-bg) !important;
|
|
117
|
+
}
|
|
118
|
+
.sv-grid-table tbody tr:hover .sv-grid-cell {
|
|
119
|
+
background: var(--sg-row-hover-bg) !important;
|
|
120
|
+
}
|
|
121
|
+
/* Selection wins over zebra (declared after, same specificity). */
|
|
122
|
+
.sv-grid-table tbody tr.sv-grid-row-selected .sv-grid-cell,
|
|
123
|
+
.sv-grid-table tbody tr .sv-grid-cell[data-selected-range='true'] {
|
|
124
|
+
background: var(--sg-selection-bg) !important;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
/* Pagination bar: layout + theme (the grid ships the markup, not the styling). */
|
|
128
|
+
.sv-grid-pagination {
|
|
129
|
+
display: flex !important;
|
|
130
|
+
align-items: center !important;
|
|
131
|
+
justify-content: flex-end !important;
|
|
132
|
+
gap: 24px !important;
|
|
133
|
+
padding: 12px 16px !important;
|
|
134
|
+
border: 1px solid var(--sg-border) !important;
|
|
135
|
+
border-top: 0 !important;
|
|
136
|
+
border-radius: 0 0 6px 6px !important;
|
|
137
|
+
background: var(--sg-header-bg) !important;
|
|
138
|
+
color: var(--sg-fg) !important;
|
|
139
|
+
font-size: 13px !important;
|
|
140
|
+
}
|
|
141
|
+
.sv-grid-pagination-pagesize {
|
|
142
|
+
display: inline-flex;
|
|
143
|
+
align-items: center;
|
|
144
|
+
gap: 8px;
|
|
145
|
+
color: var(--sg-muted);
|
|
146
|
+
}
|
|
147
|
+
.sv-grid-pagination-pagesize select,
|
|
148
|
+
.sv-grid-pagination-btn {
|
|
149
|
+
border: 1px solid var(--sg-input-border);
|
|
150
|
+
background: var(--sg-input-bg);
|
|
151
|
+
color: var(--sg-fg);
|
|
152
|
+
border-radius: 5px;
|
|
153
|
+
height: 28px;
|
|
154
|
+
font: inherit;
|
|
155
|
+
font-size: 13px;
|
|
156
|
+
cursor: pointer;
|
|
157
|
+
}
|
|
158
|
+
.sv-grid-pagination-btn {
|
|
159
|
+
display: inline-flex !important;
|
|
160
|
+
align-items: center !important;
|
|
161
|
+
justify-content: center !important;
|
|
162
|
+
width: 28px !important;
|
|
163
|
+
border: 0 !important;
|
|
164
|
+
background: transparent !important;
|
|
165
|
+
font-size: 16px !important;
|
|
166
|
+
}
|
|
167
|
+
.sv-grid-pagination-btn:hover:not(:disabled) {
|
|
168
|
+
background: var(--sg-input-bg) !important;
|
|
169
|
+
color: var(--sg-accent) !important;
|
|
170
|
+
}
|
|
171
|
+
.sv-grid-pagination-btn:disabled {
|
|
172
|
+
color: var(--sg-muted) !important;
|
|
173
|
+
opacity: 0.4 !important;
|
|
174
|
+
cursor: default !important;
|
|
175
|
+
}
|
|
176
|
+
.sv-grid-pagination-nav {
|
|
177
|
+
display: inline-flex;
|
|
178
|
+
align-items: center;
|
|
179
|
+
gap: 4px;
|
|
180
|
+
}
|
|
@@ -11,6 +11,25 @@ npm run build
|
|
|
11
11
|
|
|
12
12
|
Open `src/App.svelte` and edit the `rows` / `columns` to use your own data.
|
|
13
13
|
|
|
14
|
+
## Theme
|
|
15
|
+
|
|
16
|
+
`src/app.css` imports one of the 20 presets that ship with `@svgrid/grid`.
|
|
17
|
+
Swap the id to re-theme the grid and the page around it together:
|
|
18
|
+
|
|
19
|
+
```css
|
|
20
|
+
@import '@svgrid/grid/themes/nord.css';
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
Pick from `ember`, `shadcn`, `tailwind`, `material`, `fluent`, `carbon`,
|
|
24
|
+
`antd`, `bootstrap`, `atlassian`, `salesforce`, `sap`, `github`, `linear`,
|
|
25
|
+
`notion`, `vercel`, `excel`, `nord`, `dracula`, `catppuccin`, `ag-alpine`.
|
|
26
|
+
|
|
27
|
+
Every preset carries a light and a dark palette. Dark hangs off
|
|
28
|
+
`<html data-theme="dark">`, which the toggle in the header writes and
|
|
29
|
+
`index.html` restores before the first paint. To drop the presets and write
|
|
30
|
+
your own `--sg-*` values instead, see
|
|
31
|
+
[Theme and density](https://svgrid.com/docs/getting-started/5-theme-and-density).
|
|
32
|
+
|
|
14
33
|
## Next steps
|
|
15
34
|
|
|
16
35
|
- Add more features: `rowExpandingFeature`, `columnGroupingFeature`, pagination, pinned rows. See the [docs](https://svgrid.com/docs/).
|
|
@@ -1,9 +1,19 @@
|
|
|
1
1
|
<!doctype html>
|
|
2
|
-
<html lang="en">
|
|
2
|
+
<html lang="en" data-theme="light">
|
|
3
3
|
<head>
|
|
4
4
|
<meta charset="UTF-8" />
|
|
5
5
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
6
6
|
<title>SvGrid app</title>
|
|
7
|
+
<script>
|
|
8
|
+
// Settle the theme before the first paint, otherwise the page renders in
|
|
9
|
+
// one palette and App.svelte's toggle swaps it a frame later. With nothing
|
|
10
|
+
// saved yet we follow the OS.
|
|
11
|
+
try {
|
|
12
|
+
var saved = localStorage.getItem('theme')
|
|
13
|
+
var fallback = matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light'
|
|
14
|
+
document.documentElement.dataset.theme = saved || fallback
|
|
15
|
+
} catch (e) {}
|
|
16
|
+
</script>
|
|
7
17
|
</head>
|
|
8
18
|
<body>
|
|
9
19
|
<div id="app"></div>
|
|
@@ -1,54 +1,143 @@
|
|
|
1
|
-
<script>
|
|
2
|
-
import { SvGrid } from '@svgrid/grid'
|
|
3
|
-
|
|
4
|
-
// Capabilities are boolean props - `sortable`, `filterable`, `editable`,
|
|
5
|
-
// `groupable`, `pageable` - and each injects the feature it needs. For finer
|
|
6
|
-
// control you can register features explicitly with `tableFeatures({ ... })`
|
|
7
|
-
// and pass them as `features`; see docs/getting-started/4-features.md.
|
|
8
|
-
|
|
9
|
-
//
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
},
|
|
28
|
-
{
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
</
|
|
1
|
+
<script>
|
|
2
|
+
import { SvGrid } from '@svgrid/grid'
|
|
3
|
+
|
|
4
|
+
// Capabilities are boolean props - `sortable`, `filterable`, `editable`,
|
|
5
|
+
// `groupable`, `pageable` - and each injects the feature it needs. For finer
|
|
6
|
+
// control you can register features explicitly with `tableFeatures({ ... })`
|
|
7
|
+
// and pass them as `features`; see docs/getting-started/4-features.md.
|
|
8
|
+
|
|
9
|
+
// Light/dark. The preset imported in src/app.css declares both palettes, so
|
|
10
|
+
// writing `data-theme` on <html> is all it takes to swap them. index.html has
|
|
11
|
+
// already restored the saved choice by the time this runs.
|
|
12
|
+
let theme = $state(document.documentElement.dataset.theme === 'dark' ? 'dark' : 'light')
|
|
13
|
+
|
|
14
|
+
$effect(() => {
|
|
15
|
+
document.documentElement.dataset.theme = theme
|
|
16
|
+
try {
|
|
17
|
+
localStorage.setItem('theme', theme)
|
|
18
|
+
} catch (e) {
|
|
19
|
+
// Private mode / storage disabled. The toggle still works for this tab.
|
|
20
|
+
}
|
|
21
|
+
})
|
|
22
|
+
|
|
23
|
+
// Your data. Swap for a fetch() in onMount, a store, or props.
|
|
24
|
+
let rows = $state([
|
|
25
|
+
{ id: 1, name: 'Ada Lovelace', team: 'Engineering', salary: 145000, active: true },
|
|
26
|
+
{ id: 2, name: 'Alan Turing', team: 'Research', salary: 160000, active: true },
|
|
27
|
+
{ id: 3, name: 'Grace Hopper', team: 'Engineering', salary: 152000, active: false },
|
|
28
|
+
{ id: 4, name: 'Katherine Johnson', team: 'Data', salary: 138000, active: true },
|
|
29
|
+
{ id: 5, name: 'Edsger Dijkstra', team: 'Research', salary: 149000, active: false },
|
|
30
|
+
])
|
|
31
|
+
|
|
32
|
+
const columns = [
|
|
33
|
+
{ field: 'name', header: 'Name', editorType: 'text', width: 200 },
|
|
34
|
+
{ field: 'team', header: 'Team', editorType: 'text', width: 150 },
|
|
35
|
+
{
|
|
36
|
+
field: 'salary',
|
|
37
|
+
header: 'Salary',
|
|
38
|
+
width: 130,
|
|
39
|
+
align: 'right',
|
|
40
|
+
format: { type: 'currency', currency: 'USD' },
|
|
41
|
+
},
|
|
42
|
+
{ field: 'active', header: 'Active', editorType: 'checkbox', width: 90 },
|
|
43
|
+
]
|
|
44
|
+
</script>
|
|
45
|
+
|
|
46
|
+
<main>
|
|
47
|
+
<header>
|
|
48
|
+
<div>
|
|
49
|
+
<h1>SvGrid</h1>
|
|
50
|
+
<p>
|
|
51
|
+
Sort, filter, select, and double-click a cell to edit. Edit
|
|
52
|
+
<code>src/App.svelte</code> to make it yours.
|
|
53
|
+
</p>
|
|
54
|
+
</div>
|
|
55
|
+
<button
|
|
56
|
+
type="button"
|
|
57
|
+
onclick={() => (theme = theme === 'dark' ? 'light' : 'dark')}
|
|
58
|
+
aria-label="Switch to {theme === 'dark' ? 'light' : 'dark'} mode"
|
|
59
|
+
>
|
|
60
|
+
{theme === 'dark' ? 'Light' : 'Dark'}
|
|
61
|
+
</button>
|
|
62
|
+
</header>
|
|
63
|
+
|
|
64
|
+
<div class="grid-shell">
|
|
65
|
+
<SvGrid
|
|
66
|
+
data={rows}
|
|
67
|
+
{columns}
|
|
68
|
+
sortable
|
|
69
|
+
filterable
|
|
70
|
+
editable
|
|
71
|
+
selectionMode="row"
|
|
72
|
+
showRowSelection={true}
|
|
73
|
+
showRowNumbers={true}
|
|
74
|
+
rowHeight={38}
|
|
75
|
+
containerHeight="100%"
|
|
76
|
+
fitColumns={true}
|
|
77
|
+
/>
|
|
78
|
+
</div>
|
|
79
|
+
|
|
80
|
+
<p class="hint">
|
|
81
|
+
Theme: <code>src/app.css</code> imports one of 20 presets. Swap the id for
|
|
82
|
+
<code>material</code>, <code>nord</code>, <code>dracula</code>, and so on.
|
|
83
|
+
</p>
|
|
84
|
+
</main>
|
|
85
|
+
|
|
86
|
+
<style>
|
|
87
|
+
/* Chrome reads the same --sg-* tokens as the grid, so it re-themes with it. */
|
|
88
|
+
main {
|
|
89
|
+
max-width: 720px;
|
|
90
|
+
margin: 3rem auto;
|
|
91
|
+
padding: 0 1rem;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
header {
|
|
95
|
+
display: flex;
|
|
96
|
+
align-items: flex-start;
|
|
97
|
+
justify-content: space-between;
|
|
98
|
+
gap: 1rem;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
h1 {
|
|
102
|
+
margin: 0;
|
|
103
|
+
font-size: 1.4rem;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
p {
|
|
107
|
+
color: var(--sg-muted);
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
code {
|
|
111
|
+
font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
|
|
112
|
+
font-size: 0.9em;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
button {
|
|
116
|
+
flex: none;
|
|
117
|
+
padding: 0.4rem 0.8rem;
|
|
118
|
+
border: 1px solid var(--sg-border);
|
|
119
|
+
border-radius: var(--sg-radius, 6px);
|
|
120
|
+
background: var(--sg-bg-subtle, transparent);
|
|
121
|
+
color: var(--sg-fg);
|
|
122
|
+
font: inherit;
|
|
123
|
+
font-size: 0.85rem;
|
|
124
|
+
cursor: pointer;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
button:hover {
|
|
128
|
+
background: var(--sg-row-hover-bg);
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
button:focus-visible {
|
|
132
|
+
outline: 2px solid var(--sg-accent);
|
|
133
|
+
outline-offset: 2px;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
.grid-shell {
|
|
137
|
+
height: 320px;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
.hint {
|
|
141
|
+
font-size: 0.85rem;
|
|
142
|
+
}
|
|
143
|
+
</style>
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
/* ---------------------------------------------------------------------------
|
|
2
|
+
* Theme.
|
|
3
|
+
*
|
|
4
|
+
* @svgrid/grid ships 20 presets, each one a full light AND dark palette on the
|
|
5
|
+
* --sg-* custom properties: ember, shadcn, tailwind, material, fluent, carbon,
|
|
6
|
+
* antd, bootstrap, atlassian, salesforce, sap, github, linear, notion, vercel,
|
|
7
|
+
* excel, nord, dracula, catppuccin, ag-alpine.
|
|
8
|
+
*
|
|
9
|
+
* Swap the id below to re-theme the grid and this page together, or delete the
|
|
10
|
+
* import and declare the tokens yourself. The dark half of every preset hangs
|
|
11
|
+
* off <html data-theme="dark">, which the toggle in App.svelte writes.
|
|
12
|
+
*
|
|
13
|
+
* Token reference: https://svgrid.com/docs/getting-started/5-theme-and-density
|
|
14
|
+
* ------------------------------------------------------------------------- */
|
|
15
|
+
/* svgrid-theme:start */
|
|
16
|
+
@import '@svgrid/grid/themes/shadcn.css';
|
|
17
|
+
/* svgrid-theme:end */
|
|
18
|
+
|
|
19
|
+
/* Which way the page leans, so scrollbars, form controls and the canvas behind
|
|
20
|
+
the app follow the theme instead of staying stubbornly light. */
|
|
21
|
+
:root {
|
|
22
|
+
color-scheme: light;
|
|
23
|
+
}
|
|
24
|
+
:root[data-theme='dark'] {
|
|
25
|
+
color-scheme: dark;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/* The page chrome reads the same tokens the grid does, so changing the preset
|
|
29
|
+
above moves the whole app, not just the table. */
|
|
30
|
+
body {
|
|
31
|
+
margin: 0;
|
|
32
|
+
min-height: 100vh;
|
|
33
|
+
background: var(--sg-bg);
|
|
34
|
+
color: var(--sg-fg);
|
|
35
|
+
font-family: var(--sg-font, system-ui, sans-serif);
|
|
36
|
+
}
|