gbs-add-block 1.2.11 → 1.2.13

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 (34) hide show
  1. package/README.md +4 -2
  2. package/index.cjs +7 -1
  3. package/package.json +1 -1
  4. package/source/beta-components/fileuploader/README.md +99 -0
  5. package/source/beta-components/fileuploader/__tests__/core.test.ts +395 -0
  6. package/source/beta-components/fileuploader/core/format.ts +79 -0
  7. package/source/beta-components/fileuploader/core/index.ts +26 -0
  8. package/source/beta-components/fileuploader/core/store.ts +432 -0
  9. package/source/beta-components/fileuploader/core/transport.ts +145 -0
  10. package/source/beta-components/fileuploader/core/types.ts +126 -0
  11. package/source/beta-components/fileuploader/core/validate.ts +83 -0
  12. package/source/beta-components/fileuploader/index.ts +7 -0
  13. package/source/beta-components/fileuploader/react/FileRow.tsx +268 -0
  14. package/source/beta-components/fileuploader/react/FileUploader.tsx +515 -0
  15. package/source/beta-components/fileuploader/react/icons.tsx +80 -0
  16. package/source/beta-components/fileuploader/react/locale.ts +33 -0
  17. package/source/beta-components/fileuploader/react/props.ts +31 -0
  18. package/source/beta-components/fileuploader/react/useFileUploader.ts +32 -0
  19. package/source/beta-components/fileuploader/styles.css +395 -0
  20. package/source/beta-components/toaster/README.md +128 -0
  21. package/source/beta-components/toaster/__tests__/core.test.ts +256 -0
  22. package/source/beta-components/toaster/core/api.ts +87 -0
  23. package/source/beta-components/toaster/core/index.ts +14 -0
  24. package/source/beta-components/toaster/core/store.ts +211 -0
  25. package/source/beta-components/toaster/core/toast.ts +12 -0
  26. package/source/beta-components/toaster/core/types.ts +78 -0
  27. package/source/beta-components/toaster/index.ts +6 -0
  28. package/source/beta-components/toaster/react/ToastItem.tsx +164 -0
  29. package/source/beta-components/toaster/react/Toaster.tsx +187 -0
  30. package/source/beta-components/toaster/react/icons.tsx +56 -0
  31. package/source/beta-components/toaster/react/locale.ts +6 -0
  32. package/source/beta-components/toaster/react/props.ts +15 -0
  33. package/source/beta-components/toaster/react/useToasts.ts +11 -0
  34. package/source/beta-components/toaster/styles.css +283 -0
