@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,48 @@
|
|
|
1
|
+
import { useTokenColor } from "../lib/token-color.js";
|
|
2
|
+
import { cn } from "../lib/utils.js";
|
|
3
|
+
import { Text as Text$1 } from "../base/text.js";
|
|
4
|
+
import { Pressable, View } from "react-native";
|
|
5
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
6
|
+
|
|
7
|
+
//#region src/native/layout/tab-bar.tsx
|
|
8
|
+
/**
|
|
9
|
+
* Switcher for top-level destinations placed at the bottom of the screen (3 to 5 items). Distinct from `layout/tabs` (underlined tabs that switch the body).
|
|
10
|
+
* The selected item uses `text-action-ink` (AA even on dark surfaces), and the selected state is shown by weight (`font-semibold`) as well as color.
|
|
11
|
+
* Each item's press target is at least 56px tall (above the 44pt real-device guideline). **The bottom safe area (home indicator) is the app's responsibility**
|
|
12
|
+
* (pass `pb-*` in `className`, or place it inside an outer `SafeAreaView`).
|
|
13
|
+
*/
|
|
14
|
+
function TabBar({ items, value, onValueChange, accessibilityLabel = "メイン", className }) {
|
|
15
|
+
const activeColor = useTokenColor("--action-ink");
|
|
16
|
+
const inactiveColor = useTokenColor("--muted-foreground");
|
|
17
|
+
return /* @__PURE__ */ jsx(View, {
|
|
18
|
+
role: "navigation",
|
|
19
|
+
"aria-label": accessibilityLabel,
|
|
20
|
+
className: cn("border-line bg-card border-t", className),
|
|
21
|
+
children: /* @__PURE__ */ jsx(View, {
|
|
22
|
+
role: "tablist",
|
|
23
|
+
className: "flex-row",
|
|
24
|
+
children: items.map((item) => {
|
|
25
|
+
const active = item.value === value;
|
|
26
|
+
const color = active ? activeColor : inactiveColor;
|
|
27
|
+
return /* @__PURE__ */ jsxs(Pressable, {
|
|
28
|
+
role: "tab",
|
|
29
|
+
"aria-selected": active,
|
|
30
|
+
"aria-label": item.label,
|
|
31
|
+
onPress: () => onValueChange(item.value),
|
|
32
|
+
className: "min-h-14 flex-1 items-center justify-center gap-1 px-2 py-2",
|
|
33
|
+
children: [item.icon ? item.icon({
|
|
34
|
+
active,
|
|
35
|
+
color
|
|
36
|
+
}) : null, /* @__PURE__ */ jsx(Text$1, {
|
|
37
|
+
numberOfLines: 1,
|
|
38
|
+
className: cn("text-xs", active ? "text-action-ink font-semibold" : "text-muted-foreground font-normal"),
|
|
39
|
+
children: item.label
|
|
40
|
+
})]
|
|
41
|
+
}, item.value);
|
|
42
|
+
})
|
|
43
|
+
})
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
//#endregion
|
|
48
|
+
export { TabBar };
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { ReactNode } from "react";
|
|
2
|
+
//#region src/native/layout/tabs.d.ts
|
|
3
|
+
export type TabType = {
|
|
4
|
+
[value: string]: {
|
|
5
|
+
name: string;
|
|
6
|
+
render?: () => ReactNode;
|
|
7
|
+
onSelect?: () => void;
|
|
8
|
+
};
|
|
9
|
+
};
|
|
10
|
+
export type TabsProps = {
|
|
11
|
+
tabs: TabType;
|
|
12
|
+
defaultValue?: string;
|
|
13
|
+
/** Controlled mode: specify it to manage the selected value from outside. */
|
|
14
|
+
value?: string;
|
|
15
|
+
onValueChange?: (value: string) => void;
|
|
16
|
+
/** Slot placed at the right end of the same row as the tab strip (used like a toolbar). */
|
|
17
|
+
trailing?: ReactNode;
|
|
18
|
+
className?: string;
|
|
19
|
+
};
|
|
20
|
+
/**
|
|
21
|
+
* Tabs with the same record-driven API as the web `layout/tabs`.
|
|
22
|
+
* Visually they are flush-left underlined tabs matching the web base/tabs:
|
|
23
|
+
* a 2px indigo underline for the active tab sits on a shared faint rule.
|
|
24
|
+
*
|
|
25
|
+
* They scroll horizontally so they never wrap as the tab count grows (instead of web's `flex-wrap`).
|
|
26
|
+
*/
|
|
27
|
+
export declare function Tabs({ tabs, defaultValue, value, onValueChange, trailing, className }: TabsProps): import("react").JSX.Element;
|
|
28
|
+
//#endregion
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { cn } from "../lib/utils.js";
|
|
2
|
+
import { Text as Text$1 } from "../base/text.js";
|
|
3
|
+
import { useState } from "react";
|
|
4
|
+
import { Pressable, ScrollView, View } from "react-native";
|
|
5
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
6
|
+
|
|
7
|
+
//#region src/native/layout/tabs.tsx
|
|
8
|
+
/**
|
|
9
|
+
* Tabs with the same record-driven API as the web `layout/tabs`.
|
|
10
|
+
* Visually they are flush-left underlined tabs matching the web base/tabs:
|
|
11
|
+
* a 2px indigo underline for the active tab sits on a shared faint rule.
|
|
12
|
+
*
|
|
13
|
+
* They scroll horizontally so they never wrap as the tab count grows (instead of web's `flex-wrap`).
|
|
14
|
+
*/
|
|
15
|
+
function Tabs({ tabs, defaultValue, value, onValueChange, trailing, className }) {
|
|
16
|
+
const keys = Object.keys(tabs);
|
|
17
|
+
const [internalValue, setInternalValue] = useState(defaultValue ?? keys[0] ?? "");
|
|
18
|
+
const active = value ?? internalValue;
|
|
19
|
+
const select = (next) => {
|
|
20
|
+
tabs[next]?.onSelect?.();
|
|
21
|
+
onValueChange?.(next);
|
|
22
|
+
if (value === void 0) setInternalValue(next);
|
|
23
|
+
};
|
|
24
|
+
const hasContent = Object.values(tabs).some((tab) => tab.render);
|
|
25
|
+
return /* @__PURE__ */ jsxs(View, {
|
|
26
|
+
className: cn(hasContent && "gap-4", className),
|
|
27
|
+
children: [/* @__PURE__ */ jsxs(View, {
|
|
28
|
+
role: "tablist",
|
|
29
|
+
className: "flex-row items-center border-b border-line",
|
|
30
|
+
children: [/* @__PURE__ */ jsx(ScrollView, {
|
|
31
|
+
horizontal: true,
|
|
32
|
+
showsHorizontalScrollIndicator: false,
|
|
33
|
+
contentContainerStyle: { gap: 24 },
|
|
34
|
+
children: keys.map((key) => {
|
|
35
|
+
const on = key === active;
|
|
36
|
+
return /* @__PURE__ */ jsxs(Pressable, {
|
|
37
|
+
role: "tab",
|
|
38
|
+
"aria-selected": on,
|
|
39
|
+
onPress: () => select(key),
|
|
40
|
+
className: "-mb-px pb-2.5",
|
|
41
|
+
hitSlop: 12,
|
|
42
|
+
children: [/* @__PURE__ */ jsx(Text$1, {
|
|
43
|
+
className: cn("text-sm", on ? "text-foreground font-medium" : "text-muted-foreground font-normal"),
|
|
44
|
+
children: tabs[key]?.name
|
|
45
|
+
}), on ? /* @__PURE__ */ jsx(View, { className: "absolute inset-x-0 bottom-0 h-0.5 bg-action" }) : null]
|
|
46
|
+
}, key);
|
|
47
|
+
})
|
|
48
|
+
}), trailing]
|
|
49
|
+
}), hasContent ? tabs[active]?.render?.() : null]
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
//#endregion
|
|
54
|
+
export { Tabs };
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { Tuning, TuningCatalog } from "../../tuning/types.js";
|
|
2
|
+
//#region src/native/layout/tuning-panel.d.ts
|
|
3
|
+
export type TuningPanelProps = {
|
|
4
|
+
/** The tuning content (the difference from the defaults). Where it is stored is up to the app. */
|
|
5
|
+
tuning: Tuning;
|
|
6
|
+
onChange: (next: Tuning) => void;
|
|
7
|
+
/** What is tuned. Defaults to love3's generated output. */
|
|
8
|
+
catalog?: TuningCatalog;
|
|
9
|
+
/** Called on export (copying, sharing, etc. is up to the app). Receives the JSON string. */
|
|
10
|
+
onExport?: (json: string) => void;
|
|
11
|
+
};
|
|
12
|
+
/**
|
|
13
|
+
* The native tuning screen (minimal). Moves tokens and exports/imports them as `love3-tuning` JSON, the same format as web's `/tuning`.
|
|
14
|
+
*
|
|
15
|
+
* - Colors: edits the current theme's (light or dark) values in hex. An unreadable value keeps a red border and is not applied
|
|
16
|
+
* - Radius: px per role (fixed at build time on real devices, so it has no effect there; it remains in the JSON)
|
|
17
|
+
* - JSON: "書き出す" (Export) puts the current tuning in the field (and also passes it to `onExport`); "読み込む" (Import) validates the JSON in the field and applies it.
|
|
18
|
+
* If even one entry is invalid, the whole input is rejected and the reasons are shown (no partial application)
|
|
19
|
+
* - For items that have no effect on real devices, the `nativeLimits` explanation is shown
|
|
20
|
+
*
|
|
21
|
+
* It is not polished (a verification screen built only from love3's native components).
|
|
22
|
+
*/
|
|
23
|
+
export declare function TuningPanel({ tuning, onChange, catalog, onExport }: TuningPanelProps): import("react").JSX.Element;
|
|
24
|
+
//#endregion
|
|
@@ -0,0 +1,246 @@
|
|
|
1
|
+
import { catalog } from "../../tuning/catalog.gen.js";
|
|
2
|
+
import { parseTokenColor } from "../../tuning/color.js";
|
|
3
|
+
import { RADIUS_MAX, RADIUS_ROLES, emptyTuning } from "../../tuning/types.js";
|
|
4
|
+
import { colorValue, countChanges, normalize, withColor, withRadius } from "../../tuning/model.js";
|
|
5
|
+
import { nativeLimits } from "../../tuning/native.js";
|
|
6
|
+
import { parseTuning, toJson } from "../../tuning/schema.js";
|
|
7
|
+
import { useActiveScheme } from "../lib/theme.js";
|
|
8
|
+
import { Text as Text$1 } from "../base/text.js";
|
|
9
|
+
import { Panel } from "./panel.js";
|
|
10
|
+
import { Button } from "../atom/button.js";
|
|
11
|
+
import { Label } from "../atom/label.js";
|
|
12
|
+
import { TextField } from "../atom/text-field.js";
|
|
13
|
+
import { useId, useMemo, useState } from "react";
|
|
14
|
+
import { View } from "react-native";
|
|
15
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
16
|
+
|
|
17
|
+
//#region src/native/layout/tuning-panel.tsx
|
|
18
|
+
/** Token groups shown in the color editor (ramp, primary action, state, chart; brand colors are shared, so they are separate). */
|
|
19
|
+
const EDITABLE_GROUPS = /* @__PURE__ */ new Set([
|
|
20
|
+
"ramp",
|
|
21
|
+
"action",
|
|
22
|
+
"state",
|
|
23
|
+
"chart"
|
|
24
|
+
]);
|
|
25
|
+
/**
|
|
26
|
+
* The native tuning screen (minimal). Moves tokens and exports/imports them as `love3-tuning` JSON, the same format as web's `/tuning`.
|
|
27
|
+
*
|
|
28
|
+
* - Colors: edits the current theme's (light or dark) values in hex. An unreadable value keeps a red border and is not applied
|
|
29
|
+
* - Radius: px per role (fixed at build time on real devices, so it has no effect there; it remains in the JSON)
|
|
30
|
+
* - JSON: "書き出す" (Export) puts the current tuning in the field (and also passes it to `onExport`); "読み込む" (Import) validates the JSON in the field and applies it.
|
|
31
|
+
* If even one entry is invalid, the whole input is rejected and the reasons are shown (no partial application)
|
|
32
|
+
* - For items that have no effect on real devices, the `nativeLimits` explanation is shown
|
|
33
|
+
*
|
|
34
|
+
* It is not polished (a verification screen built only from love3's native components).
|
|
35
|
+
*/
|
|
36
|
+
function TuningPanel({ tuning, onChange, catalog: catalog$1 = catalog, onExport }) {
|
|
37
|
+
const scheme = useActiveScheme();
|
|
38
|
+
const normalized = useMemo(() => normalize(tuning, catalog$1), [tuning, catalog$1]);
|
|
39
|
+
const limits = useMemo(() => nativeLimits(tuning, catalog$1), [tuning, catalog$1]);
|
|
40
|
+
const [json, setJson] = useState("");
|
|
41
|
+
const [reasons, setReasons] = useState([]);
|
|
42
|
+
const [epoch, setEpoch] = useState(0);
|
|
43
|
+
const importJson = () => {
|
|
44
|
+
const result = parseTuning(json, catalog$1);
|
|
45
|
+
if (result.ok) {
|
|
46
|
+
setReasons([]);
|
|
47
|
+
setEpoch((value) => value + 1);
|
|
48
|
+
onChange(result.tuning);
|
|
49
|
+
} else setReasons(result.reasons);
|
|
50
|
+
};
|
|
51
|
+
const exportJson = () => {
|
|
52
|
+
const text = toJson(tuning, catalog$1);
|
|
53
|
+
setJson(text);
|
|
54
|
+
setReasons([]);
|
|
55
|
+
onExport?.(text);
|
|
56
|
+
};
|
|
57
|
+
return /* @__PURE__ */ jsxs(Panel, {
|
|
58
|
+
className: "gap-4 p-4",
|
|
59
|
+
children: [
|
|
60
|
+
/* @__PURE__ */ jsxs(View, {
|
|
61
|
+
className: "gap-1",
|
|
62
|
+
children: [/* @__PURE__ */ jsxs(Text$1, {
|
|
63
|
+
role: "heading",
|
|
64
|
+
"aria-level": "2",
|
|
65
|
+
className: "text-base font-semibold",
|
|
66
|
+
children: [
|
|
67
|
+
"調整(",
|
|
68
|
+
scheme === "dark" ? "ダーク" : "ライト",
|
|
69
|
+
")"
|
|
70
|
+
]
|
|
71
|
+
}), /* @__PURE__ */ jsxs(Text$1, {
|
|
72
|
+
className: "text-muted-foreground text-sm",
|
|
73
|
+
children: [
|
|
74
|
+
"変えた項目は ",
|
|
75
|
+
countChanges(tuning, catalog$1),
|
|
76
|
+
" 件。書き出した JSON は web の調整ページでも読めます。"
|
|
77
|
+
]
|
|
78
|
+
})]
|
|
79
|
+
}),
|
|
80
|
+
/* @__PURE__ */ jsxs(View, {
|
|
81
|
+
className: "gap-2",
|
|
82
|
+
children: [/* @__PURE__ */ jsx(Label, { children: "色" }), catalog$1.colors.filter((color) => EDITABLE_GROUPS.has(color.group)).map((color) => /* @__PURE__ */ jsx(ColorRow, {
|
|
83
|
+
name: color.name,
|
|
84
|
+
value: colorValue(normalized, catalog$1, scheme, color.name),
|
|
85
|
+
alpha: color.alpha,
|
|
86
|
+
onCommit: (value) => onChange(withColor(tuning, catalog$1, scheme, color.name, value))
|
|
87
|
+
}, `${color.name}-${epoch}`))]
|
|
88
|
+
}),
|
|
89
|
+
/* @__PURE__ */ jsxs(View, {
|
|
90
|
+
className: "gap-2",
|
|
91
|
+
children: [/* @__PURE__ */ jsx(Label, { children: "角丸(px)" }), RADIUS_ROLES.map((role) => /* @__PURE__ */ jsx(RadiusRow, {
|
|
92
|
+
role,
|
|
93
|
+
value: normalized.radius[role] ?? catalog$1.radius[role],
|
|
94
|
+
onCommit: (px) => onChange(withRadius(tuning, role, px))
|
|
95
|
+
}, `${role}-${epoch}`))]
|
|
96
|
+
}),
|
|
97
|
+
limits.length > 0 ? /* @__PURE__ */ jsx(View, {
|
|
98
|
+
className: "gap-1",
|
|
99
|
+
accessibilityRole: "summary",
|
|
100
|
+
children: limits.map((note) => /* @__PURE__ */ jsx(Text$1, {
|
|
101
|
+
className: "text-muted-foreground text-xs",
|
|
102
|
+
children: note
|
|
103
|
+
}, note))
|
|
104
|
+
}) : null,
|
|
105
|
+
/* @__PURE__ */ jsxs(View, {
|
|
106
|
+
className: "gap-2",
|
|
107
|
+
children: [
|
|
108
|
+
/* @__PURE__ */ jsx(Label, { children: "JSON(love3-tuning)" }),
|
|
109
|
+
/* @__PURE__ */ jsx(TextField, {
|
|
110
|
+
value: json,
|
|
111
|
+
onChangeText: setJson,
|
|
112
|
+
multiline: true,
|
|
113
|
+
numberOfLines: 6,
|
|
114
|
+
autoCapitalize: "none",
|
|
115
|
+
autoCorrect: false,
|
|
116
|
+
accessibilityLabel: "調整の JSON",
|
|
117
|
+
placeholder: "{ \"format\": \"love3-tuning\", \"version\": 1 }",
|
|
118
|
+
invalid: reasons.length > 0
|
|
119
|
+
}),
|
|
120
|
+
reasons.map((reason) => /* @__PURE__ */ jsx(Text$1, {
|
|
121
|
+
role: "alert",
|
|
122
|
+
className: "text-destructive text-xs",
|
|
123
|
+
children: reason
|
|
124
|
+
}, reason)),
|
|
125
|
+
/* @__PURE__ */ jsxs(View, {
|
|
126
|
+
className: "flex-row flex-wrap gap-2",
|
|
127
|
+
children: [
|
|
128
|
+
/* @__PURE__ */ jsx(Button, {
|
|
129
|
+
size: "sm",
|
|
130
|
+
variant: "outline",
|
|
131
|
+
onPress: exportJson,
|
|
132
|
+
children: "書き出す"
|
|
133
|
+
}),
|
|
134
|
+
/* @__PURE__ */ jsx(Button, {
|
|
135
|
+
size: "sm",
|
|
136
|
+
onPress: importJson,
|
|
137
|
+
children: "読み込む"
|
|
138
|
+
}),
|
|
139
|
+
/* @__PURE__ */ jsx(Button, {
|
|
140
|
+
size: "sm",
|
|
141
|
+
variant: "ghost",
|
|
142
|
+
onPress: () => {
|
|
143
|
+
setEpoch((value) => value + 1);
|
|
144
|
+
onChange(emptyTuning());
|
|
145
|
+
},
|
|
146
|
+
children: "すべて既定に戻す"
|
|
147
|
+
})
|
|
148
|
+
]
|
|
149
|
+
})
|
|
150
|
+
]
|
|
151
|
+
})
|
|
152
|
+
]
|
|
153
|
+
});
|
|
154
|
+
}
|
|
155
|
+
/** The reason shown when the input is unreadable. Rendered under the row with `role="alert"` and linked from the input via `aria-describedby`. */
|
|
156
|
+
function Reason({ id, text }) {
|
|
157
|
+
return /* @__PURE__ */ jsx(Text$1, {
|
|
158
|
+
id,
|
|
159
|
+
role: "alert",
|
|
160
|
+
className: "text-destructive text-xs",
|
|
161
|
+
children: text
|
|
162
|
+
});
|
|
163
|
+
}
|
|
164
|
+
/**
|
|
165
|
+
* One color. **Keeps its own in-progress text (draft) and keeps showing it in the field until focus leaves** (`#abc` is a valid 3-digit form
|
|
166
|
+
* but may also be partway through a 6-digit one; replacing the field with the applied value would make it impossible to finish typing 6 digits). Once the value is readable, it is applied on every keystroke
|
|
167
|
+
* (the applied value takes the canonical form `#aabbcc`, but the field keeps the text as typed). On blur, the applied value (canonical form) is shown in the field.
|
|
168
|
+
* An unreadable value shows a reason and is not applied.
|
|
169
|
+
*/
|
|
170
|
+
function ColorRow({ name, value, alpha, onCommit }) {
|
|
171
|
+
const reasonId = useId();
|
|
172
|
+
const [draft, setDraft] = useState(null);
|
|
173
|
+
const parsed = draft === null ? null : parseTokenColor(draft, { alpha });
|
|
174
|
+
const reason = parsed !== null && !parsed.ok ? parsed.reason : void 0;
|
|
175
|
+
return /* @__PURE__ */ jsxs(View, {
|
|
176
|
+
className: "gap-1",
|
|
177
|
+
children: [/* @__PURE__ */ jsxs(View, {
|
|
178
|
+
className: "flex-row items-center gap-2",
|
|
179
|
+
children: [/* @__PURE__ */ jsxs(Text$1, {
|
|
180
|
+
className: "w-32 font-mono text-xs",
|
|
181
|
+
children: ["--", name]
|
|
182
|
+
}), /* @__PURE__ */ jsx(View, {
|
|
183
|
+
className: "flex-1",
|
|
184
|
+
children: /* @__PURE__ */ jsx(TextField, {
|
|
185
|
+
value: draft ?? value,
|
|
186
|
+
onChangeText: (text) => {
|
|
187
|
+
setDraft(text);
|
|
188
|
+
const next = parseTokenColor(text, { alpha });
|
|
189
|
+
if (next.ok) onCommit(next.value);
|
|
190
|
+
},
|
|
191
|
+
onBlur: () => setDraft(null),
|
|
192
|
+
accessibilityLabel: `--${name}`,
|
|
193
|
+
autoCapitalize: "none",
|
|
194
|
+
autoCorrect: false,
|
|
195
|
+
invalid: reason !== void 0,
|
|
196
|
+
"aria-describedby": reason === void 0 ? void 0 : reasonId
|
|
197
|
+
})
|
|
198
|
+
})]
|
|
199
|
+
}), reason === void 0 ? null : /* @__PURE__ */ jsx(Reason, {
|
|
200
|
+
id: reasonId,
|
|
201
|
+
text: reason
|
|
202
|
+
})]
|
|
203
|
+
});
|
|
204
|
+
}
|
|
205
|
+
/** The problem with a radius input (undefined if it is readable and within range). */
|
|
206
|
+
function radiusProblem(text) {
|
|
207
|
+
const number = Number(text);
|
|
208
|
+
if (text.trim() === "" || !Number.isFinite(number)) return `角丸は数で入力してください(0〜${64})`;
|
|
209
|
+
if (number < 0 || number > 64) return `角丸は 0〜${64} の数で入力してください(実際: ${text})`;
|
|
210
|
+
}
|
|
211
|
+
/** One radius. As with colors, in-progress text (such as `6.`) keeps showing in the field until focus leaves. */
|
|
212
|
+
function RadiusRow({ role, value, onCommit }) {
|
|
213
|
+
const reasonId = useId();
|
|
214
|
+
const [draft, setDraft] = useState(null);
|
|
215
|
+
const reason = draft === null ? void 0 : radiusProblem(draft);
|
|
216
|
+
return /* @__PURE__ */ jsxs(View, {
|
|
217
|
+
className: "gap-1",
|
|
218
|
+
children: [/* @__PURE__ */ jsxs(View, {
|
|
219
|
+
className: "flex-row items-center gap-2",
|
|
220
|
+
children: [/* @__PURE__ */ jsxs(Text$1, {
|
|
221
|
+
className: "w-32 font-mono text-xs",
|
|
222
|
+
children: ["radius-", role]
|
|
223
|
+
}), /* @__PURE__ */ jsx(View, {
|
|
224
|
+
className: "flex-1",
|
|
225
|
+
children: /* @__PURE__ */ jsx(TextField, {
|
|
226
|
+
value: draft ?? String(value),
|
|
227
|
+
onChangeText: (text) => {
|
|
228
|
+
setDraft(text);
|
|
229
|
+
if (radiusProblem(text) === void 0) onCommit(Number(text));
|
|
230
|
+
},
|
|
231
|
+
onBlur: () => setDraft(null),
|
|
232
|
+
type: "number",
|
|
233
|
+
accessibilityLabel: `radius-${role}`,
|
|
234
|
+
invalid: reason !== void 0,
|
|
235
|
+
"aria-describedby": reason === void 0 ? void 0 : reasonId
|
|
236
|
+
})
|
|
237
|
+
})]
|
|
238
|
+
}), reason === void 0 ? null : /* @__PURE__ */ jsx(Reason, {
|
|
239
|
+
id: reasonId,
|
|
240
|
+
text: reason
|
|
241
|
+
})]
|
|
242
|
+
});
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
//#endregion
|
|
246
|
+
export { TuningPanel };
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { TextInputProps } from "react-native";
|
|
2
|
+
//#region src/native/lib/autocomplete.d.ts
|
|
3
|
+
export type AutoComplete = NonNullable<TextInputProps["autoComplete"]>;
|
|
4
|
+
export type TextContentType = NonNullable<TextInputProps["textContentType"]>;
|
|
5
|
+
/** iOS `textContentType`. `undefined` if there is no corresponding word (leave it to the iOS default). */
|
|
6
|
+
export declare function textContentTypeOf(autoComplete: AutoComplete | undefined): TextContentType | undefined;
|
|
7
|
+
//#endregion
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
//#region src/native/lib/autocomplete.ts
|
|
2
|
+
const CONTENT_TYPE = {
|
|
3
|
+
email: "emailAddress",
|
|
4
|
+
username: "username",
|
|
5
|
+
"username-new": "username",
|
|
6
|
+
"current-password": "password",
|
|
7
|
+
password: "password",
|
|
8
|
+
"new-password": "newPassword",
|
|
9
|
+
"password-new": "newPassword",
|
|
10
|
+
"one-time-code": "oneTimeCode",
|
|
11
|
+
"sms-otp": "oneTimeCode",
|
|
12
|
+
"email-otp": "oneTimeCode",
|
|
13
|
+
tel: "telephoneNumber",
|
|
14
|
+
name: "name",
|
|
15
|
+
"given-name": "givenName",
|
|
16
|
+
"name-given": "givenName",
|
|
17
|
+
"family-name": "familyName",
|
|
18
|
+
"name-family": "familyName",
|
|
19
|
+
"postal-code": "postalCode",
|
|
20
|
+
"street-address": "fullStreetAddress",
|
|
21
|
+
"address-line1": "streetAddressLine1",
|
|
22
|
+
"address-line2": "streetAddressLine2",
|
|
23
|
+
organization: "organizationName",
|
|
24
|
+
url: "URL",
|
|
25
|
+
"cc-number": "creditCardNumber",
|
|
26
|
+
off: "none"
|
|
27
|
+
};
|
|
28
|
+
/** iOS `textContentType`. `undefined` if there is no corresponding word (leave it to the iOS default). */
|
|
29
|
+
function textContentTypeOf(autoComplete) {
|
|
30
|
+
return autoComplete === void 0 ? void 0 : CONTENT_TYPE[autoComplete];
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
//#endregion
|
|
34
|
+
export { textContentTypeOf };
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
//#region src/native/lib/breakpoints.d.ts
|
|
2
|
+
/** Same breakpoints (px) as web (Tailwind). */
|
|
3
|
+
export declare const BREAKPOINT: {
|
|
4
|
+
readonly sm: 640;
|
|
5
|
+
readonly md: 768;
|
|
6
|
+
readonly lg: 1024;
|
|
7
|
+
};
|
|
8
|
+
export type Breakpoint = keyof typeof BREAKPOINT;
|
|
9
|
+
/**
|
|
10
|
+
* Whether to show a `hideBelow` column at this screen width. A width exactly at the breakpoint **shows** it (Tailwind's `sm:` is 640px and up).
|
|
11
|
+
* If unspecified, always shown.
|
|
12
|
+
*/
|
|
13
|
+
export declare function isShownAtWidth(hideBelow: Breakpoint | undefined, width: number): boolean;
|
|
14
|
+
//#endregion
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
//#region src/native/lib/breakpoints.ts
|
|
2
|
+
/** Same breakpoints (px) as web (Tailwind). */
|
|
3
|
+
const BREAKPOINT = {
|
|
4
|
+
sm: 640,
|
|
5
|
+
md: 768,
|
|
6
|
+
lg: 1024
|
|
7
|
+
};
|
|
8
|
+
/**
|
|
9
|
+
* Whether to show a `hideBelow` column at this screen width. A width exactly at the breakpoint **shows** it (Tailwind's `sm:` is 640px and up).
|
|
10
|
+
* If unspecified, always shown.
|
|
11
|
+
*/
|
|
12
|
+
function isShownAtWidth(hideBelow, width) {
|
|
13
|
+
return hideBelow === void 0 || width >= BREAKPOINT[hideBelow];
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
//#endregion
|
|
17
|
+
export { BREAKPOINT, isShownAtWidth };
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
//#region src/native/lib/chart-palette.d.ts
|
|
2
|
+
/**
|
|
3
|
+
* Default chart colors, returned in the same order as the web `--chart-1` to `--chart-5` (five colors that separate series by hue; each has a distinct value that stays readable in dark mode).
|
|
4
|
+
*
|
|
5
|
+
* `--chart-*` are raw color tokens with separate light and dark values, so the actual values can be looked up directly. They also follow `.dark`.
|
|
6
|
+
*/
|
|
7
|
+
export declare function useChartPalette(): string[];
|
|
8
|
+
//#endregion
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { useTokenColor } from "./token-color.js";
|
|
2
|
+
|
|
3
|
+
//#region src/native/lib/chart-palette.ts
|
|
4
|
+
/**
|
|
5
|
+
* Default chart colors, returned in the same order as the web `--chart-1` to `--chart-5` (five colors that separate series by hue; each has a distinct value that stays readable in dark mode).
|
|
6
|
+
*
|
|
7
|
+
* `--chart-*` are raw color tokens with separate light and dark values, so the actual values can be looked up directly. They also follow `.dark`.
|
|
8
|
+
*/
|
|
9
|
+
function useChartPalette() {
|
|
10
|
+
return [
|
|
11
|
+
useTokenColor("--chart-1"),
|
|
12
|
+
useTokenColor("--chart-2"),
|
|
13
|
+
useTokenColor("--chart-3"),
|
|
14
|
+
useTokenColor("--chart-4"),
|
|
15
|
+
useTokenColor("--chart-5")
|
|
16
|
+
].filter((color) => typeof color === "string");
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
//#endregion
|
|
20
|
+
export { useChartPalette };
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { ButtonProps } from "../atom/button.js";
|
|
2
|
+
import { ExternalStore } from "./external-store.js";
|
|
3
|
+
//#region src/native/lib/confirm.d.ts
|
|
4
|
+
export type ConfirmDialogButton = {
|
|
5
|
+
text: string;
|
|
6
|
+
onClick: () => void;
|
|
7
|
+
variant?: ButtonProps["variant"];
|
|
8
|
+
};
|
|
9
|
+
export type ConfirmDialogValue = {
|
|
10
|
+
isOpen: boolean;
|
|
11
|
+
title?: string;
|
|
12
|
+
description?: string;
|
|
13
|
+
buttons?: ConfirmDialogButton[];
|
|
14
|
+
/** When the outside is pressed. If unspecified, it does not close. Calling `close` closes it. */
|
|
15
|
+
onOutsideClick?: (close: () => void) => void;
|
|
16
|
+
/**
|
|
17
|
+
* Called by the renderer when it is dismissed without a button press (Escape, the Android back action, etc.).
|
|
18
|
+
* `confirm()` resolves false with it so that the waiting caller does not hang.
|
|
19
|
+
*/
|
|
20
|
+
onDismiss?: () => void;
|
|
21
|
+
};
|
|
22
|
+
/** State read by `ConfirmDialog`. The imperative API (`confirm()`) writes here. */
|
|
23
|
+
export declare const confirmDialogStore: ExternalStore<ConfirmDialogValue>;
|
|
24
|
+
/** Closes the open confirm dialog (keeps its content, so the text does not vanish during the closing animation). */
|
|
25
|
+
export declare function closeConfirmDialog(): void;
|
|
26
|
+
/**
|
|
27
|
+
* Call only while a `ConfirmDialog` is mounted (`once/confirm` calls it in an effect). Returns an unsubscribe function.
|
|
28
|
+
* **When the last one detaches, a confirm left open is resolved with false and closed** (if it unmounted while open, the waiting caller would
|
|
29
|
+
* hang, and an old confirm would appear the next time one mounts).
|
|
30
|
+
*/
|
|
31
|
+
export declare function registerConfirmDialog(): () => void;
|
|
32
|
+
export type ConfirmOptions = {
|
|
33
|
+
description?: string;
|
|
34
|
+
/** Label of the "yes" button (default "はい"). */
|
|
35
|
+
yes?: string;
|
|
36
|
+
/** Label of the "no" button (default "いいえ"). */
|
|
37
|
+
no?: string;
|
|
38
|
+
/** Pass `destructive` for confirming irreversible actions, so it also looks dangerous. */
|
|
39
|
+
yesVariant?: ButtonProps["variant"];
|
|
40
|
+
};
|
|
41
|
+
/**
|
|
42
|
+
* Opens a confirm dialog and resolves with the pressed button (yes = true; no, outside press, back action = false).
|
|
43
|
+
* Place one `ConfirmDialog` from `once/confirm` at the app root (**without it, it neither opens nor resolves**; a warning is logged in development.
|
|
44
|
+
* If placed later, it opens at that point).
|
|
45
|
+
* Takes no i18n argument: love3 does not depend on i18n, text is passed as `yes` / `no`, and the defaults are Japanese.
|
|
46
|
+
*/
|
|
47
|
+
export declare function confirm(message: string, options?: ConfirmOptions): Promise<boolean>;
|
|
48
|
+
//#endregion
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import { NO_LABEL, YES_LABEL } from "./labels.js";
|
|
2
|
+
import { createExternalStore } from "./external-store.js";
|
|
3
|
+
|
|
4
|
+
//#region src/native/lib/confirm.ts
|
|
5
|
+
/** State read by `ConfirmDialog`. The imperative API (`confirm()`) writes here. */
|
|
6
|
+
const confirmDialogStore = createExternalStore({ isOpen: false });
|
|
7
|
+
/** Closes the open confirm dialog (keeps its content, so the text does not vanish during the closing animation). */
|
|
8
|
+
function closeConfirmDialog() {
|
|
9
|
+
confirmDialogStore.update((prev) => ({
|
|
10
|
+
...prev,
|
|
11
|
+
isOpen: false
|
|
12
|
+
}));
|
|
13
|
+
}
|
|
14
|
+
let mountedDialogs = 0;
|
|
15
|
+
/**
|
|
16
|
+
* Call only while a `ConfirmDialog` is mounted (`once/confirm` calls it in an effect). Returns an unsubscribe function.
|
|
17
|
+
* **When the last one detaches, a confirm left open is resolved with false and closed** (if it unmounted while open, the waiting caller would
|
|
18
|
+
* hang, and an old confirm would appear the next time one mounts).
|
|
19
|
+
*/
|
|
20
|
+
function registerConfirmDialog() {
|
|
21
|
+
mountedDialogs += 1;
|
|
22
|
+
return () => {
|
|
23
|
+
mountedDialogs -= 1;
|
|
24
|
+
if (mountedDialogs > 0 || !confirmDialogStore.get().isOpen) return;
|
|
25
|
+
confirmDialogStore.get().onDismiss?.();
|
|
26
|
+
closeConfirmDialog();
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
let pendingResolve = null;
|
|
30
|
+
/**
|
|
31
|
+
* Opens a confirm dialog and resolves with the pressed button (yes = true; no, outside press, back action = false).
|
|
32
|
+
* Place one `ConfirmDialog` from `once/confirm` at the app root (**without it, it neither opens nor resolves**; a warning is logged in development.
|
|
33
|
+
* If placed later, it opens at that point).
|
|
34
|
+
* Takes no i18n argument: love3 does not depend on i18n, text is passed as `yes` / `no`, and the defaults are Japanese.
|
|
35
|
+
*/
|
|
36
|
+
function confirm(message, options) {
|
|
37
|
+
const { description, yes, no, yesVariant } = options ?? {};
|
|
38
|
+
if (__DEV__ && mountedDialogs === 0) console.warn("confirm(): ConfirmDialog がマウントされていない。アプリのルートに <ConfirmDialog /> を 1 つ置くまで、この確認は開かず、解決もしない。");
|
|
39
|
+
return new Promise((resolvePromise) => {
|
|
40
|
+
pendingResolve?.(false);
|
|
41
|
+
const resolve = (value) => {
|
|
42
|
+
if (pendingResolve === resolve) pendingResolve = null;
|
|
43
|
+
resolvePromise(value);
|
|
44
|
+
};
|
|
45
|
+
pendingResolve = resolve;
|
|
46
|
+
confirmDialogStore.set({
|
|
47
|
+
isOpen: true,
|
|
48
|
+
title: message,
|
|
49
|
+
description,
|
|
50
|
+
buttons: [{
|
|
51
|
+
text: no ?? "いいえ",
|
|
52
|
+
onClick: () => resolve(false),
|
|
53
|
+
variant: "outline"
|
|
54
|
+
}, {
|
|
55
|
+
text: yes ?? "はい",
|
|
56
|
+
onClick: () => resolve(true),
|
|
57
|
+
variant: yesVariant
|
|
58
|
+
}],
|
|
59
|
+
onOutsideClick: (close) => {
|
|
60
|
+
resolve(false);
|
|
61
|
+
close();
|
|
62
|
+
},
|
|
63
|
+
onDismiss: () => resolve(false)
|
|
64
|
+
});
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
//#endregion
|
|
69
|
+
export { closeConfirmDialog, confirm, confirmDialogStore, registerConfirmDialog };
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
//#region src/native/lib/control.d.ts
|
|
2
|
+
/**
|
|
3
|
+
* The accessibility props that input components (`atom/text-field`, `checkbox`, `switch`, `slider`) receive from the form wrapper (`FormControl` in `base/form`).
|
|
4
|
+
* Corresponds to the web `FormControl` (Radix's Slot passes `id` / `aria-*` to the direct child input).
|
|
5
|
+
*
|
|
6
|
+
* - `id` / `aria-labelledby` / `aria-describedby` / `aria-invalid`: react-native-web emits them to the DOM (association with the label and description works).
|
|
7
|
+
* Real-device React Native honors `aria-labelledby` on Android only and does not read `aria-describedby`,
|
|
8
|
+
* so for real-device screen readers, pass `accessibilityLabel` and `accessibilityHint` (passed when the form's parts are strings)
|
|
9
|
+
* - `accessibilityHint`: supplementary text (`description`). Real-device VoiceOver / TalkBack reads it after the name
|
|
10
|
+
*/
|
|
11
|
+
export type ControlA11yProps = {
|
|
12
|
+
id?: string;
|
|
13
|
+
"aria-labelledby"?: string;
|
|
14
|
+
"aria-describedby"?: string;
|
|
15
|
+
"aria-invalid"?: boolean;
|
|
16
|
+
accessibilityHint?: string;
|
|
17
|
+
};
|
|
18
|
+
/**
|
|
19
|
+
* Props received by inputs that carry their own name (`atom/checkbox` = the label on the right, `atom/slider` = its own `label`).
|
|
20
|
+
* `aria-labelledby` is **not included in the type** (no silently dropping what has nowhere to go). Only inputs that want the form wrapper's label as their name
|
|
21
|
+
* (`text-field`, `switch`) accept all of `ControlA11yProps`.
|
|
22
|
+
*/
|
|
23
|
+
export type NamedControlA11yProps = Omit<ControlA11yProps, "aria-labelledby">;
|
|
24
|
+
//#endregion
|
|
File without changes
|