@godxjp/ui 2.2.0 → 5.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 (47) hide show
  1. package/BRAND.md +39 -29
  2. package/CHANGELOG.md +566 -10
  3. package/README.md +143 -168
  4. package/config/eslint.js +54 -0
  5. package/config/prettier.cjs +20 -0
  6. package/config/tsconfig.base.json +22 -0
  7. package/config/vitest.base.ts +26 -0
  8. package/dist/MiniMonth-YAmPGEpC.d.ts +143 -0
  9. package/dist/Table.types-BbsxoIYE.d.ts +352 -0
  10. package/dist/color-DO0qqUAb.d.ts +38 -0
  11. package/dist/components/composites.d.ts +963 -0
  12. package/dist/components/composites.js +7340 -0
  13. package/dist/components/composites.js.map +1 -0
  14. package/dist/components/primitives.d.ts +2633 -163
  15. package/dist/components/primitives.js +7266 -165
  16. package/dist/components/primitives.js.map +1 -1
  17. package/dist/components/shell.d.ts +82 -12
  18. package/dist/components/shell.js +168 -162
  19. package/dist/components/shell.js.map +1 -1
  20. package/dist/hooks.d.ts +83 -8
  21. package/dist/hooks.js +497 -83
  22. package/dist/hooks.js.map +1 -1
  23. package/dist/i18n.d.ts +55 -3
  24. package/dist/i18n.js +456 -5
  25. package/dist/i18n.js.map +1 -1
  26. package/dist/index.d.ts +24 -5
  27. package/dist/index.js +12524 -267
  28. package/dist/index.js.map +1 -1
  29. package/dist/padding-DY0JV5Ja.d.ts +16 -0
  30. package/dist/preferences.d.ts +132 -0
  31. package/dist/preferences.js +262 -0
  32. package/dist/preferences.js.map +1 -0
  33. package/dist/props.d.ts +86 -0
  34. package/dist/props.js +16 -0
  35. package/dist/props.js.map +1 -0
  36. package/dist/size-CQwNvOWd.d.ts +19 -0
  37. package/dist/{data.d.ts → types-LTj-2bl-.d.ts} +7 -12
  38. package/dist/useTableViews-D5NIAJ7h.d.ts +154 -0
  39. package/package.json +92 -35
  40. package/src/tokens/tailwind.css +158 -0
  41. package/dist/components/screens.d.ts +0 -51
  42. package/dist/components/screens.js +0 -806
  43. package/dist/components/screens.js.map +0 -1
  44. package/dist/data.js +0 -93
  45. package/dist/data.js.map +0 -1
  46. package/src/tokens/tokens-ext.css +0 -401
  47. package/src/tokens/tokens.css +0 -765
package/dist/hooks.js CHANGED
@@ -1,91 +1,152 @@
1
- import { useState, useEffect, useCallback } from 'react';
1
+ import { useState, useEffect, useCallback, useSyncExternalStore, useMemo } from 'react';
2
2
  import i18next from 'i18next';
3
3
  import 'i18next-browser-languagedetector';
4
4
  import 'react-i18next';
5
5
 
6
- var FORGE_LOCALE_STORAGE_KEY = "forge.locale";
7
- var i18n_default = i18next;
6
+ // src/hooks/useTweaks.ts
7
+
8
+ // src/preferences/holder.ts
9
+ var DEFAULT = {
10
+ locale: "ja",
11
+ timezone: "Asia/Tokyo"
12
+ };
13
+ var CURRENT = { ...DEFAULT };
14
+ var subscribers = /* @__PURE__ */ new Set();
15
+ function getGodxConfig() {
16
+ return CURRENT;
17
+ }
18
+ function subscribeGodxConfig(fn) {
19
+ subscribers.add(fn);
20
+ return () => {
21
+ subscribers.delete(fn);
22
+ };
23
+ }
8
24
 
