@lt-three/love3 0.0.0 → 0.1.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.
- package/README.md +85 -1
- package/dist/native/analytics/animation.d.ts +4 -0
- package/dist/native/analytics/animation.js +6 -0
- package/dist/native/analytics/chart.types.d.ts +25 -0
- package/dist/native/analytics/chart.types.js +0 -0
- package/dist/native/analytics/line-chart.d.ts +4 -0
- package/dist/native/analytics/line-chart.ios.d.ts +4 -0
- package/dist/native/analytics/line-chart.ios.js +24 -0
- package/dist/native/analytics/line-chart.js +33 -0
- package/dist/native/analytics/metric-card.d.ts +10 -0
- package/dist/native/analytics/metric-card.js +29 -0
- package/dist/native/analytics/multi-line-chart.d.ts +18 -0
- package/dist/native/analytics/multi-line-chart.js +216 -0
- package/dist/native/analytics/pie-chart.d.ts +15 -0
- package/dist/native/analytics/pie-chart.ios.d.ts +4 -0
- package/dist/native/analytics/pie-chart.ios.js +24 -0
- package/dist/native/analytics/pie-chart.js +92 -0
- package/dist/native/analytics/single-number.d.ts +19 -0
- package/dist/native/analytics/single-number.js +101 -0
- package/dist/native/atom/avatar.d.ts +21 -0
- package/dist/native/atom/avatar.js +46 -0
- package/dist/native/atom/badge.d.ts +28 -0
- package/dist/native/atom/badge.js +51 -0
- package/dist/native/atom/button.d.ts +46 -0
- package/dist/native/atom/button.js +109 -0
- package/dist/native/atom/checkbox.d.ts +21 -0
- package/dist/native/atom/checkbox.js +47 -0
- package/dist/native/atom/colored-value.d.ts +12 -0
- package/dist/native/atom/colored-value.js +24 -0
- package/dist/native/atom/image.d.ts +21 -0
- package/dist/native/atom/image.js +72 -0
- package/dist/native/atom/label.d.ts +18 -0
- package/dist/native/atom/label.js +33 -0
- package/dist/native/atom/link.d.ts +18 -0
- package/dist/native/atom/link.js +25 -0
- package/dist/native/atom/readout.d.ts +22 -0
- package/dist/native/atom/readout.js +39 -0
- package/dist/native/atom/search-input.d.ts +12 -0
- package/dist/native/atom/search-input.js +36 -0
- package/dist/native/atom/select.d.ts +54 -0
- package/dist/native/atom/select.js +200 -0
- package/dist/native/atom/separator.d.ts +12 -0
- package/dist/native/atom/separator.js +16 -0
- package/dist/native/atom/skeleton.d.ts +12 -0
- package/dist/native/atom/skeleton.js +39 -0
- package/dist/native/atom/slider.d.ts +32 -0
- package/dist/native/atom/slider.js +75 -0
- package/dist/native/atom/spinner.d.ts +15 -0
- package/dist/native/atom/spinner.js +25 -0
- package/dist/native/atom/switch.d.ts +18 -0
- package/dist/native/atom/switch.js +52 -0
- package/dist/native/atom/symbol.d.ts +16 -0
- package/dist/native/atom/symbol.js +38 -0
- package/dist/native/atom/symbol.types.d.ts +25 -0
- package/dist/native/atom/symbol.types.js +49 -0
- package/dist/native/atom/text-field.d.ts +49 -0
- package/dist/native/atom/text-field.js +56 -0
- package/dist/native/atom/text.d.ts +2 -0
- package/dist/native/atom/text.js +3 -0
- package/dist/native/atom/theme-toggle.d.ts +21 -0
- package/dist/native/atom/theme-toggle.js +36 -0
- package/dist/native/atom/timezone-select.d.ts +16 -0
- package/dist/native/atom/timezone-select.js +28 -0
- package/dist/native/atom/typography.d.ts +13 -0
- package/dist/native/atom/typography.js +28 -0
- package/dist/native/base/button.d.ts +61 -0
- package/dist/native/base/button.js +92 -0
- package/dist/native/base/form.d.ts +72 -0
- package/dist/native/base/form.js +113 -0
- package/dist/native/base/glass-card.d.ts +15 -0
- package/dist/native/base/glass-card.js +38 -0
- package/dist/native/base/input.d.ts +29 -0
- package/dist/native/base/input.js +27 -0
- package/dist/native/base/sheet.d.ts +71 -0
- package/dist/native/base/sheet.js +187 -0
- package/dist/native/base/text.d.ts +14 -0
- package/dist/native/base/text.js +53 -0
- package/dist/native/form/checkbox.d.ts +13 -0
- package/dist/native/form/checkbox.js +55 -0
- package/dist/native/form/date-picker.d.ts +23 -0
- package/dist/native/form/date-picker.js +119 -0
- package/dist/native/form/form.d.ts +22 -0
- package/dist/native/form/form.js +44 -0
- package/dist/native/form/radio-group.d.ts +23 -0
- package/dist/native/form/radio-group.js +88 -0
- package/dist/native/form/select.d.ts +13 -0
- package/dist/native/form/select.js +45 -0
- package/dist/native/form/slider.d.ts +14 -0
- package/dist/native/form/slider.js +42 -0
- package/dist/native/form/switch.d.ts +9 -0
- package/dist/native/form/switch.js +59 -0
- package/dist/native/form/text-area.d.ts +11 -0
- package/dist/native/form/text-area.js +50 -0
- package/dist/native/form/text-input.d.ts +18 -0
- package/dist/native/form/text-input.js +90 -0
- package/dist/native/index.d.ts +1 -0
- package/dist/native/index.js +0 -0
- package/dist/native/layout/accordion.d.ts +25 -0
- package/dist/native/layout/accordion.js +54 -0
- package/dist/native/layout/card.d.ts +47 -0
- package/dist/native/layout/card.js +57 -0
- package/dist/native/layout/description.d.ts +16 -0
- package/dist/native/layout/description.js +32 -0
- package/dist/native/layout/error.d.ts +21 -0
- package/dist/native/layout/error.js +44 -0
- package/dist/native/layout/information.d.ts +25 -0
- package/dist/native/layout/information.js +35 -0
- package/dist/native/layout/locked.d.ts +38 -0
- package/dist/native/layout/locked.js +94 -0
- package/dist/native/layout/modal.d.ts +25 -0
- package/dist/native/layout/modal.js +93 -0
- package/dist/native/layout/not-found.d.ts +17 -0
- package/dist/native/layout/not-found.js +30 -0
- package/dist/native/layout/page-frame.d.ts +23 -0
- package/dist/native/layout/page-frame.js +46 -0
- package/dist/native/layout/panel.d.ts +18 -0
- package/dist/native/layout/panel.js +23 -0
- package/dist/native/layout/reorderable-list.d.ts +42 -0
- package/dist/native/layout/reorderable-list.js +52 -0
- package/dist/native/layout/sheet.d.ts +30 -0
- package/dist/native/layout/sheet.js +46 -0
- package/dist/native/layout/simple-dialog.d.ts +16 -0
- package/dist/native/layout/simple-dialog.js +20 -0
- package/dist/native/layout/simple-table.d.ts +50 -0
- package/dist/native/layout/simple-table.js +75 -0
- package/dist/native/layout/tab-bar.d.ts +33 -0
- package/dist/native/layout/tab-bar.js +48 -0
- package/dist/native/layout/tabs.d.ts +28 -0
- package/dist/native/layout/tabs.js +54 -0
- package/dist/native/layout/tuning-panel.d.ts +24 -0
- package/dist/native/layout/tuning-panel.js +246 -0
- package/dist/native/lib/autocomplete.d.ts +7 -0
- package/dist/native/lib/autocomplete.js +34 -0
- package/dist/native/lib/breakpoints.d.ts +14 -0
- package/dist/native/lib/breakpoints.js +17 -0
- package/dist/native/lib/chart-palette.d.ts +8 -0
- package/dist/native/lib/chart-palette.js +20 -0
- package/dist/native/lib/confirm.d.ts +48 -0
- package/dist/native/lib/confirm.js +69 -0
- package/dist/native/lib/control.d.ts +24 -0
- package/dist/native/lib/control.js +0 -0
- package/dist/native/lib/external-store.d.ts +16 -0
- package/dist/native/lib/external-store.js +30 -0
- package/dist/native/lib/keyboard.d.ts +15 -0
- package/dist/native/lib/keyboard.js +24 -0
- package/dist/native/lib/labels.d.ts +15 -0
- package/dist/native/lib/labels.js +17 -0
- package/dist/native/lib/loading.d.ts +10 -0
- package/dist/native/lib/loading.js +15 -0
- package/dist/native/lib/network.d.ts +11 -0
- package/dist/native/lib/network.js +20 -0
- package/dist/native/lib/scheme.d.ts +18 -0
- package/dist/native/lib/scheme.js +22 -0
- package/dist/native/lib/snackbar.d.ts +45 -0
- package/dist/native/lib/snackbar.js +59 -0
- package/dist/native/lib/theme.d.ts +34 -0
- package/dist/native/lib/theme.js +53 -0
- package/dist/native/lib/timezone.d.ts +26 -0
- package/dist/native/lib/timezone.js +95 -0
- package/dist/native/lib/token-color.d.ts +5 -0
- package/dist/native/lib/token-color.js +13 -0
- package/dist/native/lib/token-color.web.d.ts +4 -0
- package/dist/native/lib/token-color.web.js +7 -0
- package/dist/native/lib/tuning-context.d.ts +9 -0
- package/dist/native/lib/tuning-context.js +7 -0
- package/dist/native/lib/tuning.d.ts +24 -0
- package/dist/native/lib/tuning.js +42 -0
- package/dist/native/lib/typography.d.ts +10 -0
- package/dist/native/lib/typography.js +11 -0
- package/dist/native/lib/utils.d.ts +11 -0
- package/dist/native/lib/utils.js +21 -0
- package/dist/native/once/confirm.d.ts +7 -0
- package/dist/native/once/confirm.js +60 -0
- package/dist/native/once/error-boundary.d.ts +28 -0
- package/dist/native/once/error-boundary.js +55 -0
- package/dist/native/once/loading-lock.d.ts +8 -0
- package/dist/native/once/loading-lock.js +28 -0
- package/dist/native/once/offline-banner.d.ts +15 -0
- package/dist/native/once/offline-banner.js +35 -0
- package/dist/native/once/snackbar.d.ts +17 -0
- package/dist/native/once/snackbar.js +106 -0
- package/dist/native/styles.css +46 -0
- package/dist/shared/analytics.d.ts +71 -0
- package/dist/shared/analytics.js +0 -0
- package/dist/shared/button.d.ts +7 -0
- package/dist/shared/button.js +23 -0
- package/dist/shared/form.d.ts +45 -0
- package/dist/shared/form.js +46 -0
- package/dist/shared/index.d.ts +3 -0
- package/dist/shared/index.js +3 -0
- package/dist/tokens/contrast-pairs.d.ts +30 -0
- package/dist/tokens/contrast-pairs.js +404 -0
- package/dist/tokens/index.d.ts +6 -0
- package/dist/tokens/index.js +10 -0
- package/dist/tokens/styles.gen.css +250 -0
- package/dist/tokens/tokens.gen.d.ts +141 -0
- package/dist/tokens/tokens.gen.js +206 -0
- package/dist/tuning/catalog.d.ts +27 -0
- package/dist/tuning/catalog.gen.d.ts +4 -0
- package/dist/tuning/catalog.gen.js +397 -0
- package/dist/tuning/catalog.js +128 -0
- package/dist/tuning/color.d.ts +20 -0
- package/dist/tuning/color.js +80 -0
- package/dist/tuning/css.d.ts +35 -0
- package/dist/tuning/css.js +147 -0
- package/dist/tuning/index.d.ts +8 -0
- package/dist/tuning/index.js +9 -0
- package/dist/tuning/model.d.ts +33 -0
- package/dist/tuning/model.js +154 -0
- package/dist/tuning/native.d.ts +35 -0
- package/dist/tuning/native.js +80 -0
- package/dist/tuning/schema.d.ts +69 -0
- package/dist/tuning/schema.js +247 -0
- package/dist/tuning/types.d.ts +75 -0
- package/dist/tuning/types.js +39 -0
- package/dist/web/analytics/line-chart.d.ts +20 -0
- package/dist/web/analytics/line-chart.js +28 -0
- package/dist/web/analytics/metric-card.d.ts +18 -0
- package/dist/web/analytics/metric-card.js +40 -0
- package/dist/web/analytics/multi-line-chart.d.ts +39 -0
- package/dist/web/analytics/multi-line-chart.js +345 -0
- package/dist/web/atom/avatar.d.ts +11 -0
- package/dist/web/atom/avatar.js +24 -0
- package/dist/web/atom/badge.d.ts +2 -0
- package/dist/web/atom/badge.js +3 -0
- package/dist/web/atom/button.d.ts +23 -0
- package/dist/web/atom/button.js +35 -0
- package/dist/web/atom/checkbox.d.ts +19 -0
- package/dist/web/atom/checkbox.js +33 -0
- package/dist/web/atom/colored-value.d.ts +13 -0
- package/dist/web/atom/colored-value.js +24 -0
- package/dist/web/atom/help.d.ts +10 -0
- package/dist/web/atom/help.js +18 -0
- package/dist/web/atom/label.d.ts +2 -0
- package/dist/web/atom/label.js +3 -0
- package/dist/web/atom/segmented-control.d.ts +39 -0
- package/dist/web/atom/segmented-control.js +42 -0
- package/dist/web/atom/select.d.ts +43 -0
- package/dist/web/atom/select.js +93 -0
- package/dist/web/atom/separator.d.ts +2 -0
- package/dist/web/atom/separator.js +3 -0
- package/dist/web/atom/skeleton.d.ts +2 -0
- package/dist/web/atom/skeleton.js +3 -0
- package/dist/web/atom/slider.d.ts +33 -0
- package/dist/web/atom/slider.js +70 -0
- package/dist/web/atom/spinner.d.ts +8 -0
- package/dist/web/atom/spinner.js +14 -0
- package/dist/web/atom/switch.d.ts +11 -0
- package/dist/web/atom/switch.js +10 -0
- package/dist/web/atom/text-area.d.ts +23 -0
- package/dist/web/atom/text-area.js +27 -0
- package/dist/web/atom/text-field.d.ts +18 -0
- package/dist/web/atom/text-field.js +22 -0
- package/dist/web/atom/toggle-chip.d.ts +22 -0
- package/dist/web/atom/toggle-chip.js +28 -0
- package/dist/web/atom/tooltip.d.ts +23 -0
- package/dist/web/atom/tooltip.js +26 -0
- package/dist/web/atom/typography.d.ts +6 -0
- package/dist/web/atom/typography.js +23 -0
- package/dist/web/base/accordion.d.ts +9 -0
- package/dist/web/base/accordion.js +44 -0
- package/dist/web/base/alert.d.ts +11 -0
- package/dist/web/base/alert.js +37 -0
- package/dist/web/base/avatar.d.ts +8 -0
- package/dist/web/base/avatar.js +29 -0
- package/dist/web/base/badge.d.ts +12 -0
- package/dist/web/base/badge.js +26 -0
- package/dist/web/base/breadcrumb.d.ts +16 -0
- package/dist/web/base/breadcrumb.js +72 -0
- package/dist/web/base/button.d.ts +12 -0
- package/dist/web/base/button.js +47 -0
- package/dist/web/base/card.d.ts +19 -0
- package/dist/web/base/card.js +74 -0
- package/dist/web/base/checkbox.d.ts +6 -0
- package/dist/web/base/checkbox.js +21 -0
- package/dist/web/base/dialog.d.ts +20 -0
- package/dist/web/base/dialog.js +92 -0
- package/dist/web/base/dropdown-menu.d.ts +29 -0
- package/dist/web/base/dropdown-menu.js +125 -0
- package/dist/web/base/field.d.ts +27 -0
- package/dist/web/base/field.js +118 -0
- package/dist/web/base/form.d.ts +38 -0
- package/dist/web/base/form.js +110 -0
- package/dist/web/base/input.d.ts +9 -0
- package/dist/web/base/input.js +23 -0
- package/dist/web/base/label.d.ts +6 -0
- package/dist/web/base/label.js +15 -0
- package/dist/web/base/pagination.d.ts +34 -0
- package/dist/web/base/pagination.js +90 -0
- package/dist/web/base/popover.d.ts +11 -0
- package/dist/web/base/popover.js +37 -0
- package/dist/web/base/radio-group.d.ts +7 -0
- package/dist/web/base/radio-group.js +27 -0
- package/dist/web/base/scroll-area.d.ts +10 -0
- package/dist/web/base/scroll-area.js +41 -0
- package/dist/web/base/select.d.ts +17 -0
- package/dist/web/base/select.js +96 -0
- package/dist/web/base/separator.d.ts +6 -0
- package/dist/web/base/separator.js +17 -0
- package/dist/web/base/sheet.d.ts +19 -0
- package/dist/web/base/sheet.js +88 -0
- package/dist/web/base/skeleton.d.ts +5 -0
- package/dist/web/base/skeleton.js +14 -0
- package/dist/web/base/slider.d.ts +9 -0
- package/dist/web/base/slider.js +39 -0
- package/dist/web/base/sonner.d.ts +9 -0
- package/dist/web/base/sonner.js +25 -0
- package/dist/web/base/spinner.d.ts +5 -0
- package/dist/web/base/spinner.js +17 -0
- package/dist/web/base/switch.d.ts +6 -0
- package/dist/web/base/switch.js +19 -0
- package/dist/web/base/table.d.ts +14 -0
- package/dist/web/base/table.js +68 -0
- package/dist/web/base/tabs.d.ts +9 -0
- package/dist/web/base/tabs.js +36 -0
- package/dist/web/base/textarea.d.ts +9 -0
- package/dist/web/base/textarea.js +23 -0
- package/dist/web/base/tooltip.d.ts +9 -0
- package/dist/web/base/tooltip.js +36 -0
- package/dist/web/form/checkbox.d.ts +35 -0
- package/dist/web/form/checkbox.js +91 -0
- package/dist/web/form/radio-group.d.ts +24 -0
- package/dist/web/form/radio-group.js +75 -0
- package/dist/web/form/select.d.ts +16 -0
- package/dist/web/form/select.js +44 -0
- package/dist/web/form/slider.d.ts +19 -0
- package/dist/web/form/slider.js +51 -0
- package/dist/web/form/switch.d.ts +11 -0
- package/dist/web/form/switch.js +50 -0
- package/dist/web/form/text-area.d.ts +10 -0
- package/dist/web/form/text-area.js +31 -0
- package/dist/web/form/text-input.d.ts +10 -0
- package/dist/web/form/text-input.js +42 -0
- package/dist/web/index.d.ts +1 -0
- package/dist/web/index.js +0 -0
- package/dist/web/layout/accordion.d.ts +26 -0
- package/dist/web/layout/accordion.js +29 -0
- package/dist/web/layout/alert-modal.d.ts +41 -0
- package/dist/web/layout/alert-modal.js +71 -0
- package/dist/web/layout/card.d.ts +17 -0
- package/dist/web/layout/card.js +35 -0
- package/dist/web/layout/description.d.ts +12 -0
- package/dist/web/layout/description.js +20 -0
- package/dist/web/layout/information.d.ts +36 -0
- package/dist/web/layout/information.js +53 -0
- package/dist/web/layout/modal.d.ts +16 -0
- package/dist/web/layout/modal.js +37 -0
- package/dist/web/layout/page-frame.d.ts +37 -0
- package/dist/web/layout/page-frame.js +79 -0
- package/dist/web/layout/scroll-area.d.ts +10 -0
- package/dist/web/layout/scroll-area.js +13 -0
- package/dist/web/layout/sheet.d.ts +32 -0
- package/dist/web/layout/sheet.js +68 -0
- package/dist/web/layout/simple-dialog.d.ts +11 -0
- package/dist/web/layout/simple-dialog.js +24 -0
- package/dist/web/layout/simple-table.d.ts +49 -0
- package/dist/web/layout/simple-table.js +58 -0
- package/dist/web/layout/tabs.d.ts +26 -0
- package/dist/web/layout/tabs.js +42 -0
- package/dist/web/layout/titled-modal.d.ts +32 -0
- package/dist/web/layout/titled-modal.js +58 -0
- package/dist/web/lib/css-vars.d.ts +14 -0
- package/dist/web/lib/css-vars.js +18 -0
- package/dist/web/lib/dialog.d.ts +13 -0
- package/dist/web/lib/dialog.js +0 -0
- package/dist/web/lib/form.d.ts +2 -0
- package/dist/web/lib/form.js +3 -0
- package/dist/web/lib/labels.d.ts +27 -0
- package/dist/web/lib/labels.js +29 -0
- package/dist/web/lib/modal-open-focus.d.ts +17 -0
- package/dist/web/lib/modal-open-focus.js +44 -0
- package/dist/web/lib/restore-focus.d.ts +12 -0
- package/dist/web/lib/restore-focus.js +32 -0
- package/dist/web/lib/utils.d.ts +4 -0
- package/dist/web/lib/utils.js +10 -0
- package/dist/web/styles.css +162 -0
- package/package.json +203 -4
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { cn } from "../lib/utils.js";
|
|
2
|
+
import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "../base/table.js";
|
|
3
|
+
import { isValidElement } from "react";
|
|
4
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
5
|
+
|
|
6
|
+
//#region src/web/layout/simple-table.tsx
|
|
7
|
+
/** Tailwind picks up class names statically, so they are kept in a table rather than assembled from strings. */
|
|
8
|
+
const hideBelowClass = {
|
|
9
|
+
sm: "hidden sm:table-cell",
|
|
10
|
+
md: "hidden md:table-cell",
|
|
11
|
+
lg: "hidden lg:table-cell"
|
|
12
|
+
};
|
|
13
|
+
const alignClass = {
|
|
14
|
+
left: "text-left",
|
|
15
|
+
right: "text-right",
|
|
16
|
+
center: "text-center"
|
|
17
|
+
};
|
|
18
|
+
/** The default rendering of a column with no `render`. It shows the row's `key` value only when it is a string, a number or an element. */
|
|
19
|
+
function cellValue(row, key) {
|
|
20
|
+
const value = Reflect.get(row, key);
|
|
21
|
+
if (typeof value === "string" || typeof value === "number" || isValidElement(value)) return value;
|
|
22
|
+
return null;
|
|
23
|
+
}
|
|
24
|
+
function SimpleTable({ columns, data, getRowKey, onRowClick, getRowProps, className, wrapperClassName }) {
|
|
25
|
+
return /* @__PURE__ */ jsxs(Table, {
|
|
26
|
+
className,
|
|
27
|
+
wrapperClassName,
|
|
28
|
+
children: [/* @__PURE__ */ jsx(TableHeader, { children: /* @__PURE__ */ jsx(TableRow, { children: columns.map((col) => /* @__PURE__ */ jsx(TableHead, {
|
|
29
|
+
className: cn(col.hideBelow && hideBelowClass[col.hideBelow], col.align && alignClass[col.align], col.headClassName),
|
|
30
|
+
children: col.header
|
|
31
|
+
}, col.key)) }) }), /* @__PURE__ */ jsx(TableBody, { children: data.map((row, index) => {
|
|
32
|
+
const { onClick, onKeyDown, tabIndex, className: rowClassName, ...rest } = getRowProps?.(row, index) ?? {};
|
|
33
|
+
return /* @__PURE__ */ jsx(TableRow, {
|
|
34
|
+
...rest,
|
|
35
|
+
tabIndex: tabIndex ?? (onRowClick ? 0 : void 0),
|
|
36
|
+
onClick: (event) => {
|
|
37
|
+
onRowClick?.(row);
|
|
38
|
+
onClick?.(event);
|
|
39
|
+
},
|
|
40
|
+
onKeyDown: (event) => {
|
|
41
|
+
if (onRowClick && event.target === event.currentTarget && (event.key === "Enter" || event.key === " ")) {
|
|
42
|
+
event.preventDefault();
|
|
43
|
+
onRowClick(row);
|
|
44
|
+
}
|
|
45
|
+
onKeyDown?.(event);
|
|
46
|
+
},
|
|
47
|
+
className: cn(onRowClick && "focus-visible:outline-ring cursor-pointer focus-visible:outline-2 focus-visible:-outline-offset-2", rowClassName),
|
|
48
|
+
children: columns.map((col) => /* @__PURE__ */ jsx(TableCell, {
|
|
49
|
+
className: cn(col.hideBelow && hideBelowClass[col.hideBelow], col.align && alignClass[col.align], col.mono && "font-mono tabular-nums", col.strong && "text-foreground font-medium", col.cellClassName),
|
|
50
|
+
children: col.render ? col.render(row) : cellValue(row, col.key)
|
|
51
|
+
}, col.key))
|
|
52
|
+
}, getRowKey ? getRowKey(row, index) : index);
|
|
53
|
+
}) })]
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
//#endregion
|
|
58
|
+
export { SimpleTable };
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
//#region src/web/layout/tabs.d.ts
|
|
3
|
+
export type TabType = {
|
|
4
|
+
[value: string]: {
|
|
5
|
+
name: string;
|
|
6
|
+
render?: () => React.ReactNode;
|
|
7
|
+
onSelect?: () => void;
|
|
8
|
+
};
|
|
9
|
+
};
|
|
10
|
+
export type TabsProps = {
|
|
11
|
+
tabs: TabType;
|
|
12
|
+
full?: boolean;
|
|
13
|
+
defaultValue?: string;
|
|
14
|
+
/** Controlled mode: specify when the value is managed from outside */
|
|
15
|
+
value?: string;
|
|
16
|
+
onValueChange?: (value: string) => void;
|
|
17
|
+
/** A slot on the right, laid out side by side at the same height as TabsList (used like a toolbar) */
|
|
18
|
+
trailing?: React.ReactNode;
|
|
19
|
+
/**
|
|
20
|
+
* DEV-E2E: adds a data-testid of `${testIdPrefix}-${value}` to each trigger.
|
|
21
|
+
* When omitted, no data-testid is output.
|
|
22
|
+
*/
|
|
23
|
+
testIdPrefix?: string;
|
|
24
|
+
};
|
|
25
|
+
export declare function Tabs(props: TabsProps): React.JSX.Element;
|
|
26
|
+
//#endregion
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { cn } from "../lib/utils.js";
|
|
2
|
+
import { Tabs as Tabs$1, TabsContent, TabsList, TabsTrigger } from "../base/tabs.js";
|
|
3
|
+
import { useState } from "react";
|
|
4
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
5
|
+
|
|
6
|
+
//#region src/web/layout/tabs.tsx
|
|
7
|
+
function Tabs(props) {
|
|
8
|
+
const [internalValue, setInternalValue] = useState(props.defaultValue ?? Object.keys(props.tabs)[0] ?? "");
|
|
9
|
+
const tabValue = props.value ?? internalValue;
|
|
10
|
+
const setTabValue = (value) => {
|
|
11
|
+
props.onValueChange?.(value);
|
|
12
|
+
if (props.value === void 0) setInternalValue(value);
|
|
13
|
+
};
|
|
14
|
+
const hasContent = Object.values(props.tabs).some((tab) => tab.render);
|
|
15
|
+
return /* @__PURE__ */ jsxs(Tabs$1, {
|
|
16
|
+
value: tabValue,
|
|
17
|
+
onValueChange: (value) => {
|
|
18
|
+
props.tabs[value]?.onSelect?.();
|
|
19
|
+
setTabValue(value);
|
|
20
|
+
},
|
|
21
|
+
className: hasContent ? "gap-4" : void 0,
|
|
22
|
+
children: [/* @__PURE__ */ jsxs("div", {
|
|
23
|
+
className: cn("flex items-center gap-2", props.trailing && "justify-between"),
|
|
24
|
+
children: [/* @__PURE__ */ jsx(TabsList, {
|
|
25
|
+
className: cn(props.full && "w-full"),
|
|
26
|
+
children: Object.keys(props.tabs).map((tab) => /* @__PURE__ */ jsx(TabsTrigger, {
|
|
27
|
+
value: tab,
|
|
28
|
+
className: "cursor-pointer",
|
|
29
|
+
...hasContent ? {} : { "aria-controls": void 0 },
|
|
30
|
+
"data-testid": props.testIdPrefix ? `${props.testIdPrefix}-${tab}` : void 0,
|
|
31
|
+
children: props.tabs[tab]?.name
|
|
32
|
+
}, tab))
|
|
33
|
+
}), props.trailing]
|
|
34
|
+
}), hasContent && /* @__PURE__ */ jsx(TabsContent, {
|
|
35
|
+
value: tabValue,
|
|
36
|
+
children: props.tabs[tabValue]?.render?.()
|
|
37
|
+
}, tabValue)]
|
|
38
|
+
});
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
//#endregion
|
|
42
|
+
export { Tabs };
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import * as React$1 from "react";
|
|
2
|
+
//#region src/web/layout/titled-modal.d.ts
|
|
3
|
+
type TitledModalProps = {
|
|
4
|
+
open: boolean;
|
|
5
|
+
onOpenChange: (open: boolean) => void;
|
|
6
|
+
title: React$1.ReactNode;
|
|
7
|
+
/** A description placed at the top of the body. It also serves as the description for assistive technology. */
|
|
8
|
+
description?: React$1.ReactNode;
|
|
9
|
+
children?: React$1.ReactNode;
|
|
10
|
+
/** Actions laid out right-aligned. When present, a 1px divider is added above them. */
|
|
11
|
+
footer?: React$1.ReactNode;
|
|
12
|
+
/** Accessible name of the close button. */
|
|
13
|
+
closeLabel?: string;
|
|
14
|
+
/** The content of the × (lucide's X when omitted). */
|
|
15
|
+
closeIcon?: React$1.ReactNode;
|
|
16
|
+
className?: string;
|
|
17
|
+
"data-testid"?: string;
|
|
18
|
+
};
|
|
19
|
+
/**
|
|
20
|
+
* A larger modal with a heading and a ×.
|
|
21
|
+
*
|
|
22
|
+
* Overlay surface (no border or ring, shadow only), width 560. A thin 1px divider under the heading (16/24 semibold) and ×, the body is p-5,
|
|
23
|
+
* and if there is a footer, a divider is drawn above it and the actions are laid out right-aligned. The caller holds the open state (controlled only).
|
|
24
|
+
*
|
|
25
|
+
* While shown it carries `data-modal-open`, so floating persistent UI can be hidden with CSS
|
|
26
|
+
*
|
|
27
|
+
* The initial focus on open goes to the first action only when it was opened by keyboard, and otherwise to the modal body
|
|
28
|
+
* (so that no ring appears on the first button when opened with the mouse; `useModalOpenAutoFocus`).
|
|
29
|
+
*/
|
|
30
|
+
export declare function TitledModal({ open, onOpenChange, title, description, children, footer, closeLabel, closeIcon, className, "data-testid": testId }: TitledModalProps): React$1.JSX.Element;
|
|
31
|
+
//#endregion
|
|
32
|
+
export {};
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { cn } from "../lib/utils.js";
|
|
2
|
+
import { CLOSE_LABEL } from "../lib/labels.js";
|
|
3
|
+
import { Dialog, DialogClose, DialogContent, DialogDescription, DialogTitle } from "../base/dialog.js";
|
|
4
|
+
import { useModalOpenAutoFocus } from "../lib/modal-open-focus.js";
|
|
5
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
6
|
+
import { XIcon } from "lucide-react";
|
|
7
|
+
|
|
8
|
+
//#region src/web/layout/titled-modal.tsx
|
|
9
|
+
/**
|
|
10
|
+
* A larger modal with a heading and a ×.
|
|
11
|
+
*
|
|
12
|
+
* Overlay surface (no border or ring, shadow only), width 560. A thin 1px divider under the heading (16/24 semibold) and ×, the body is p-5,
|
|
13
|
+
* and if there is a footer, a divider is drawn above it and the actions are laid out right-aligned. The caller holds the open state (controlled only).
|
|
14
|
+
*
|
|
15
|
+
* While shown it carries `data-modal-open`, so floating persistent UI can be hidden with CSS
|
|
16
|
+
*
|
|
17
|
+
* The initial focus on open goes to the first action only when it was opened by keyboard, and otherwise to the modal body
|
|
18
|
+
* (so that no ring appears on the first button when opened with the mouse; `useModalOpenAutoFocus`).
|
|
19
|
+
*/
|
|
20
|
+
function TitledModal({ open, onOpenChange, title, description, children, footer, closeLabel = CLOSE_LABEL, closeIcon, className, "data-testid": testId }) {
|
|
21
|
+
const { contentRef, onOpenAutoFocus } = useModalOpenAutoFocus();
|
|
22
|
+
return /* @__PURE__ */ jsx(Dialog, {
|
|
23
|
+
open,
|
|
24
|
+
onOpenChange,
|
|
25
|
+
children: /* @__PURE__ */ jsxs(DialogContent, {
|
|
26
|
+
ref: contentRef,
|
|
27
|
+
onOpenAutoFocus,
|
|
28
|
+
showCloseButton: false,
|
|
29
|
+
"data-modal-open": "",
|
|
30
|
+
"data-testid": testId,
|
|
31
|
+
className: cn("flex max-h-[calc(100vh-4rem)] w-[560px] outline-none max-w-[calc(100%-2rem)] flex-col gap-0 p-0 sm:max-w-[560px]", className),
|
|
32
|
+
children: [
|
|
33
|
+
/* @__PURE__ */ jsxs("header", {
|
|
34
|
+
className: "flex shrink-0 items-center justify-between gap-4 border-b border-border px-5 py-3.5",
|
|
35
|
+
children: [/* @__PURE__ */ jsx(DialogTitle, { children: title }), /* @__PURE__ */ jsx(DialogClose, {
|
|
36
|
+
"aria-label": closeLabel,
|
|
37
|
+
className: "text-muted-foreground hover:bg-accent hover:text-foreground grid size-7 shrink-0 cursor-pointer place-items-center rounded-control outline-none transition-colors focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-popover [&_svg]:size-4",
|
|
38
|
+
children: closeIcon ?? /* @__PURE__ */ jsx(XIcon, {})
|
|
39
|
+
})]
|
|
40
|
+
}),
|
|
41
|
+
/* @__PURE__ */ jsxs("div", {
|
|
42
|
+
className: "min-h-0 flex-1 overflow-y-auto p-5",
|
|
43
|
+
children: [description && /* @__PURE__ */ jsx(DialogDescription, {
|
|
44
|
+
className: "mb-4",
|
|
45
|
+
children: description
|
|
46
|
+
}), children]
|
|
47
|
+
}),
|
|
48
|
+
footer && /* @__PURE__ */ jsx("footer", {
|
|
49
|
+
className: "flex shrink-0 items-center justify-end gap-2 border-t border-border px-5 py-3.5",
|
|
50
|
+
children: footer
|
|
51
|
+
})
|
|
52
|
+
]
|
|
53
|
+
})
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
//#endregion
|
|
58
|
+
export { TitledModal };
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { CSSProperties } from "react";
|
|
2
|
+
//#region src/web/lib/css-vars.d.ts
|
|
3
|
+
/** A style part that holds only CSS custom properties (`--foo`). */
|
|
4
|
+
export type CssVars = Record<`--${string}`, string | number>;
|
|
5
|
+
/**
|
|
6
|
+
* Turns CSS custom properties into something that can be passed to `style`.
|
|
7
|
+
*
|
|
8
|
+
* `CSSProperties` does not know about `--foo`, so passing an object literal directly makes tsc
|
|
9
|
+
* reject it as an excess property. Silencing it with `as CSSProperties` removes the type check, so
|
|
10
|
+
* it is received with template-literal-type keys and returned as `CSSProperties`.
|
|
11
|
+
* `overrides` wins (a `style` passed by the caller can override the default variables).
|
|
12
|
+
*/
|
|
13
|
+
export declare function cssVars(vars: CssVars, overrides?: CSSProperties): CSSProperties;
|
|
14
|
+
//#endregion
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
//#region src/web/lib/css-vars.ts
|
|
2
|
+
/**
|
|
3
|
+
* Turns CSS custom properties into something that can be passed to `style`.
|
|
4
|
+
*
|
|
5
|
+
* `CSSProperties` does not know about `--foo`, so passing an object literal directly makes tsc
|
|
6
|
+
* reject it as an excess property. Silencing it with `as CSSProperties` removes the type check, so
|
|
7
|
+
* it is received with template-literal-type keys and returned as `CSSProperties`.
|
|
8
|
+
* `overrides` wins (a `style` passed by the caller can override the default variables).
|
|
9
|
+
*/
|
|
10
|
+
function cssVars(vars, overrides) {
|
|
11
|
+
return {
|
|
12
|
+
...vars,
|
|
13
|
+
...overrides
|
|
14
|
+
};
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
//#endregion
|
|
18
|
+
export { cssVars };
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
//#region src/web/lib/dialog.d.ts
|
|
2
|
+
/**
|
|
3
|
+
* The shape (a structural type) in which `Modal` / `Sheet` receive the open/close state.
|
|
4
|
+
* You can build it yourself with `useState`, or pass the return value of an app-side hook with an unsaved-changes guard as is
|
|
5
|
+
* (love3 depends on neither i18n nor forms, so it does not include the hook itself).
|
|
6
|
+
*/
|
|
7
|
+
export type DialogController = {
|
|
8
|
+
isOpen: boolean;
|
|
9
|
+
close: () => void;
|
|
10
|
+
/** Decides whether a click outside may close it. When you want to insert a confirmation, handle it here (by default it is treated the same as `close`) */
|
|
11
|
+
onOutsideClick: () => void;
|
|
12
|
+
};
|
|
13
|
+
//#endregion
|
|
File without changes
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import { BaseInputProps, FieldInputProps, fieldContext, formContext, useFieldContext, useFieldValue, useFormContext, useOptionalFieldContext } from "../../shared/form.js";
|
|
2
|
+
export { type BaseInputProps, type FieldInputProps, fieldContext, formContext, useFieldContext, useFieldValue, useFormContext, useOptionalFieldContext };
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
//#region src/web/lib/labels.d.ts
|
|
2
|
+
/** Close button (Dialog, Sheet, TitledModal) */
|
|
3
|
+
export declare const CLOSE_LABEL = "閉じる";
|
|
4
|
+
/** Help trigger (atom/help, FormLabel of base/form) */
|
|
5
|
+
export declare const HELP_LABEL = "ヘルプ";
|
|
6
|
+
/** Loading indicator (Spinner) */
|
|
7
|
+
export declare const LOADING_LABEL = "読み込み中";
|
|
8
|
+
/** The pagination nav and the buttons inside it */
|
|
9
|
+
export declare const PAGINATION_LABEL = "ページネーション";
|
|
10
|
+
export declare const PREVIOUS_PAGE_LABEL = "前のページへ";
|
|
11
|
+
export declare const NEXT_PAGE_LABEL = "次のページへ";
|
|
12
|
+
/** Page numbers that are omitted in the middle */
|
|
13
|
+
export declare const MORE_PAGES_LABEL = "他のページ";
|
|
14
|
+
/** The breadcrumb nav and the omitted levels in the middle */
|
|
15
|
+
export declare const BREADCRUMB_LABEL = "パンくずリスト";
|
|
16
|
+
export declare const MORE_LEVELS_LABEL = "他の階層";
|
|
17
|
+
/** Column names of the line chart's table alternative (a table hidden for screen readers), and the default name */
|
|
18
|
+
export declare const LINE_CHART_TABLE_SERIES_LABEL = "系列";
|
|
19
|
+
export declare const LINE_CHART_TABLE_POINT_LABEL = "見出し";
|
|
20
|
+
export declare const LINE_CHART_TABLE_VALUE_LABEL = "値";
|
|
21
|
+
export declare const LINE_CHART_DEFAULT_LABEL = "折れ線チャート";
|
|
22
|
+
export declare const LINE_CHART_SINGLE_SERIES_LABEL = "値";
|
|
23
|
+
export declare const LINE_CHART_EMPTY_TEXT = "データがありません";
|
|
24
|
+
/** Notes added to the table for screen readers (when non-numeric points were dropped, or when many points were sampled) */
|
|
25
|
+
export declare const lineChartTableDropped: (count: number) => string;
|
|
26
|
+
export declare const lineChartTableThinned: (shown: number, total: number) => string;
|
|
27
|
+
//#endregion
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
//#region src/web/lib/labels.ts
|
|
2
|
+
/** Close button (Dialog, Sheet, TitledModal) */
|
|
3
|
+
const CLOSE_LABEL = "閉じる";
|
|
4
|
+
/** Help trigger (atom/help, FormLabel of base/form) */
|
|
5
|
+
const HELP_LABEL = "ヘルプ";
|
|
6
|
+
/** Loading indicator (Spinner) */
|
|
7
|
+
const LOADING_LABEL = "読み込み中";
|
|
8
|
+
/** The pagination nav and the buttons inside it */
|
|
9
|
+
const PAGINATION_LABEL = "ページネーション";
|
|
10
|
+
const PREVIOUS_PAGE_LABEL = "前のページへ";
|
|
11
|
+
const NEXT_PAGE_LABEL = "次のページへ";
|
|
12
|
+
/** Page numbers that are omitted in the middle */
|
|
13
|
+
const MORE_PAGES_LABEL = "他のページ";
|
|
14
|
+
/** The breadcrumb nav and the omitted levels in the middle */
|
|
15
|
+
const BREADCRUMB_LABEL = "パンくずリスト";
|
|
16
|
+
const MORE_LEVELS_LABEL = "他の階層";
|
|
17
|
+
/** Column names of the line chart's table alternative (a table hidden for screen readers), and the default name */
|
|
18
|
+
const LINE_CHART_TABLE_SERIES_LABEL = "系列";
|
|
19
|
+
const LINE_CHART_TABLE_POINT_LABEL = "見出し";
|
|
20
|
+
const LINE_CHART_TABLE_VALUE_LABEL = "値";
|
|
21
|
+
const LINE_CHART_DEFAULT_LABEL = "折れ線チャート";
|
|
22
|
+
const LINE_CHART_SINGLE_SERIES_LABEL = "値";
|
|
23
|
+
const LINE_CHART_EMPTY_TEXT = "データがありません";
|
|
24
|
+
/** Notes added to the table for screen readers (when non-numeric points were dropped, or when many points were sampled) */
|
|
25
|
+
const lineChartTableDropped = (count) => `数でない値の点 ${count.toString()} 件は除いています。`;
|
|
26
|
+
const lineChartTableThinned = (shown, total) => `点が多いので、${total.toString()} 点のうち ${shown.toString()} 点を等間隔に抜き出しています。`;
|
|
27
|
+
|
|
28
|
+
//#endregion
|
|
29
|
+
export { BREADCRUMB_LABEL, CLOSE_LABEL, HELP_LABEL, LINE_CHART_DEFAULT_LABEL, LINE_CHART_EMPTY_TEXT, LINE_CHART_SINGLE_SERIES_LABEL, LINE_CHART_TABLE_POINT_LABEL, LINE_CHART_TABLE_SERIES_LABEL, LINE_CHART_TABLE_VALUE_LABEL, LOADING_LABEL, MORE_LEVELS_LABEL, MORE_PAGES_LABEL, NEXT_PAGE_LABEL, PAGINATION_LABEL, PREVIOUS_PAGE_LABEL, lineChartTableDropped, lineChartTableThinned };
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
//#region src/web/lib/modal-open-focus.d.ts
|
|
2
|
+
/**
|
|
3
|
+
* Changes where the initial focus goes when a modal opens, depending on how it was opened.
|
|
4
|
+
*
|
|
5
|
+
* - Opened by keyboard: put it on the first action (a button), as Radix does by default. The focus ring also shows
|
|
6
|
+
* - Opened by pointer / opened with no interaction (something shown at start-up, etc.): put it on the modal body.
|
|
7
|
+
* No ring appears on the first button (WebKit applies `:focus-visible` even to script-driven focus,
|
|
8
|
+
* so putting it on the button would show a ring). Focus is inside the modal, so Tab moves to the first action,
|
|
9
|
+
* Esc closes it, and assistive technology announces it as a dialog
|
|
10
|
+
*
|
|
11
|
+
* Pass the returned `contentRef` and `onOpenAutoFocus` to DialogContent. Give the body `outline-none`.
|
|
12
|
+
*/
|
|
13
|
+
export declare function useModalOpenAutoFocus(): {
|
|
14
|
+
contentRef: import("react").RefObject<HTMLDivElement | null>;
|
|
15
|
+
onOpenAutoFocus: (event: Event) => void;
|
|
16
|
+
};
|
|
17
|
+
//#endregion
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { useCallback, useRef } from "react";
|
|
2
|
+
|
|
3
|
+
//#region src/web/lib/modal-open-focus.ts
|
|
4
|
+
let lastModality = "unknown";
|
|
5
|
+
let listening = false;
|
|
6
|
+
/**
|
|
7
|
+
* Remembers whether the previous action was by keyboard or by pointer (attached to the document only once).
|
|
8
|
+
* It listens in the capture phase, so it is not missed even if stopPropagation is called along the way.
|
|
9
|
+
*/
|
|
10
|
+
function listenModality() {
|
|
11
|
+
if (listening || typeof document === "undefined") return;
|
|
12
|
+
listening = true;
|
|
13
|
+
document.addEventListener("keydown", (e) => {
|
|
14
|
+
if (e.metaKey || e.altKey || e.ctrlKey) return;
|
|
15
|
+
lastModality = "keyboard";
|
|
16
|
+
}, true);
|
|
17
|
+
document.addEventListener("pointerdown", () => lastModality = "pointer", true);
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Changes where the initial focus goes when a modal opens, depending on how it was opened.
|
|
21
|
+
*
|
|
22
|
+
* - Opened by keyboard: put it on the first action (a button), as Radix does by default. The focus ring also shows
|
|
23
|
+
* - Opened by pointer / opened with no interaction (something shown at start-up, etc.): put it on the modal body.
|
|
24
|
+
* No ring appears on the first button (WebKit applies `:focus-visible` even to script-driven focus,
|
|
25
|
+
* so putting it on the button would show a ring). Focus is inside the modal, so Tab moves to the first action,
|
|
26
|
+
* Esc closes it, and assistive technology announces it as a dialog
|
|
27
|
+
*
|
|
28
|
+
* Pass the returned `contentRef` and `onOpenAutoFocus` to DialogContent. Give the body `outline-none`.
|
|
29
|
+
*/
|
|
30
|
+
function useModalOpenAutoFocus() {
|
|
31
|
+
listenModality();
|
|
32
|
+
const contentRef = useRef(null);
|
|
33
|
+
return {
|
|
34
|
+
contentRef,
|
|
35
|
+
onOpenAutoFocus: useCallback((event) => {
|
|
36
|
+
if (lastModality === "keyboard") return;
|
|
37
|
+
event.preventDefault();
|
|
38
|
+
contentRef.current?.focus({ preventScroll: true });
|
|
39
|
+
}, [])
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
//#endregion
|
|
44
|
+
export { useModalOpenAutoFocus };
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
//#region src/web/lib/restore-focus.d.ts
|
|
2
|
+
/**
|
|
3
|
+
* Radix's Dialog returns focus after closing only to `DialogTrigger`. love3's Modal, Sheet and AlertModal keep `open` in state
|
|
4
|
+
* (there is no trigger), so as is, focus falls to `body` (keyboard and assistive-technology users lose track of where they opened it).
|
|
5
|
+
* It remembers the element that had focus when the dialog opened and returns focus there on close. When there is no remembered element, such as when it was opened with `DialogTrigger`, or
|
|
6
|
+
* when the caller called `preventDefault`, it does nothing (it leaves it to Radix's default behaviour).
|
|
7
|
+
*/
|
|
8
|
+
export declare function useRestoreFocus(onOpenAutoFocus: ((event: Event) => void) | undefined, onCloseAutoFocus: ((event: Event) => void) | undefined): {
|
|
9
|
+
onOpenAutoFocus: (event: Event) => void;
|
|
10
|
+
onCloseAutoFocus: (event: Event) => void;
|
|
11
|
+
};
|
|
12
|
+
//#endregion
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { useRef } from "react";
|
|
2
|
+
|
|
3
|
+
//#region src/web/lib/restore-focus.ts
|
|
4
|
+
/**
|
|
5
|
+
* Radix's Dialog returns focus after closing only to `DialogTrigger`. love3's Modal, Sheet and AlertModal keep `open` in state
|
|
6
|
+
* (there is no trigger), so as is, focus falls to `body` (keyboard and assistive-technology users lose track of where they opened it).
|
|
7
|
+
* It remembers the element that had focus when the dialog opened and returns focus there on close. When there is no remembered element, such as when it was opened with `DialogTrigger`, or
|
|
8
|
+
* when the caller called `preventDefault`, it does nothing (it leaves it to Radix's default behaviour).
|
|
9
|
+
*/
|
|
10
|
+
function useRestoreFocus(onOpenAutoFocus, onCloseAutoFocus) {
|
|
11
|
+
const previous = useRef(null);
|
|
12
|
+
return {
|
|
13
|
+
onOpenAutoFocus: (event) => {
|
|
14
|
+
const active = document.activeElement;
|
|
15
|
+
previous.current = active instanceof HTMLElement && active !== document.body ? active : null;
|
|
16
|
+
onOpenAutoFocus?.(event);
|
|
17
|
+
},
|
|
18
|
+
onCloseAutoFocus: (event) => {
|
|
19
|
+
onCloseAutoFocus?.(event);
|
|
20
|
+
if (event.defaultPrevented) return;
|
|
21
|
+
const target = previous.current;
|
|
22
|
+
previous.current = null;
|
|
23
|
+
if (target?.isConnected) {
|
|
24
|
+
event.preventDefault();
|
|
25
|
+
target.focus();
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
//#endregion
|
|
32
|
+
export { useRestoreFocus };
|
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Web styles for love3 (`@lt-three/love3/web/styles.css`). Import it once, at the entry of your app's CSS.
|
|
3
|
+
*
|
|
4
|
+
* @import "@lt-three/love3/web/styles.css";
|
|
5
|
+
*
|
|
6
|
+
* It contains Tailwind v4 itself, the fonts, the animations, the tokens (`@lt-three/love3/styles.css`) and the base styles.
|
|
7
|
+
* The token values live in `src/tokens/definition.ts` (the SSOT). No tokens are written here.
|
|
8
|
+
* `.theme-light` only lightens the raw tokens (such as `bg-paper` / `text-ink-900`).
|
|
9
|
+
* Semantic classes (such as `bg-background` / `bg-primary`) do not get lighter, so components do not use them
|
|
10
|
+
* (to keep a public page always light, use raw-token classes or redeclare the variables on the consuming side).
|
|
11
|
+
*/
|
|
12
|
+
@import "tailwindcss/theme.css" layer(theme);
|
|
13
|
+
@import "tailwindcss/preflight.css" layer(base);
|
|
14
|
+
@import "tailwindcss/utilities.css";
|
|
15
|
+
|
|
16
|
+
/* Latin text uses self-hosted Geist (Latin only, so it is light). Japanese text uses the Yu Gothic fonts that ship with the OS, and falls back to sans-serif where they are not installed */
|
|
17
|
+
@import "@fontsource-variable/geist";
|
|
18
|
+
/* Numbers, IDs, code and data tables use Geist Mono */
|
|
19
|
+
@import "@fontsource-variable/geist-mono";
|
|
20
|
+
|
|
21
|
+
@import "tw-animate-css";
|
|
22
|
+
|
|
23
|
+
/* The token definitions (@theme, light, dark). It is generated, so to change a value edit definition.ts */
|
|
24
|
+
@import "@lt-three/love3/styles.css";
|
|
25
|
+
|
|
26
|
+
/*
|
|
27
|
+
* Picks up the Tailwind classes used by love3's components (components are *.tsx; the *.ts files in lib have no classes).
|
|
28
|
+
* node_modules (through a workspace symlink) is outside automatic detection, so it is stated explicitly here.
|
|
29
|
+
* The `@source` path is relative to the location of this CSS file.
|
|
30
|
+
* In the published build (`dist/`) the components are `*.js`, so both `tsx` and `js` are picked up (the same file is placed in both src and dist).
|
|
31
|
+
* The consuming app's own sources are left to automatic detection based on the bundler's project root
|
|
32
|
+
* (the Vite root, or the cwd for PostCSS); the location of this CSS file does not matter.
|
|
33
|
+
*/
|
|
34
|
+
@source "./**/*.{tsx,js}";
|
|
35
|
+
@source not "./**/*.test.tsx";
|
|
36
|
+
|
|
37
|
+
/*
|
|
38
|
+
* Notifications (sonner): overlay surface + thin 1px border + soft shadow (shadow-md). Corner radius is the surface step.
|
|
39
|
+
* The state is shown by the icon colour, not by a left border (the text stays in the normal colour).
|
|
40
|
+
* sonner injects its styles at runtime (at the same specificity as the default `[data-sonner-toast][data-styled=true]`, load order would make us lose), so
|
|
41
|
+
* `[data-sonner-toaster]` and `[data-styled="true"]` are added to raise the specificity by one step and win reliably (!important is not used).
|
|
42
|
+
*/
|
|
43
|
+
[data-sonner-toaster] [data-sonner-toast][data-styled="true"] {
|
|
44
|
+
box-shadow: var(--shadow-md);
|
|
45
|
+
font-size: 13px;
|
|
46
|
+
}
|
|
47
|
+
[data-sonner-toaster] [data-sonner-toast][data-styled="true"][data-type="success"] [data-icon] {
|
|
48
|
+
color: var(--state-success);
|
|
49
|
+
}
|
|
50
|
+
[data-sonner-toaster] [data-sonner-toast][data-styled="true"][data-type="error"] [data-icon] {
|
|
51
|
+
color: var(--state-error);
|
|
52
|
+
}
|
|
53
|
+
[data-sonner-toaster] [data-sonner-toast][data-styled="true"][data-type="warning"] [data-icon] {
|
|
54
|
+
color: var(--state-warning);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
@layer base {
|
|
58
|
+
html {
|
|
59
|
+
font-family: var(--font-primary), sans-serif;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
input[type="number"]::-webkit-outer-spin-button,
|
|
63
|
+
input[type="number"]::-webkit-inner-spin-button,
|
|
64
|
+
input[type="number"] {
|
|
65
|
+
-webkit-appearance: none;
|
|
66
|
+
-moz-appearance: textfield !important;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
* {
|
|
70
|
+
@apply border-border outline-ring/50;
|
|
71
|
+
}
|
|
72
|
+
body {
|
|
73
|
+
@apply bg-background text-foreground;
|
|
74
|
+
/* Japanese breaks lines at bunsetsu (phrase) boundaries and does not wrap in the middle of a word */
|
|
75
|
+
word-break: auto-phrase;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/* Headings use the same Geist as the body. Flush left, with slightly tightened letter spacing. */
|
|
79
|
+
h1,
|
|
80
|
+
h2,
|
|
81
|
+
h3,
|
|
82
|
+
h4,
|
|
83
|
+
h5,
|
|
84
|
+
h6 {
|
|
85
|
+
font-family: var(--font-sans, "Geist Variable", sans-serif);
|
|
86
|
+
font-weight: 700;
|
|
87
|
+
letter-spacing: -0.01em;
|
|
88
|
+
/* Headings also break at phrase boundaries. Intentional line breaks are controlled in the copy itself (a \n in the text) */
|
|
89
|
+
word-break: auto-phrase;
|
|
90
|
+
}
|
|
91
|
+
h1 {
|
|
92
|
+
@apply text-3xl my-2;
|
|
93
|
+
letter-spacing: -0.02em;
|
|
94
|
+
}
|
|
95
|
+
h2 {
|
|
96
|
+
@apply text-2xl my-2;
|
|
97
|
+
font-weight: 500;
|
|
98
|
+
}
|
|
99
|
+
h3 {
|
|
100
|
+
@apply text-xl my-2;
|
|
101
|
+
font-weight: 500;
|
|
102
|
+
}
|
|
103
|
+
h4 {
|
|
104
|
+
@apply text-lg my-2;
|
|
105
|
+
font-weight: 500;
|
|
106
|
+
}
|
|
107
|
+
h5 {
|
|
108
|
+
@apply text-base my-2;
|
|
109
|
+
font-weight: 500;
|
|
110
|
+
}
|
|
111
|
+
h6 {
|
|
112
|
+
@apply text-sm my-1;
|
|
113
|
+
font-weight: 500;
|
|
114
|
+
color: var(--muted-foreground);
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/* Numbers / IDs / code / data use tabular (monospaced) figures */
|
|
118
|
+
code,
|
|
119
|
+
kbd,
|
|
120
|
+
samp,
|
|
121
|
+
pre,
|
|
122
|
+
[data-mono] {
|
|
123
|
+
font-variant-numeric: tabular-nums;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
/* Text selection is a pale indigo surface. The text colour is unchanged */
|
|
127
|
+
::selection {
|
|
128
|
+
background: color-mix(in srgb, var(--action) 30%, transparent);
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
/*
|
|
133
|
+
* States of a text link.
|
|
134
|
+
* default: text only
|
|
135
|
+
* hover: a 1px underline
|
|
136
|
+
* active: knock out the text fill and draw a 1px outline on the outside + an underline (while pressed)
|
|
137
|
+
* The colour follows currentColor, so it works the same way when the text colour changes, such as white text on top of a wallpaper.
|
|
138
|
+
* hover switches instantly (no transition).
|
|
139
|
+
*
|
|
140
|
+
* An "outside-only outline" cannot be made with -webkit-text-stroke (it is drawn centred on the glyph edge), so
|
|
141
|
+
* it is made with the SVG filter #text-outside-stroke (grow the text by 1px and knock out the original text).
|
|
142
|
+
* The consuming app places the filter definition in the DOM once.
|
|
143
|
+
*/
|
|
144
|
+
@utility text-outline {
|
|
145
|
+
filter: url(#text-outside-stroke);
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
@utility text-link {
|
|
149
|
+
text-underline-position: from-font;
|
|
150
|
+
text-decoration-thickness: 1px;
|
|
151
|
+
|
|
152
|
+
@media (hover: hover) {
|
|
153
|
+
&:hover {
|
|
154
|
+
text-decoration-line: underline;
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
&:active {
|
|
159
|
+
text-decoration-line: underline;
|
|
160
|
+
filter: url(#text-outside-stroke);
|
|
161
|
+
}
|
|
162
|
+
}
|