@@ -0,0 +1,395 @@
1
+ /*
2
+ * FileUploader styles.
3
+ *
4
+ * Tokens read the DataGrid's --dg-* variables when they are set on an ancestor
5
+ * (set them on :root to share one theme) and fall back to the same palette
6
+ * otherwise. Rules live in the `components` layer, so utility classes passed
7
+ * through `className` / `classNames` override them.
8
+ */
9
+ @layer theme, base, components, utilities;
10
+
11
+ @layer components {
12
+ .fu-root {
13
+ --fu-bg: var(--dg-bg, light-dark(#ffffff, #0b0b0e));
14
+ --fu-fg: var(--dg-fg, light-dark(#18181b, #f4f4f5));
15
+ --fu-muted: var(--dg-muted, light-dark(#71717a, #a1a1aa));
16
+ --fu-border: var(--dg-border, light-dark(#e4e4e7, #27272a));
17
+ --fu-hover: var(--dg-hover, light-dark(#f4f4f5, #1f1f23));
18
+ --fu-input-bg: var(--dg-input-bg, light-dark(#ffffff, #121216));
19
+ --fu-accent: var(--dg-accent, light-dark(#2563eb, #60a5fa));
20
+ --fu-accent-fg: var(--dg-accent-fg, light-dark(#ffffff, #0b1220));
21
+ --fu-accent-soft: var(--dg-accent-soft, light-dark(#eff6ff, #172554));
22
+ --fu-focus: var(--dg-focus, light-dark(#2563eb, #60a5fa));
23
+ --fu-danger: var(--dg-danger, light-dark(#dc2626, #f87171));
24
+ --fu-success: light-dark(#16a34a, #4ade80);
25
+ --fu-radius: var(--dg-radius, 8px);
26
+ --fu-font-size: var(--dg-font-size, 13px);
27
+ --fu-dropzone-py: 20px;
28
+ --fu-thumb-size: 40px;
29
+
30
+ color-scheme: inherit;
31
+ display: flex;
32
+ flex-direction: column;
33
+ gap: 8px;
34
+ width: 100%;
35
+ min-width: 0;
36
+ font-size: var(--fu-font-size);
37
+ line-height: 1.4;
38
+ color: var(--fu-fg);
39
+ }
40
+
41
+ :where(.dark, [data-theme="dark"]) .fu-root {
42
+ color-scheme: dark;
43
+ }
44
+ :where(.light, [data-theme="light"]) .fu-root {
45
+ color-scheme: light;
46
+ }
47
+
48
+ :where(.fu-root) *,
49
+ :where(.fu-root) *::before,
50
+ :where(.fu-root) *::after {
51
+ box-sizing: border-box;
52
+ }
53
+
54
+ .fu-root[data-size="sm"] {
55
+ --fu-font-size: 12px;
56
+ --fu-dropzone-py: 12px;
57
+ --fu-thumb-size: 32px;
58
+ }
59
+ .fu-root[data-size="lg"] {
60
+ --fu-font-size: 14px;
61
+ --fu-dropzone-py: 32px;
62
+ --fu-thumb-size: 48px;
63
+ }
64
+
65
+ .fu-label {
66
+ font-weight: 500;
67
+ }
68
+ .fu-label[data-required]::after {
69
+ content: " *";
70
+ color: var(--fu-danger);
71
+ }
72
+ .fu-description {
73
+ color: var(--fu-muted);
74
+ font-size: 0.92em;
75
+ }
76
+ .fu-error {
77
+ color: var(--fu-danger);
78
+ font-size: 0.92em;
79
+ }
80
+
81
+ /* ------------------------------------------------------------ dropzone */
82
+
83
+ .fu-dropzone {
84
+ position: relative;
85
+ border: 1.5px dashed color-mix(in oklab, var(--fu-border), var(--fu-fg) 20%);
86
+ border-radius: var(--fu-radius);
87
+ background: var(--fu-input-bg);
88
+ transition:
89
+ border-color 120ms ease,
90
+ background-color 120ms ease;
91
+ }
92
+ .fu-dropzone:hover:not(:has(.fu-trigger:disabled)) {
93
+ border-color: color-mix(in oklab, var(--fu-border), var(--fu-fg) 40%);
94
+ }
95
+ .fu-dropzone[data-dragging] {
96
+ border-color: var(--fu-accent);
97
+ background: var(--fu-accent-soft);
98
+ }
99
+ .fu-root[data-invalid] .fu-dropzone {
100
+ border-color: var(--fu-danger);
101
+ }
102
+
103
+ .fu-trigger {
104
+ display: flex;
105
+ flex-direction: column;
106
+ align-items: center;
107
+ gap: 4px;
108
+ width: 100%;
109
+ padding: var(--fu-dropzone-py) 16px;
110
+ border: 0;
111
+ border-radius: inherit;
112
+ background: transparent;
113
+ color: inherit;
114
+ font: inherit;
115
+ text-align: center;
116
+ cursor: pointer;
117
+ }
118
+ .fu-trigger:focus-visible {
119
+ outline: 2px solid var(--fu-focus);
120
+ outline-offset: 2px;
121
+ }
122
+ .fu-trigger:disabled {
123
+ opacity: 0.55;
124
+ cursor: not-allowed;
125
+ }
126
+ .fu-dropzone-icon {
127
+ color: var(--fu-muted);
128
+ }
129
+ .fu-dropzone[data-dragging] .fu-dropzone-icon {
130
+ color: var(--fu-accent);
131
+ }
132
+ .fu-browse {
133
+ color: var(--fu-accent);
134
+ font-weight: 500;
135
+ text-decoration: underline;
136
+ text-underline-offset: 2px;
137
+ }
138
+ .fu-hint {
139
+ color: var(--fu-muted);
140
+ font-size: 0.92em;
141
+ }
142
+
143
+ /* ---------------------------------------------------------- rejections */
144
+
145
+ .fu-rejections {
146
+ display: flex;
147
+ flex-direction: column;
148
+ gap: 4px;
149
+ margin: 0;
150
+ padding: 0;
151
+ list-style: none;
152
+ }
153
+ .fu-rejection {
154
+ display: flex;
155
+ align-items: center;
156
+ gap: 8px;
157
+ padding: 4px 4px 4px 10px;
158
+ border-radius: 6px;
159
+ background: color-mix(in oklab, var(--fu-danger), transparent 90%);
160
+ color: var(--fu-danger);
161
+ }
162
+ .fu-rejection > span {
163
+ flex: 1 1 auto;
164
+ min-width: 0;
165
+ }
166
+
167
+ /* --------------------------------------------------------------- files */
168
+
169
+ .fu-list {
170
+ display: flex;
171
+ flex-direction: column;
172
+ gap: 6px;
173
+ margin: 0;
174
+ padding: 0;
175
+ list-style: none;
176
+ }
177
+ .fu-item {
178
+ display: flex;
179
+ align-items: center;
180
+ gap: 10px;
181
+ padding: 8px 8px 8px 10px;
182
+ border: 1px solid var(--fu-border);
183
+ border-radius: var(--fu-radius);
184
+ background: var(--fu-bg);
185
+ }
186
+ .fu-item[data-status="error"] {
187
+ border-color: color-mix(in oklab, var(--fu-danger), var(--fu-border) 55%);
188
+ }
189
+
190
+ .fu-thumb {
191
+ --fu-kind: var(--fu-muted);
192
+
193
+ position: relative;
194
+ display: grid;
195
+ flex: none;
196
+ place-items: center;
197
+ width: var(--fu-thumb-size);
198
+ height: var(--fu-thumb-size);
199
+ overflow: hidden;
200
+ border-radius: 6px;
201
+ background: color-mix(in oklab, var(--fu-kind), transparent 88%);
202
+ color: var(--fu-kind);
203
+ }
204
+ .fu-thumb img {
205
+ position: absolute;
206
+ inset: 0;
207
+ width: 100%;
208
+ height: 100%;
209
+ object-fit: cover;
210
+ }
211
+ .fu-thumb[data-kind="image"] {
212
+ --fu-kind: light-dark(#2563eb, #60a5fa);
213
+ }
214
+ .fu-thumb[data-kind="video"] {
215
+ --fu-kind: light-dark(#9333ea, #c084fc);
216
+ }
217
+ .fu-thumb[data-kind="audio"] {
218
+ --fu-kind: light-dark(#d97706, #fbbf24);
219
+ }
220
+ .fu-thumb[data-kind="pdf"] {
221
+ --fu-kind: light-dark(#dc2626, #f87171);
222
+ }
223
+ .fu-thumb[data-kind="spreadsheet"] {
224
+ --fu-kind: light-dark(#16a34a, #4ade80);
225
+ }
226
+ .fu-thumb[data-kind="document"] {
227
+ --fu-kind: light-dark(#0891b2, #22d3ee);
228
+ }
229
+
230
+ .fu-body {
231
+ display: flex;
232
+ flex: 1 1 auto;
233
+ flex-direction: column;
234
+ gap: 1px;
235
+ min-width: 0;
236
+ }
237
+ .fu-name,
238
+ .fu-meta,
239
+ .fu-item-error {
240
+ overflow: hidden;
241
+ text-overflow: ellipsis;
242
+ white-space: nowrap;
243
+ }
244
+ .fu-name {
245
+ font-weight: 500;
246
+ }
247
+ .fu-meta {
248
+ color: var(--fu-muted);
249
+ font-size: 0.92em;
250
+ font-variant-numeric: tabular-nums;
251
+ }
252
+ .fu-item[data-status="success"] .fu-meta {
253
+ color: var(--fu-success);
254
+ }
255
+ .fu-item[data-status="error"] .fu-meta,
256
+ .fu-item-error {
257
+ color: var(--fu-danger);
258
+ font-size: 0.92em;
259
+ }
260
+
261
+ .fu-progress {
262
+ height: 4px;
263
+ margin-top: 4px;
264
+ overflow: hidden;
265
+ border-radius: 999px;
266
+ background: var(--fu-hover);
267
+ }
268
+ /* Scaling instead of width: moving the bar never triggers layout. */
269
+ .fu-progress-bar {
270
+ display: block;
271
+ height: 100%;
272
+ background: var(--fu-accent);
273
+ transform-origin: left;
274
+ transition: transform 150ms linear;
275
+ }
276
+ .fu-progress-bar:dir(rtl) {
277
+ transform-origin: right;
278
+ }
279
+ .fu-item[data-status="paused"] .fu-progress-bar {
280
+ background: var(--fu-muted);
281
+ }
282
+ .fu-item[data-status="error"] .fu-progress-bar {
283
+ background: var(--fu-danger);
284
+ }
285
+
286
+ .fu-actions {
287
+ display: flex;
288
+ flex: none;
289
+ align-items: center;
290
+ gap: 2px;
291
+ color: var(--fu-muted);
292
+ }
293
+ .fu-icon-button {
294
+ display: inline-grid;
295
+ place-items: center;
296
+ width: 28px;
297
+ height: 28px;
298
+ padding: 0;
299
+ border: 0;
300
+ border-radius: 6px;
301
+ background: transparent;
302
+ color: inherit;
303
+ cursor: pointer;
304
+ }
305
+ .fu-icon-button:hover:not(:disabled) {
306
+ background: var(--fu-hover);
307
+ color: var(--fu-fg);
308
+ }
309
+ .fu-icon-button:focus-visible {
310
+ outline: 2px solid var(--fu-focus);
311
+ outline-offset: 1px;
312
+ }
313
+ .fu-icon-button:disabled {
314
+ opacity: 0.4;
315
+ cursor: default;
316
+ }
317
+
318
+ /* -------------------------------------------------------------- footer */
319
+
320
+ .fu-footer {
321
+ display: flex;
322
+ align-items: center;
323
+ justify-content: flex-end;
324
+ gap: 8px;
325
+ }
326
+ .fu-footer .fu-progress {
327
+ flex: 1 1 auto;
328
+ margin: 0;
329
+ }
330
+ .fu-footer-text {
331
+ color: var(--fu-muted);
332
+ font-size: 0.92em;
333
+ font-variant-numeric: tabular-nums;
334
+ white-space: nowrap;
335
+ }
336
+ .fu-button {
337
+ height: 32px;
338
+ padding: 0 12px;
339
+ border: 1px solid var(--fu-border);
340
+ border-radius: 6px;
341
+ background: var(--fu-bg);
342
+ color: inherit;
343
+ font: inherit;
344
+ font-weight: 500;
345
+ white-space: nowrap;
346
+ cursor: pointer;
347
+ }
348
+ .fu-button:hover:not(:disabled) {
349
+ background: var(--fu-hover);
350
+ }
351
+ .fu-button[data-variant="primary"] {
352
+ border-color: var(--fu-accent);
353
+ background: var(--fu-accent);
354
+ color: var(--fu-accent-fg);
355
+ }
356
+ .fu-button[data-variant="primary"]:hover:not(:disabled) {
357
+ background: color-mix(in oklab, var(--fu-accent), var(--fu-fg) 12%);
358
+ }
359
+ .fu-button:focus-visible {
360
+ outline: 2px solid var(--fu-focus);
361
+ outline-offset: 1px;
362
+ }
363
+ .fu-button:disabled {
364
+ opacity: 0.55;
365
+ cursor: not-allowed;
366
+ }
367
+
368
+ .fu-sr-only {
369
+ position: absolute;
370
+ width: 1px;
371
+ height: 1px;
372
+ margin: -1px;
373
+ padding: 0;
374
+ overflow: hidden;
375
+ clip-path: inset(50%);
376
+ white-space: nowrap;
377
+ border: 0;
378
+ }
379
+
380
+ @media (prefers-reduced-motion: reduce) {
381
+ .fu-dropzone,
382
+ .fu-progress-bar {
383
+ transition: none;
384
+ }
385
+ }
386
+
387
+ @media (forced-colors: active) {
388
+ .fu-progress-bar {
389
+ background: Highlight;
390
+ }
391
+ .fu-dropzone[data-dragging] {
392
+ border-color: Highlight;
393
+ }
394
+ }
395
+ }
@@ -0,0 +1,128 @@
1
+ # Toaster
2
+
3
+ Notifications for any React 19 app — Vite, Next.js, Remix or plain React — styled
4
+ to match the DataGrid, Combobox and DatePicker. No runtime dependencies besides
5
+ React.
6
+
7
+ - **`<Toaster />`** — mount once; renders the toasts.
8
+ - **`toast()`** — call from anywhere: components, event handlers, data layers,
9
+ even code outside React. No hook, context or provider.
10
+
11
+ ## Setup
12
+
13
+ ```ts
14
+ import { toast, Toaster } from "@/components/toaster";
15
+ import "@/components/toaster/styles.css";
16
+ ```
17
+
18
+ ```tsx
19
+ // Once, near the root of the app (e.g. app/layout.tsx or App.tsx)
20
+ <Toaster />
21
+
22
+ // Anywhere
23
+ toast.success("Settings saved");
24
+ ```
25
+
26
+ Import from one path everywhere. `toast()` and `<Toaster />` meet through a
27
+ shared store, and a bundler that sees two import paths may create two stores.
28
+
29
+ ## Showing toasts
30
+
31
+ ```tsx
32
+ toast("Event created", { description: "Monday, 10:00" });
33
+ toast.success("Saved");
34
+ toast.error("Upload failed", { action: { label: "Retry", onClick: retry } });
35
+ toast.warning("Storage almost full");
36
+ toast.info("New version available");
37
+ const id = toast.loading("Uploading…"); // stays until updated
38
+
39
+ toast.update(id, { type: "success", title: "Uploaded" });
40
+ toast.dismiss(id); // or toast.dismiss() for all
41
+
42
+ toast.promise(saveUser(data), {
43
+ loading: "Saving…",
44
+ success: (user) => `${user.name} saved`,
45
+ error: (error) => `Could not save: ${(error as Error).message}`,
46
+ });
47
+
48
+ toast.custom(({ dismiss }) => <MyCard onClose={dismiss} />);
49
+ ```
50
+
51
+ Every call returns the toast's id. Passing an `id` that is already on screen
52
+ updates that toast instead of adding another.
53
+
54
+ ## Toast options
55
+
56
+ | Option | Type | Default | Description |
57
+ | --- | --- | --- | --- |
58
+ | `id` | `string` | generated | Reuse to update a toast in place. |
59
+ | `type` | `"default"` \| `"success"` \| `"error"` \| `"warning"` \| `"info"` \| `"loading"` | `"default"` | Icon, color bar and announcement. |
60
+ | `description` | `ReactNode` | — | Second line. |
61
+ | `duration` | `number` | `5000` (loading: stays) | Milliseconds. `0` or `Infinity` stays until dismissed. |
62
+ | `dismissible` | `boolean` | `true` | Close button, Escape and swipe. |
63
+ | `action` / `cancel` | `{ label, onClick(event) }` | — | Buttons. The toast closes after a click unless `event.preventDefault()` is called. |
64
+ | `icon` | `ReactNode` | the type's icon | `null` hides it. |
65
+ | `className` | `string` | — | Class for this toast. |
66
+ | `render` | `({ id, dismiss }) => ReactNode` | — | Custom body (`toast.custom` sets it). |
67
+ | `onDismiss` / `onAutoClose` | `(toast) => void` | — | Closed by someone, or because time ran out. |
68
+
69
+ ## Toaster props
70
+
71
+ | Prop | Type | Default | Description |
72
+ | --- | --- | --- | --- |
73
+ | `position` | `"top-left"` \| `"top-center"` \| `"top-right"` \| `"bottom-left"` \| `"bottom-center"` \| `"bottom-right"` | `"top-right"` | Where the stack sits. Full width on narrow screens. |
74
+ | `limit` | `number` | `3` | Toasts on screen at once. The rest wait, with their timers held. |
75
+ | `duration` | `number` | `5000` | Default auto-close delay. |
76
+ | `closeButton` | `boolean` | `true` | Close button on dismissible toasts. |
77
+ | `hotkey` | `string[]` | `["altKey", "KeyT"]` | Moves focus to the notifications. `[]` turns it off. |
78
+ | `icons` | `Partial<Record<ToastType, ReactNode>>` | — | Replace icons per type; `null` hides one. |
79
+ | `store` | `ToastStore` | the `toast()` store | For a separate notification area. |
80
+ | `dir` | `"ltr"` \| `"rtl"` \| `"auto"` | inherited | Text direction. |
81
+ | `className`, `classNames`, `style` | — | — | Slots: `region`, `list`, `toast`, `icon`, `content`, `title`, `description`, `actions`, `action`, `cancel`, `close`. |
82
+ | `localeText` | `Partial<ToasterLocaleText>` | English | `regionLabel(hotkey)`, `close`. |
83
+
84
+ ## Behavior
85
+
86
+ - Timers pause while the pointer is over the toasts, while focus is inside them,
87
+ and while the browser tab is hidden.
88
+ - Toasts beyond `limit` wait with their full time and appear as others close.
89
+ - Toasts render in a portal on `<body>`, so no parent's overflow, transform or
90
+ z-index can hide them.
91
+
92
+ ## Keyboard and accessibility
93
+
94
+ | Keys | Action |
95
+ | --- | --- |
96
+ | **Alt + T** | Move focus to the notifications (configurable with `hotkey`). |
97
+ | **Tab** | Move between toasts and their buttons. |
98
+ | **Escape** | Dismiss the focused toast. |
99
+ | swipe sideways | Dismiss (touch, pen or mouse). |
100
+
101
+ The list is a polite live region, always in the page, so new toasts are read out
102
+ without interrupting. Error toasts use `role="alert"` and interrupt. The region is
103
+ a labelled landmark ("Notifications (Alt+T)").
104
+
105
+ ## Theming
106
+
107
+ Override `--ts-*` variables (they default to the grid's `--dg-*`). Toasts render
108
+ on `<body>`, so set shared `--dg-*` values on `:root`:
109
+
110
+ ```css
111
+ .ts-region { --ts-width: 420px; --ts-success: #059669; --ts-radius: 12px; }
112
+ ```
113
+
114
+ State attributes: `data-position` on the region; `data-type`, `data-state`
115
+ (`open` / `closing`), `data-custom`, `data-swiping` on toasts.
116
+
117
+ ## Headless use
118
+
119
+ `useToasts(store?)` returns the live snapshot (`toasts`, `paused`, `limit`) for a
120
+ custom UI, and `visibleToasts(snapshot)` picks the ones to show.
121
+ `createToastStore()` and `createToastApi(store)` build separate instances. All of
122
+ `core` runs without React.
123
+
124
+ ## Known limits
125
+
126
+ - One stack per `<Toaster />`; toasts don't collapse into an expandable pile.
127
+ - `toast()` does nothing during a server render or in server code. Call it in
128
+ the browser, e.g. after a Server Action resolves.