9
- // src/data/products.ts
10
- var PRODUCTS = [
11
- {
12
- id: "restaurant",
13
- name: "godx-restaurant",
14
- tenant: "restaurant",
15
- role: "\u30EC\u30B9\u30C8\u30E9\u30F3\u7BA1\u7406",
16
- desc: "\u5E97\u8217\u5411\u3051\u7D71\u5408\u7BA1\u7406\u30D7\u30E9\u30C3\u30C8\u30D5\u30A9\u30FC\u30E0",
17
- color: "oklch(58% 0.18 25)",
18
- owner: "Satoshi F",
19
- devs: 6,
20
- projects: [
21
- { id: "api", name: "restaurant-api", stack: "NestJS \xB7 PostgreSQL", kind: "service", devs: 3, status: "active", branch: "main", lastCommit: "12\u5206\u524D", openIssues: 8, prs: 2, sandbox: true },
22
- { id: "admin", name: "restaurant-admin", stack: "Next.js 14 \xB7 React", kind: "web", devs: 2, status: "active", branch: "main", lastCommit: "1\u6642\u9593\u524D", openIssues: 5, prs: 1, sandbox: true },
23
- { id: "pos", name: "restaurant-pos", stack: "Tauri \xB7 Vue 3", kind: "desktop", devs: 1, status: "active", branch: "feature/print", lastCommit: "30\u5206\u524D", openIssues: 3, prs: 1, sandbox: true },
24
- { id: "kds", name: "restaurant-kds", stack: "React \xB7 Electron", kind: "workstation", devs: 1, status: "review", branch: "main", lastCommit: "\u6628\u65E5", openIssues: 2, prs: 1, sandbox: true },
25
- { id: "kintai", name: "restaurant-kintai", stack: "Vue 3 \xB7 Laravel", kind: "service", devs: 2, status: "active", branch: "main", lastCommit: "5\u5206\u524D", openIssues: 4, prs: 0, sandbox: true },
26
- { id: "mobile", name: "restaurant-mobile", stack: "React Native", kind: "mobile", devs: 1, status: "planning", branch: "develop", lastCommit: "3\u65E5\u524D", openIssues: 1, prs: 0, sandbox: false }
27
- ]
28
- },
29
- {
30
- id: "godx",
31
- name: "godx-admin",
32
- tenant: "godx",
33
- role: "Platform admin",
34
- desc: "GoDX Forge developer workspace",
35
- color: "oklch(60% 0.137 163)",
36
- owner: "Satoshi F",
37
- devs: 4,
38
- projects: [
39
- { id: "frontend", name: "godx-admin-frontend", stack: "React \xB7 Vite", kind: "web", devs: 2, status: "active", branch: "master", lastCommit: "2\u6642\u9593\u524D", openIssues: 6, prs: 2, sandbox: true },
40
- { id: "api", name: "godx-admin-api", stack: "Go \xB7 Gin", kind: "service", devs: 1, status: "active", branch: "master", lastCommit: "4\u6642\u9593\u524D", openIssues: 3, prs: 1, sandbox: true },
41
- { id: "ui", name: "@godxjp/ui", stack: "TypeScript \xB7 React", kind: "library", devs: 2, status: "active", branch: "master", lastCommit: "1\u6642\u9593\u524D", openIssues: 2, prs: 0, sandbox: false }
42
- ]
43
- },
44
- {
45
- id: "kintai",
46
- name: "dxs-kintai",
47
- tenant: "kintai",
48
- role: "HR / Attendance",
49
- desc: "\u52E4\u6020\u7BA1\u7406\u30D7\u30E9\u30C3\u30C8\u30D5\u30A9\u30FC\u30E0",
50
- color: "oklch(56% 0.15 240)",
51
- owner: "Naoki N",
52
- devs: 3,
53
- projects: [
54
- { id: "frontend", name: "kintai-web", stack: "Vue 3 \xB7 Vite", kind: "web", devs: 2, status: "active", branch: "main", lastCommit: "20\u5206\u524D", openIssues: 7, prs: 1, sandbox: true },
55
- { id: "backend", name: "kintai-api", stack: "Laravel 11", kind: "service", devs: 1, status: "active", branch: "main", lastCommit: "1\u65E5\u524D", openIssues: 4, prs: 0, sandbox: true }
56
- ]
57
- },
58
- {
59
- id: "tempo",
60
- name: "dxs-tempo",
61
- tenant: "tempo",
62
- role: "Shop / Inventory",
63
- desc: "\u5E97\u8217\u30FB\u5728\u5EAB\u30D0\u30C3\u30AF\u30A8\u30F3\u30C9",
64
- color: "oklch(48% 0.16 285)",
65
- owner: "Naoki N",
66
- devs: 2,
67
- projects: [
68
- { id: "api", name: "tempo-api", stack: "Go \xB7 Echo", kind: "service", devs: 2, status: "active", branch: "main", lastCommit: "5\u6642\u9593\u524D", openIssues: 9, prs: 1, sandbox: true },
69
- { id: "ops", name: "tempo-ops", stack: "Terraform", kind: "infra", devs: 1, status: "planning", branch: "main", lastCommit: "1\u9031\u9593\u524D", openIssues: 1, prs: 0, sandbox: false }
70
- ]
71
- },
72
- {
73
- id: "betoya",
74
- name: "betoya",
75
- tenant: "betoya",
76
- role: "Vietnamese restaurant",
77
- desc: "\u30D9\u30C8\u5C4B Tenant",
78
- color: "oklch(58% 0.159 150)",
79
- owner: "Anh K",
80
- devs: 1,
81
- projects: [
82
- { id: "site", name: "betoya-site", stack: "Astro", kind: "web", devs: 1, status: "active", branch: "main", lastCommit: "\u6628\u65E5", openIssues: 2, prs: 0, sandbox: false }
83
- ]
25
+ // src/i18n/format.ts
26
+ function resolveLocale(opts) {
27
+ return opts?.locale ?? getGodxConfig().locale;
28
+ }
29
+ function resolveTimezone(opts) {
30
+ return opts?.timezone ?? getGodxConfig().timezone;
31
+ }
32
+ function toDate(value) {
33
+ if (value instanceof Date) return value;
34
+ if (typeof value === "number") return new Date(value);
35
+ if (typeof value === "string") return new Date(value);
36
+ if (typeof value === "object" && value !== null && "toDate" in value) {
37
+ const v = value;
38
+ return v.toDate(getGodxConfig().timezone);
84
39
  }
85
- ];
40
+ throw new TypeError(`format helpers: unsupported value ${String(value)}`);
41
+ }
42
+ function extractIntlOpts(opts) {
43
+ if (!opts) return {};
44
+ const { locale: _l, timezone: _t, ...rest } = opts;
45
+ return rest;
46
+ }
47
+ function formatDate(value, opts) {
48
+ const locale = resolveLocale(opts);
49
+ const timezone = resolveTimezone(opts);
50
+ const intlOpts = {
51
+ dateStyle: "medium",
52
+ ...extractIntlOpts(opts),
53
+ timeZone: timezone
54
+ };
55
+ return new Intl.DateTimeFormat(locale, intlOpts).format(toDate(value));
56
+ }
57
+ function formatTime(value, opts) {
58
+ const locale = resolveLocale(opts);
59
+ const timezone = resolveTimezone(opts);
60
+ const intlOpts = {
61
+ timeStyle: "short",
62
+ ...extractIntlOpts(opts),
63
+ timeZone: timezone
64
+ };
65
+ return new Intl.DateTimeFormat(locale, intlOpts).format(toDate(value));
66
+ }
67
+ function formatDateTime(value, opts) {
68
+ const locale = resolveLocale(opts);
69
+ const timezone = resolveTimezone(opts);
70
+ const intlOpts = {
71
+ dateStyle: "medium",
72
+ timeStyle: "short",
73
+ ...extractIntlOpts(opts),
74
+ timeZone: timezone
75
+ };
76
+ return new Intl.DateTimeFormat(locale, intlOpts).format(toDate(value));
77
+ }
78
+ function formatNumber(value, opts) {
79
+ const locale = resolveLocale(opts);
80
+ return new Intl.NumberFormat(locale, extractIntlOpts(opts)).format(value);
81
+ }
82
+ function formatCurrency(value, opts) {
83
+ const locale = resolveLocale(opts);
84
+ const intlOpts = {
85
+ style: "currency",
86
+ ...extractIntlOpts(opts)
87
+ };
88
+ return new Intl.NumberFormat(locale, intlOpts).format(value);
89
+ }
90
+
91
+ // src/i18n/relative.ts
92
+ var SECOND = 1e3;
93
+ var MINUTE = 60 * SECOND;
94
+ var HOUR = 60 * MINUTE;
95
+ var DAY = 24 * HOUR;
96
+ var WEEK = 7 * DAY;
97
+ var MONTH = 30 * DAY;
98
+ var YEAR = 365 * DAY;
99
+ function toMs(value) {
100
+ if (value instanceof Date) return value.getTime();
101
+ if (typeof value === "number") return value;
102
+ if (typeof value === "string") return new Date(value).getTime();
103
+ if (typeof value === "object" && value !== null && "toDate" in value) {
104
+ return value.toDate(getGodxConfig().timezone).getTime();
105
+ }
106
+ throw new TypeError(`formatRelative: unsupported value ${String(value)}`);
107
+ }
108
+ function formatRelative(value, opts = {}) {
109
+ const locale = opts.locale ?? getGodxConfig().locale;
110
+ const nowMs = opts.now instanceof Date ? opts.now.getTime() : opts.now ?? Date.now();
111
+ const valueMs = toMs(value);
112
+ const diffMs = valueMs - nowMs;
113
+ const abs = Math.abs(diffMs);
114
+ let unit;
115
+ let amount;
116
+ if (abs < MINUTE) {
117
+ unit = "second";
118
+ amount = Math.round(diffMs / SECOND);
119
+ } else if (abs < HOUR) {
120
+ unit = "minute";
121
+ amount = Math.round(diffMs / MINUTE);
122
+ } else if (abs < DAY) {
123
+ unit = "hour";
124
+ amount = Math.round(diffMs / HOUR);
125
+ } else if (abs < WEEK) {
126
+ unit = "day";
127
+ amount = Math.round(diffMs / DAY);
128
+ } else if (abs < MONTH) {
129
+ unit = "week";
130
+ amount = Math.round(diffMs / WEEK);
131
+ } else if (abs < YEAR) {
132
+ unit = "month";
133
+ amount = Math.round(diffMs / MONTH);
134
+ } else {
135
+ unit = "year";
136
+ amount = Math.round(diffMs / YEAR);
137
+ }
138
+ return new Intl.RelativeTimeFormat(locale, {
139
+ numeric: opts.numeric ?? "auto",
140
+ style: opts.style ?? "long"
141
+ }).format(amount, unit);
142
+ }
143
+
144
+ // src/i18n/index.ts
145
+ var GODX_LOCALE_STORAGE_KEY = "godx.locale";
146
+ var i18n_default = i18next;
86
147
 
87
148
  // src/hooks/useTweaks.ts
88
- var STORAGE_KEY = "forge.tweaks";
149
+ var STORAGE_KEY = "godx.tweaks";
89
150
  var DEFAULTS = {
90
151
  density: "default",
91
152
  theme: "light",
@@ -124,10 +185,11 @@ function useTweaks() {
124
185
  html.lang = tweaks.locale;
125
186
  }, [tweaks.theme, tweaks.density, tweaks.tenant, tweaks.locale]);
126
187
  useEffect(() => {
188
+ if (!i18n_default.isInitialized) return;
127
189
  if (i18n_default.language?.slice(0, 2) !== tweaks.locale) {
128
190
  void i18n_default.changeLanguage(tweaks.locale);
129
191
  try {
130
- window.localStorage.setItem(FORGE_LOCALE_STORAGE_KEY, tweaks.locale);
192
+ window.localStorage.setItem(GODX_LOCALE_STORAGE_KEY, tweaks.locale);
131
193
  } catch {
132
194
  }
133
195
  }
@@ -137,8 +199,360 @@ function useTweaks() {
137
199
  }, []);
138
200
  return { tweaks, setTweak, setTweaks };
139
201
  }
140
- var PRODUCT_OPTIONS = PRODUCTS.map((p) => ({ value: p.tenant, label: p.name }));
202
+ function productOptions(products) {
203
+ return products.map((p) => ({ value: p.tenant, label: p.name }));
204
+ }
205
+ var BP_ORDER = ["xxl", "xl", "lg", "md", "sm", "xs"];
206
+ var BP_ORDER_ASC = ["xs", "sm", "md", "lg", "xl", "xxl"];
207
+ var FALLBACK_WIDTH = {
208
+ xs: "0px",
209
+ sm: "640px",
210
+ md: "768px",
211
+ lg: "1024px",
212
+ xl: "1280px",
213
+ xxl: "1536px"
214
+ };
215
+ function widthOf(bp) {
216
+ if (typeof window === "undefined") return FALLBACK_WIDTH[bp];
217
+ const v = getComputedStyle(document.documentElement).getPropertyValue(`--breakpoint-${bp}`).trim();
218
+ return v || FALLBACK_WIDTH[bp];
219
+ }
220
+ function currentBreakpoint() {
221
+ if (typeof window === "undefined") return "xs";
222
+ for (const bp of BP_ORDER) {
223
+ if (window.matchMedia(`(min-width: ${widthOf(bp)})`).matches) {
224
+ return bp;
225
+ }
226
+ }
227
+ return "xs";
228
+ }
229
+ function useBreakpoint() {
230
+ const [bp, setBp] = useState(() => currentBreakpoint());
231
+ useEffect(() => {
232
+ if (typeof window === "undefined") return;
233
+ const mqls = BP_ORDER.map(
234
+ (b) => window.matchMedia(`(min-width: ${widthOf(b)})`)
235
+ );
236
+ const update = () => setBp(currentBreakpoint());
237
+ mqls.forEach((mql) => mql.addEventListener("change", update));
238
+ update();
239
+ return () => {
240
+ mqls.forEach((mql) => mql.removeEventListener("change", update));
241
+ };
242
+ }, []);
243
+ return bp;
244
+ }
245
+ function matchBreakpoint(current, target) {
246
+ return BP_ORDER_ASC.indexOf(current) >= BP_ORDER_ASC.indexOf(target);
247
+ }
248
+ function useFormatters() {
249
+ const { locale, timezone } = useSyncExternalStore(
250
+ subscribeGodxConfig,
251
+ getGodxConfig,
252
+ getGodxConfig
253
+ );
254
+ return useMemo(
255
+ () => ({
256
+ locale,
257
+ timezone,
258
+ formatDate: (value, opts) => formatDate(value, { ...opts, locale, timezone }),
259
+ formatTime: (value, opts) => formatTime(value, { ...opts, locale, timezone }),
260
+ formatDateTime: (value, opts) => formatDateTime(value, { ...opts, locale, timezone }),
261
+ formatRelative: (value, opts) => formatRelative(value, { ...opts, locale }),
262
+ formatNumber: (value, opts) => formatNumber(value, { ...opts, locale }),
263
+ formatCurrency: (value, opts) => formatCurrency(value, { ...opts, locale })
264
+ }),
265
+ [locale, timezone]
266
+ );
267
+ }
268
+ function useTablePagination(options = {}) {
269
+ const {
270
+ defaultPage = 1,
271
+ defaultPageSize = 20,
272
+ page: controlledPage,
273
+ pageSize: controlledPageSize,
274
+ onChange
275
+ } = options;
276
+ const [internalPage, setInternalPage] = useState(defaultPage);
277
+ const [internalPageSize, setInternalPageSize] = useState(defaultPageSize);
278
+ const page = controlledPage ?? internalPage;
279
+ const pageSize = controlledPageSize ?? internalPageSize;
280
+ const setPage = useCallback(
281
+ (nextPage) => {
282
+ if (controlledPage === void 0) setInternalPage(nextPage);
283
+ onChange?.(nextPage, pageSize);
284
+ },
285
+ [controlledPage, onChange, pageSize]
286
+ );
287
+ const setPageSize = useCallback(
288
+ (nextPageSize) => {
289
+ if (controlledPageSize === void 0) setInternalPageSize(nextPageSize);
290
+ if (controlledPage === void 0) setInternalPage(1);
291
+ onChange?.(1, nextPageSize);
292
+ },
293
+ [controlledPage, controlledPageSize, onChange]
294
+ );
295
+ const handleChange = useCallback(
296
+ (nextPage, nextPageSize) => {
297
+ if (controlledPage === void 0) setInternalPage(nextPage);
298
+ if (controlledPageSize === void 0) setInternalPageSize(nextPageSize);
299
+ onChange?.(nextPage, nextPageSize);
300
+ },
301
+ [controlledPage, controlledPageSize, onChange]
302
+ );
303
+ const resetPage = useCallback(() => {
304
+ if (controlledPage === void 0) setInternalPage(1);
305
+ onChange?.(1, pageSize);
306
+ }, [controlledPage, onChange, pageSize]);
307
+ const reset = useCallback(() => {
308
+ if (controlledPage === void 0) setInternalPage(defaultPage);
309
+ if (controlledPageSize === void 0) setInternalPageSize(defaultPageSize);
310
+ onChange?.(defaultPage, defaultPageSize);
311
+ }, [
312
+ controlledPage,
313
+ controlledPageSize,
314
+ defaultPage,
315
+ defaultPageSize,
316
+ onChange
317
+ ]);
318
+ return {
319
+ page,
320
+ pageSize,
321
+ setPage,
322
+ setPageSize,
323
+ onChange: handleChange,
324
+ resetPage,
325
+ reset
326
+ };
327
+ }
328
+ function useTableSelection(options = {}) {
329
+ const {
330
+ mode = "multiple",
331
+ defaultSelected = [],
332
+ selected: controlledSelected,
333
+ onChange
334
+ } = options;
335
+ const [internal, setInternal] = useState(defaultSelected);
336
+ const selectedRowKeys = controlledSelected ?? internal;
337
+ const setSelectedRowKeys = useCallback(
338
+ (next) => {
339
+ if (controlledSelected === void 0) setInternal(next);
340
+ onChange?.(next);
341
+ },
342
+ [controlledSelected, onChange]
343
+ );
344
+ const select = useCallback(
345
+ (key) => {
346
+ const next = mode === "single" ? [key] : selectedRowKeys.includes(key) ? selectedRowKeys : [...selectedRowKeys, key];
347
+ if (next === selectedRowKeys) return;
348
+ setSelectedRowKeys(next);
349
+ },
350
+ [mode, selectedRowKeys, setSelectedRowKeys]
351
+ );
352
+ const deselect = useCallback(
353
+ (key) => {
354
+ if (!selectedRowKeys.includes(key)) return;
355
+ setSelectedRowKeys(selectedRowKeys.filter((k) => k !== key));
356
+ },
357
+ [selectedRowKeys, setSelectedRowKeys]
358
+ );
359
+ const toggle = useCallback(
360
+ (key) => {
361
+ if (selectedRowKeys.includes(key)) {
362
+ setSelectedRowKeys(selectedRowKeys.filter((k) => k !== key));
363
+ } else {
364
+ setSelectedRowKeys(mode === "single" ? [key] : [...selectedRowKeys, key]);
365
+ }
366
+ },
367
+ [mode, selectedRowKeys, setSelectedRowKeys]
368
+ );
369
+ const clear = useCallback(() => {
370
+ if (selectedRowKeys.length === 0) return;
371
+ setSelectedRowKeys([]);
372
+ }, [selectedRowKeys.length, setSelectedRowKeys]);
373
+ const isSelected = useCallback(
374
+ (key) => selectedRowKeys.includes(key),
375
+ [selectedRowKeys]
376
+ );
377
+ return {
378
+ selectedRowKeys,
379
+ setSelectedRowKeys,
380
+ toggle,
381
+ select,
382
+ deselect,
383
+ clear,
384
+ isSelected,
385
+ count: selectedRowKeys.length
386
+ };
387
+ }
388
+ function readPersisted(storage, storageKey, currentVersion, migrate) {
389
+ if (!storage) return void 0;
390
+ try {
391
+ const raw = storage.getItem(storageKey);
392
+ if (raw === null) return void 0;
393
+ const parsed = JSON.parse(raw);
394
+ if (typeof parsed !== "object" || parsed === null) return void 0;
395
+ const envelope = parsed;
396
+ if (typeof envelope.version !== "number" || !("value" in envelope))
397
+ return void 0;
398
+ if (envelope.version === currentVersion) return envelope.value;
399
+ if (migrate)
400
+ return migrate({
401
+ version: envelope.version,
402
+ value: envelope.value
403
+ });
404
+ return void 0;
405
+ } catch {
406
+ return void 0;
407
+ }
408
+ }
409
+ function writePersisted(storage, storageKey, version, value) {
410
+ if (!storage) return;
411
+ try {
412
+ storage.setItem(
413
+ storageKey,
414
+ JSON.stringify({ version, value })
415
+ );
416
+ } catch {
417
+ }
418
+ }
419
+ function resolveStorage(override) {
420
+ if (override !== void 0) return override;
421
+ if (typeof window === "undefined") return null;
422
+ try {
423
+ return window.localStorage;
424
+ } catch {
425
+ return null;
426
+ }
427
+ }
428
+ function useTableState(options) {
429
+ const {
430
+ storageKey,
431
+ defaultValue,
432
+ version = 1,
433
+ migrate,
434
+ storage: storageOverride
435
+ } = options;
436
+ const storage = resolveStorage(storageOverride);
437
+ const [value, setValue] = useState(() => {
438
+ if (storageKey === void 0) return defaultValue;
439
+ const persisted = readPersisted(storage, storageKey, version, migrate);
440
+ return persisted !== void 0 ? persisted : defaultValue;
441
+ });
442
+ useEffect(() => {
443
+ if (storageKey === void 0) return;
444
+ writePersisted(storage, storageKey, version, value);
445
+ }, [storage, storageKey, value, version]);
446
+ const setNext = useCallback(
447
+ (next) => {
448
+ setValue(
449
+ (prev) => typeof next === "function" ? next(prev) : next
450
+ );
451
+ },
452
+ []
453
+ );
454
+ return [value, setNext];
455
+ }
456
+ var CUSTOM_KEY = "custom";
457
+ function isPersistableLabel(label) {
458
+ return typeof label === "string";
459
+ }
460
+ function generateKey() {
461
+ if (typeof crypto !== "undefined" && "randomUUID" in crypto) {
462
+ return `view-${crypto.randomUUID()}`;
463
+ }
464
+ return `view-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
465
+ }
466
+ function useTableViews(options) {
467
+ const {
468
+ items: builtInItems,
469
+ defaultActive,
470
+ activeKey: controlledActiveKey,
471
+ onActiveKeyChange,
472
+ onSavedViewsChange,
473
+ storageKey,
474
+ maxSavedViews = 20
475
+ } = options;
476
+ const [internalActiveKey, setInternalActiveKey] = useState(
477
+ defaultActive ?? builtInItems[0]?.key
478
+ );
479
+ const activeKey = controlledActiveKey ?? internalActiveKey;
480
+ const [savedViews, setSavedViewsState] = useTableState({
481
+ storageKey,
482
+ defaultValue: [],
483
+ version: 1
484
+ });
485
+ const updateSavedViews = useCallback(
486
+ (next) => {
487
+ setSavedViewsState((prev) => {
488
+ const resolved = typeof next === "function" ? next(prev) : next;
489
+ const trimmed = resolved.slice(-maxSavedViews);
490
+ onSavedViewsChange?.(trimmed);
491
+ return trimmed;
492
+ });
493
+ },
494
+ [maxSavedViews, onSavedViewsChange, setSavedViewsState]
495
+ );
496
+ const setActiveKey = useCallback(
497
+ (key) => {
498
+ if (controlledActiveKey === void 0) setInternalActiveKey(key);
499
+ onActiveKeyChange?.(key);
500
+ },
501
+ [controlledActiveKey, onActiveKeyChange]
502
+ );
503
+ const applyView = useCallback(
504
+ (view) => {
505
+ setActiveKey(view.key);
506
+ return {
507
+ filters: view.filters,
508
+ sort: view.sort,
509
+ columnVisibility: view.columnVisibility
510
+ };
511
+ },
512
+ [setActiveKey]
513
+ );
514
+ const saveView = useCallback(
515
+ (label, snapshot) => {
516
+ const next = {
517
+ key: generateKey(),
518
+ label,
519
+ ...snapshot,
520
+ deletable: true
521
+ };
522
+ updateSavedViews((prev) => [...prev, next]);
523
+ setActiveKey(next.key);
524
+ return next;
525
+ },
526
+ [setActiveKey, updateSavedViews]
527
+ );
528
+ const deleteView = useCallback(
529
+ (key) => {
530
+ const target = savedViews.find((view) => view.key === key);
531
+ if (!target || !isPersistableLabel(target.label)) return;
532
+ updateSavedViews((prev) => prev.filter((view) => view.key !== key));
533
+ if (activeKey === key) {
534
+ const firstBuiltIn = builtInItems[0]?.key;
535
+ if (firstBuiltIn !== void 0) setActiveKey(firstBuiltIn);
536
+ }
537
+ },
538
+ [activeKey, builtInItems, savedViews, setActiveKey, updateSavedViews]
539
+ );
540
+ const markCustom = useCallback(() => {
541
+ if (activeKey === CUSTOM_KEY) return;
542
+ setActiveKey(CUSTOM_KEY);
543
+ }, [activeKey, setActiveKey]);
544
+ return {
545
+ items: [...builtInItems, ...savedViews],
546
+ savedViews,
547
+ activeKey,
548
+ setActiveKey,
549
+ applyView,
550
+ saveView,
551
+ deleteView,
552
+ markCustom
553
+ };
554
+ }
141
555
 
142
- export { PRODUCT_OPTIONS, useTweaks };
556
+ export { matchBreakpoint, productOptions, useBreakpoint, useFormatters, useTablePagination, useTableSelection, useTableState, useTableViews, useTweaks };
143
557
  //# sourceMappingURL=hooks.js.map
144
558
  //# sourceMappingURL=hooks.js.map