@godxjp/ui 28.8.0 → 28.10.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/agent/START-HERE.md +193 -0
- package/agent/anti-ai-tells.json +158 -0
- package/agent/components/Accordion.json +60 -0
- package/agent/components/AccountChip.json +59 -0
- package/agent/components/Actions.json +78 -0
- package/agent/components/Activity.json +80 -0
- package/agent/components/Affix.json +78 -0
- package/agent/components/Alert.json +65 -0
- package/agent/components/AlertDialog.json +109 -0
- package/agent/components/AlertDialogRoot.json +52 -0
- package/agent/components/Anchor.json +119 -0
- package/agent/components/AppLauncher.json +95 -0
- package/agent/components/AppProvider.json +105 -0
- package/agent/components/AppSettingPicker.json +88 -0
- package/agent/components/AppSettingToggle.json +70 -0
- package/agent/components/AppShell.json +159 -0
- package/agent/components/AreaChart.json +105 -0
- package/agent/components/AspectRatio.json +38 -0
- package/agent/components/Attachments.json +76 -0
- package/agent/components/AuthAccountSummary.json +64 -0
- package/agent/components/AuthDivider.json +35 -0
- package/agent/components/AuthFooter.json +48 -0
- package/agent/components/AuthIdentity.json +42 -0
- package/agent/components/AuthShell.json +108 -0
- package/agent/components/AuthStack.json +21 -0
- package/agent/components/Avatar.json +89 -0
- package/agent/components/Badge.json +96 -0
- package/agent/components/Banner.json +48 -0
- package/agent/components/BarChart.json +108 -0
- package/agent/components/BranchScopePicker.json +89 -0
- package/agent/components/Breadcrumb.json +54 -0
- package/agent/components/Button.json +133 -0
- package/agent/components/Calendar.json +259 -0
- package/agent/components/Callout.json +46 -0
- package/agent/components/Card.json +112 -0
- package/agent/components/CardBar.json +49 -0
- package/agent/components/CardContent.json +51 -0
- package/agent/components/Carousel.json +50 -0
- package/agent/components/Cascader.json +209 -0
- package/agent/components/CenteredShell.json +66 -0
- package/agent/components/ChatBubble.json +100 -0
- package/agent/components/ChatBubbleList.json +64 -0
- package/agent/components/ChatComposer.json +160 -0
- package/agent/components/ChatSuggestion.json +86 -0
- package/agent/components/Checkbox.json +68 -0
- package/agent/components/CheckboxGroup.json +96 -0
- package/agent/components/CodeBlock.json +64 -0
- package/agent/components/Collapsible.json +74 -0
- package/agent/components/ColorPicker.json +87 -0
- package/agent/components/Command.json +168 -0
- package/agent/components/CommandPalette.json +84 -0
- package/agent/components/CompactBarTrend.json +101 -0
- package/agent/components/Conversations.json +82 -0
- package/agent/components/CredentialReveal.json +93 -0
- package/agent/components/DataState.json +79 -0
- package/agent/components/DataTable.json +268 -0
- package/agent/components/DatePicker.json +275 -0
- package/agent/components/Descriptions.json +67 -0
- package/agent/components/Dialog.json +78 -0
- package/agent/components/DraggablePanel.json +106 -0
- package/agent/components/DropdownMenu.json +102 -0
- package/agent/components/EmptyState.json +83 -0
- package/agent/components/ErrorSurface.json +128 -0
- package/agent/components/FeatureList.json +43 -0
- package/agent/components/Field.json +64 -0
- package/agent/components/FilterBar.json +99 -0
- package/agent/components/Flex.json +153 -0
- package/agent/components/FloatButton.json +91 -0
- package/agent/components/Form.json +87 -0
- package/agent/components/FormErrors.json +51 -0
- package/agent/components/FormField.json +137 -0
- package/agent/components/FormFieldArray.json +39 -0
- package/agent/components/FormFieldControl.json +129 -0
- package/agent/components/FormRoot.json +122 -0
- package/agent/components/Heading.json +61 -0
- package/agent/components/HoverCard.json +55 -0
- package/agent/components/Icon.json +60 -0
- package/agent/components/InfiniteQueryState.json +58 -0
- package/agent/components/Input.json +122 -0
- package/agent/components/InputOTP.json +106 -0
- package/agent/components/Label.json +43 -0
- package/agent/components/LegalDocumentShell.json +102 -0
- package/agent/components/Legend.json +42 -0
- package/agent/components/LineChart.json +103 -0
- package/agent/components/Link.json +41 -0
- package/agent/components/ListRow.json +92 -0
- package/agent/components/Logo.json +85 -0
- package/agent/components/Marquee.json +91 -0
- package/agent/components/Masonry.json +82 -0
- package/agent/components/MasterDetail.json +95 -0
- package/agent/components/MegaMenu.json +120 -0
- package/agent/components/MobileShell.json +73 -0
- package/agent/components/NavList.json +63 -0
- package/agent/components/NumberInput.json +158 -0
- package/agent/components/OrgSwitcher.json +89 -0
- package/agent/components/OverlayPortalProvider.json +42 -0
- package/agent/components/PageContainer.json +181 -0
- package/agent/components/Pagination.json +132 -0
- package/agent/components/Paragraph.json +40 -0
- package/agent/components/PasswordInput.json +79 -0
- package/agent/components/PasswordStrength.json +51 -0
- package/agent/components/PermissionMatrix.json +81 -0
- package/agent/components/PieChart.json +99 -0
- package/agent/components/Popover.json +110 -0
- package/agent/components/PrefetchLink.json +65 -0
- package/agent/components/Progress.json +79 -0
- package/agent/components/Prose.json +57 -0
- package/agent/components/QrCode.json +62 -0
- package/agent/components/Radio.json +98 -0
- package/agent/components/RadioGroup.json +91 -0
- package/agent/components/RangeTimeline.json +80 -0
- package/agent/components/Rating.json +92 -0
- package/agent/components/ResizablePanel.json +69 -0
- package/agent/components/ResponsiveGrid.json +77 -0
- package/agent/components/Reveal.json +70 -0
- package/agent/components/ScrollArea.json +104 -0
- package/agent/components/SearchInput.json +98 -0
- package/agent/components/Segmented.json +96 -0
- package/agent/components/Select.json +397 -0
- package/agent/components/Separator.json +86 -0
- package/agent/components/ServiceCatalogCta.json +46 -0
- package/agent/components/ServiceLauncherCard.json +86 -0
- package/agent/components/ServiceRolePanel.json +83 -0
- package/agent/components/Sheet.json +85 -0
- package/agent/components/Sidebar.json +118 -0
- package/agent/components/Skeleton.json +57 -0
- package/agent/components/SkeletonArticle.json +71 -0
- package/agent/components/SkeletonAvatar.json +50 -0
- package/agent/components/SkeletonButton.json +57 -0
- package/agent/components/SkeletonForm.json +52 -0
- package/agent/components/SkeletonImage.json +37 -0
- package/agent/components/SkeletonInput.json +51 -0
- package/agent/components/SkeletonNode.json +42 -0
- package/agent/components/SkeletonRows.json +49 -0
- package/agent/components/SkeletonTable.json +45 -0
- package/agent/components/Slider.json +160 -0
- package/agent/components/SplitPane.json +66 -0
- package/agent/components/StatCard.json +83 -0
- package/agent/components/Steps.json +95 -0
- package/agent/components/Swatch.json +41 -0
- package/agent/components/Switch.json +81 -0
- package/agent/components/Table.json +112 -0
- package/agent/components/Tabs.json +158 -0
- package/agent/components/TagInput.json +105 -0
- package/agent/components/Text.json +201 -0
- package/agent/components/Textarea.json +126 -0
- package/agent/components/ThoughtChain.json +76 -0
- package/agent/components/Thumbnail.json +70 -0
- package/agent/components/TimePicker.json +200 -0
- package/agent/components/TimeRangePicker.json +90 -0
- package/agent/components/Timeline.json +47 -0
- package/agent/components/TimelineGrid.json +92 -0
- package/agent/components/Title.json +67 -0
- package/agent/components/Toaster.json +42 -0
- package/agent/components/Toggle.json +90 -0
- package/agent/components/ToggleGroup.json +102 -0
- package/agent/components/Toolbar.json +120 -0
- package/agent/components/Tooltip.json +110 -0
- package/agent/components/Topbar.json +83 -0
- package/agent/components/TopbarItem.json +79 -0
- package/agent/components/Transfer.json +141 -0
- package/agent/components/Tree.json +185 -0
- package/agent/components/TreeSelect.json +232 -0
- package/agent/components/TwoFactorSetup.json +79 -0
- package/agent/components/Typography.json +42 -0
- package/agent/components/Upload.json +221 -0
- package/agent/components/UploadCropDialog.json +60 -0
- package/agent/components/VisuallyHidden.json +20 -0
- package/agent/components/Welcome.json +65 -0
- package/agent/components/formatDate.json +46 -0
- package/agent/components/inertiaUpload.json +32 -0
- package/agent/components/useZodForm.json +39 -0
- package/agent/components-index.json +884 -0
- package/agent/components.json +15507 -0
- package/agent/index.json +56 -0
- package/agent/llms.txt +32 -0
- package/agent/patterns/account-recovery-settings.json +19 -0
- package/agent/patterns/async-data-state.json +20 -0
- package/agent/patterns/auth-recovery-panels.json +29 -0
- package/agent/patterns/badge-coloring.json +14 -0
- package/agent/patterns/common-fixes.json +16 -0
- package/agent/patterns/confirm-destructive.json +11 -0
- package/agent/patterns/data-table-page.json +18 -0
- package/agent/patterns/deferred-loading.json +12 -0
- package/agent/patterns/error-pages.json +28 -0
- package/agent/patterns/inertia-detail-page.json +13 -0
- package/agent/patterns/inertia-list-page.json +15 -0
- package/agent/patterns/inertia-persistent-layout.json +14 -0
- package/agent/patterns/organization-memberships.json +19 -0
- package/agent/patterns/page-sections.json +18 -0
- package/agent/patterns/settings-page-responsive.json +18 -0
- package/agent/patterns/settings-section-rows.json +23 -0
- package/agent/patterns/signup-form.json +13 -0
- package/agent/patterns/topbar-account-chip.json +18 -0
- package/agent/patterns/transactional-email.json +22 -0
- package/agent/patterns-index.json +323 -0
- package/agent/patterns.json +342 -0
- package/agent/rules.json +237 -0
- package/agent/tokens.json +8422 -0
- package/agent/vocabulary.json +198 -0
- package/dist/components/data-entry/input.js +8 -1
- package/dist/components/layout/flex.d.ts +2 -2
- package/dist/components/layout/flex.js +2 -0
- package/dist/components/ui/tag-input.d.ts +10 -0
- package/dist/components/ui/tag-input.js +35 -2
- package/dist/contracts/measurement.json +1 -1
- package/dist/i18n/messages/en.json +23 -1
- package/dist/i18n/messages/ja.json +21 -1
- package/dist/i18n/messages/vi.json +21 -1
- package/dist/lib/variants.js +4 -1
- package/dist/props/components/data-entry.prop.d.ts +21 -2
- package/dist/props/components/layout.prop.d.ts +42 -0
- package/dist/props/registry.d.ts +9 -0
- package/dist/props/registry.js +6 -0
- package/dist/props/vocabulary/layout.prop.d.ts +1 -1
- package/dist/styles/base.css +47 -14
- package/dist/styles/card-layout.css +6 -6
- package/dist/styles/chart-layout.css +6 -6
- package/dist/styles/control.css +41 -6
- package/dist/styles/data-display-layout.css +21 -6
- package/dist/styles/density.css +2 -0
- package/dist/styles/dialog-layout.css +4 -1
- package/dist/styles/focus-ring.css +4 -1
- package/dist/styles/layout.css +30 -3
- package/dist/styles/navigation-layout.css +3 -1
- package/dist/styles/shell-layout.css +27 -21
- package/dist/styles/table-layout.css +50 -9
- package/dist/styles/text-layout.css +94 -23
- package/dist/tokens/components/activity.css +13 -4
- package/dist/tokens/components/attachments.css +1 -1
- package/dist/tokens/components/badge.css +1 -1
- package/dist/tokens/components/card.css +28 -7
- package/dist/tokens/components/chart.css +4 -1
- package/dist/tokens/components/chat-composer.css +4 -1
- package/dist/tokens/components/control.css +69 -30
- package/dist/tokens/components/conversations.css +4 -1
- package/dist/tokens/components/data-display.css +42 -15
- package/dist/tokens/components/data-entry.css +8 -2
- package/dist/tokens/components/descriptions.css +1 -1
- package/dist/tokens/components/feedback.css +8 -5
- package/dist/tokens/components/float-button.css +8 -2
- package/dist/tokens/components/legal-document.css +12 -3
- package/dist/tokens/components/logo.css +15 -6
- package/dist/tokens/components/mega-menu.css +14 -5
- package/dist/tokens/components/navigation.css +37 -13
- package/dist/tokens/components/segmented.css +9 -2
- package/dist/tokens/components/separator.css +4 -1
- package/dist/tokens/components/shell.css +96 -31
- package/dist/tokens/components/table.css +13 -6
- package/dist/tokens/components/thought-chain.css +4 -1
- package/dist/tokens/components/toggle.css +4 -1
- package/dist/tokens/components/tree.css +1 -1
- package/dist/tokens/components/upload.css +21 -9
- package/dist/tokens/foundation.css +24 -30
- package/dist/tokens/semantic/layout.css +19 -5
- package/docs/COMPOSITION-VS-COMPONENT.md +41 -0
- package/docs/DESIGN-AUTHORITY.md +14 -0
- package/docs/DEVELOPMENT.md +81 -6
- package/docs/TOKENS.md +16 -1
- package/docs/data-entry/tag-input.tsx +37 -0
- package/docs/layout/flex.tsx +40 -0
- package/docs/roadmap/website-components.md +34 -0
- package/docs/showcase/case4-login.tsx +10 -2
- package/docs/showcase/case5-shift-calendar.tsx +1 -1
- package/docs/showcase/case6-agency-handy.tsx +6 -6
- package/docs/showcase/futurelastic-web.tsx +7 -9
- package/docs/showcase/marketing-page.tsx +61 -52
- package/docs/showcase/table-expandable-rows.tsx +4 -1
- package/docs/showcase/table-pagination.tsx +88 -18
- package/docs/showcase/theme-customization.tsx +25 -2
- package/package.json +8 -5
|
@@ -283,11 +283,19 @@ export default function LoginShowcase() {
|
|
|
283
283
|
|
|
284
284
|
<Text as="p" size="2xs" tone="muted" align="center" className="leading-relaxed">
|
|
285
285
|
ログインすると{" "}
|
|
286
|
-
<Button
|
|
286
|
+
<Button
|
|
287
|
+
variant="link"
|
|
288
|
+
size="sm"
|
|
289
|
+
className="text-[length:var(--font-size-2xs, calc(var(--font-size-base) / var(--font-size-ratio) / var(--font-size-ratio)))]"
|
|
290
|
+
>
|
|
287
291
|
利用規約
|
|
288
292
|
</Button>{" "}
|
|
289
293
|
·{" "}
|
|
290
|
-
<Button
|
|
294
|
+
<Button
|
|
295
|
+
variant="link"
|
|
296
|
+
size="sm"
|
|
297
|
+
className="text-[length:var(--font-size-2xs, calc(var(--font-size-base) / var(--font-size-ratio) / var(--font-size-ratio)))]"
|
|
298
|
+
>
|
|
291
299
|
プライバシーポリシー
|
|
292
300
|
</Button>{" "}
|
|
293
301
|
に同意したものとみなされます。
|
|
@@ -588,7 +588,7 @@ function MonthGrid({ onPick }: { onPick: (d: DayCell) => void }) {
|
|
|
588
588
|
tone="destructive"
|
|
589
589
|
variant="outline"
|
|
590
590
|
shape="sharp"
|
|
591
|
-
className="text-[length:var(--font-size-2xs)]"
|
|
591
|
+
className="text-[length:var(--font-size-2xs, calc(var(--font-size-base) / var(--font-size-ratio) / var(--font-size-ratio)))]"
|
|
592
592
|
>
|
|
593
593
|
祝
|
|
594
594
|
</Badge>
|
|
@@ -368,7 +368,7 @@ function TabBar({ active, onChange }: { active: string; onChange: (id: string) =
|
|
|
368
368
|
onClick={() => onChange(t.id)}
|
|
369
369
|
aria-current={isActive ? "page" : undefined}
|
|
370
370
|
className={
|
|
371
|
-
"
|
|
371
|
+
"text-[length:var(--font-size-2xs, calc(var(--font-size-base) / var(--font-size-ratio) / var(--font-size-ratio)))] h-full flex-col rounded-none font-medium " +
|
|
372
372
|
(isActive ? "text-primary" : "text-muted-foreground")
|
|
373
373
|
}
|
|
374
374
|
>
|
|
@@ -637,7 +637,7 @@ function InboundTab({
|
|
|
637
637
|
<ToggleGroupItem
|
|
638
638
|
key={f.id}
|
|
639
639
|
value={f.id}
|
|
640
|
-
className="
|
|
640
|
+
className="text-[length:var(--font-size-xs, calc(var(--font-size-base) / var(--font-size-ratio)))] h-9 shrink-0 rounded-full whitespace-nowrap"
|
|
641
641
|
// The library's OWN counter pill, not a `Text` at 70% opacity (gh#643). The
|
|
642
642
|
// hand-rolled one measured 2.15:1 on the selected chip's --primary fill, because
|
|
643
643
|
// `opacity-70` blends whatever ink it inherits back towards the fill underneath it —
|
|
@@ -852,19 +852,19 @@ function OutboundTab({ seg, setSeg }: { seg: string; setSeg: (v: string) => void
|
|
|
852
852
|
<ResponsiveGrid columns={3} gap="xs">
|
|
853
853
|
<ToggleGroupItem
|
|
854
854
|
value="seal"
|
|
855
|
-
className="
|
|
855
|
+
className="text-[length:var(--font-size-xs, calc(var(--font-size-base) / var(--font-size-ratio)))] h-9 rounded-xl whitespace-nowrap"
|
|
856
856
|
>
|
|
857
857
|
Chờ niêm phong
|
|
858
858
|
</ToggleGroupItem>
|
|
859
859
|
<ToggleGroupItem
|
|
860
860
|
value="handoff"
|
|
861
|
-
className="
|
|
861
|
+
className="text-[length:var(--font-size-xs, calc(var(--font-size-base) / var(--font-size-ratio)))] h-9 rounded-xl whitespace-nowrap"
|
|
862
862
|
>
|
|
863
863
|
Chờ bàn giao
|
|
864
864
|
</ToggleGroupItem>
|
|
865
865
|
<ToggleGroupItem
|
|
866
866
|
value="done"
|
|
867
|
-
className="
|
|
867
|
+
className="text-[length:var(--font-size-xs, calc(var(--font-size-base) / var(--font-size-ratio)))] h-9 rounded-xl whitespace-nowrap"
|
|
868
868
|
>
|
|
869
869
|
Đã bàn giao
|
|
870
870
|
</ToggleGroupItem>
|
|
@@ -1018,7 +1018,7 @@ export default function AgencyHandyShowcase() {
|
|
|
1018
1018
|
<ToggleGroupItem
|
|
1019
1019
|
key={s}
|
|
1020
1020
|
value={s}
|
|
1021
|
-
className="
|
|
1021
|
+
className="text-[length:var(--font-size-2xs, calc(var(--font-size-base) / var(--font-size-ratio) / var(--font-size-ratio)))] size-7 rounded-md uppercase"
|
|
1022
1022
|
title={s}
|
|
1023
1023
|
>
|
|
1024
1024
|
{s[0]}
|
|
@@ -55,16 +55,14 @@ const THEME = `
|
|
|
55
55
|
--font-family-display: "Sora", system-ui, sans-serif;
|
|
56
56
|
--font-family-body: "Be Vietnam Pro", system-ui, "Noto Sans JP", sans-serif;
|
|
57
57
|
--font-family-sans: var(--font-family-body);
|
|
58
|
-
/* 80px hero
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
because this declaration is on an ancestor of the heading. */
|
|
58
|
+
/* 80px hero, from the display ramp's BASE KNOB alone (gh#834).
|
|
59
|
+
This used to need a second line — --font-size-5xl: 5rem — and the comment that stood here said
|
|
60
|
+
why: --font-size-5xl was declared at :root as var(--font-size-display), so it substituted on
|
|
61
|
+
<html> and this scope merely INHERITED the answer root had given. Heading size="5xl" painted
|
|
62
|
+
54px with the display knob alone. The step is an initial knob now and the formula sits at the
|
|
63
|
+
call site, so one line moves the ramp: re-measured here at 80px, with 3xl/4xl following to
|
|
64
|
+
48.83/62.5px. */
|
|
66
65
|
--font-size-display: 5rem;
|
|
67
|
-
--font-size-5xl: 5rem;
|
|
68
66
|
/* The glass bar, opted into once (gh#831). acme-website.tsx hand-wrote the identical pair,
|
|
69
67
|
which is why these are framework knobs now and not two literals on .fl-navbar. */
|
|
70
68
|
--topbar-background-alpha: 80%;
|
|
@@ -30,32 +30,40 @@
|
|
|
30
30
|
*
|
|
31
31
|
* ── WHAT IS STILL HAND-WRITTEN, AND WHY (the finding this page exists to produce) ───────────────
|
|
32
32
|
*
|
|
33
|
-
* Zero classes and zero literals did NOT mean zero CSS. Six things below
|
|
34
|
-
* objects because no PROP
|
|
33
|
+
* Zero classes and zero literals did NOT mean zero CSS. Six things below WERE inline `style`
|
|
34
|
+
* objects because no PROP reached them. Each one is a named gap, in the order a reader meets them;
|
|
35
|
+
* gh#839 closed the first two:
|
|
35
36
|
*
|
|
36
|
-
* 1. THE SECTION BAND — `paddingBlock: var(--space-section-band | --space-section-hero)`.
|
|
37
|
-
* gh#
|
|
38
|
-
* `
|
|
39
|
-
*
|
|
40
|
-
*
|
|
41
|
-
*
|
|
42
|
-
*
|
|
43
|
-
*
|
|
37
|
+
* 1. THE SECTION BAND — was `paddingBlock: var(--space-section-band | --space-section-hero)`.
|
|
38
|
+
* CLOSED by gh#839: the numeric `GapProp` ladder reaches the two band steps, so
|
|
39
|
+
* `pad={{ block: 20 }}` (80px) and `pad={{ block: 24 }}` (96px) state the rhythm through the
|
|
40
|
+
* public API, and every band below now does. Neither step has a compact variant and gh#839
|
|
41
|
+
* decided it should not: `--space-section-hero` measures 96px at 1440 AND at 320, which is
|
|
42
|
+
* deliberate — Tailwind UI's own marketing sections are `py-24 sm:py-32`, i.e. 96px is the
|
|
43
|
+
* NARROW rung of the industry pair and the desktop one is the step above. A numeric `pad`
|
|
44
|
+
* step is a VALUE (`pad={3}` is 12px everywhere); making one of them responsive would break
|
|
45
|
+
* that contract for the whole ladder. `--centered-shell-landing-main-padding-block-compact`
|
|
46
|
+
* is not the counter-example it looks like: 40px → 24px is a SHELL's main padding, not a
|
|
47
|
+
* marketing band, and it switches inside `.ui-centered-shell`'s own scope, which a plain
|
|
48
|
+
* `<section>` has no equivalent of.
|
|
49
|
+
* 2. THE CENTRED COLUMN — was `marginInline: auto` + `inlineSize: 100%` +
|
|
44
50
|
* `maxInlineSize: var(--page-measure-wide)` + `paddingInline: var(--space-6)`, repeated at
|
|
45
|
-
* every band. gh#
|
|
46
|
-
* `PageContainer measure="wide"`, reasoning
|
|
47
|
-
* a centred inner column and `PageContainer`
|
|
48
|
-
*
|
|
49
|
-
*
|
|
50
|
-
*
|
|
51
|
-
*
|
|
51
|
+
* every band. CLOSED by gh#839 as `Flex measure="wide"` + `pad={{ inline: 6 }}`. gh#831 minted
|
|
52
|
+
* `--page-measure-wide` and deliberately did not add `PageContainer measure="wide"`, reasoning
|
|
53
|
+
* that a marketing page is full-bleed sections with a centred inner column and `PageContainer`
|
|
54
|
+
* owns neither. That reasoning still holds and was not reopened — the prop went to `Flex`, the
|
|
55
|
+
* primitive a full-bleed `<section>` can actually put inside itself, where it has seven call
|
|
56
|
+
* sites on this page. `measure` adds NO gutter: `pad` owns that, and one band with two padding
|
|
57
|
+
* owners is how a measure and its inset drift apart.
|
|
58
|
+
* 3. THE STICKY BAR — `position: sticky` + `insetBlockStart` + `zIndex`. `Affix` has since
|
|
59
|
+
* shipped (28.8.0, with the measured placeholder that removes the 62px page jump) and this
|
|
60
|
+
* page has not yet been moved onto it; `Topbar` still has no `sticky` prop.
|
|
52
61
|
* 4. THE GLASS — `--topbar-background-alpha` / `--topbar-backdrop-blur-size` as inline custom
|
|
53
|
-
* properties. They are `initial` by design, so a call site MUST set them, and that
|
|
54
|
-
*
|
|
55
|
-
*
|
|
56
|
-
*
|
|
57
|
-
*
|
|
58
|
-
* foundation tier would close it properly. Measured result: `blur(8px)`.
|
|
62
|
+
* properties. They are `initial` by design, so a call site MUST set them, and that is correct.
|
|
63
|
+
* This used to read `--space-2`, borrowing a token that means something else, because the
|
|
64
|
+
* library had no blur SCALE at all. gh#837 closed that: `--blur-sm|md|lg` now exists in
|
|
65
|
+
* `foundation.css`, deliberately NOT `--scaling`-multiplied, because a backdrop blur is an
|
|
66
|
+
* optical effect and compact density does not mean thinner glass. Measured: `blur(8px)`.
|
|
59
67
|
* 5. THE HALO — `position: relative` / `overflow: hidden` on the band and `position: absolute` /
|
|
60
68
|
* `inset: 0` on the `.ui-brand-glow` layer. This is the package's own idiom (both siblings
|
|
61
69
|
* write `className="ui-brand-glow absolute inset-0"`), spelled as logical inline style so
|
|
@@ -155,20 +163,9 @@ import { useTranslation } from "@godxjp/ui/i18n";
|
|
|
155
163
|
import { Flex, ResponsiveGrid, Separator, Topbar } from "@godxjp/ui/layout";
|
|
156
164
|
import { AppSettingPicker } from "@godxjp/ui/navigation";
|
|
157
165
|
|
|
158
|
-
/* ── The
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
/** (2) The centred inner column. Nothing in the package owns this shape yet. */
|
|
162
|
-
const SHELL: CSSProperties = {
|
|
163
|
-
marginInline: "auto",
|
|
164
|
-
inlineSize: "100%",
|
|
165
|
-
maxInlineSize: "var(--page-measure-wide)",
|
|
166
|
-
paddingInline: "var(--space-6)",
|
|
167
|
-
};
|
|
168
|
-
|
|
169
|
-
/** (1) The section rhythm gh#831 named and no prop reads. */
|
|
170
|
-
const BAND: CSSProperties = { paddingBlock: "var(--space-section-band)" };
|
|
171
|
-
const HERO_BAND: CSSProperties = { paddingBlock: "var(--space-section-hero)" };
|
|
166
|
+
/* ── The inline-style constants named in the docblock. Every value is a token reference; no number
|
|
167
|
+
with a unit appears anywhere in this file. Gaps 1 and 2 no longer have one: `Flex measure` and
|
|
168
|
+
`Flex pad` state the band through props (gh#839). ──────────────────────────────────────────── */
|
|
172
169
|
|
|
173
170
|
/** (5) The halo host and the halo layer. */
|
|
174
171
|
const GLOW_HOST: CSSProperties = { position: "relative", overflow: "hidden" };
|
|
@@ -189,7 +186,7 @@ const GLASS_BAR: CSSProperties = {
|
|
|
189
186
|
package writes for CenteredShell's landing preset, so no page-local media query. */
|
|
190
187
|
"--topbar-inset": "max(var(--space-6), calc((100% - var(--page-measure-wide)) / 2))",
|
|
191
188
|
"--topbar-background-alpha": "85%",
|
|
192
|
-
"--topbar-backdrop-blur-size": "var(--
|
|
189
|
+
"--topbar-backdrop-blur-size": "var(--blur-md)",
|
|
193
190
|
} as CSSProperties;
|
|
194
191
|
|
|
195
192
|
/** Per-instance halo shaping — the four `--brand-glow-*` knobs, at the call site. */
|
|
@@ -446,7 +443,13 @@ export default function MarketingPage() {
|
|
|
446
443
|
className="ui-brand-glow"
|
|
447
444
|
style={{ ...GLOW_LAYER, ...HERO_GLOW }}
|
|
448
445
|
/>
|
|
449
|
-
<
|
|
446
|
+
<Flex
|
|
447
|
+
direction="col"
|
|
448
|
+
gap="none"
|
|
449
|
+
measure="wide"
|
|
450
|
+
pad={{ inline: 6, block: 24 }}
|
|
451
|
+
style={ABOVE_GLOW}
|
|
452
|
+
>
|
|
450
453
|
<ResponsiveGrid columns={{ base: 1, lg: 2 }} gap="xl" align="start">
|
|
451
454
|
<Reveal>
|
|
452
455
|
<Flex direction="col" gap="lg">
|
|
@@ -532,12 +535,12 @@ export default function MarketingPage() {
|
|
|
532
535
|
</Card>
|
|
533
536
|
</Reveal>
|
|
534
537
|
</ResponsiveGrid>
|
|
535
|
-
</
|
|
538
|
+
</Flex>
|
|
536
539
|
</section>
|
|
537
540
|
|
|
538
541
|
{/* ── LOGO WALL ────────────────────────────────────────────────────────────────────── */}
|
|
539
542
|
<section aria-labelledby="logos-title">
|
|
540
|
-
<
|
|
543
|
+
<Flex direction="col" gap="none" measure="wide" pad={{ inline: 6, block: 20 }}>
|
|
541
544
|
<Flex direction="col" gap="lg">
|
|
542
545
|
<Flex direction="col" gap="xs">
|
|
543
546
|
<Heading level={2} size="lg" id="logos-title">
|
|
@@ -567,13 +570,13 @@ export default function MarketingPage() {
|
|
|
567
570
|
))}
|
|
568
571
|
</Marquee>
|
|
569
572
|
</Flex>
|
|
570
|
-
</
|
|
573
|
+
</Flex>
|
|
571
574
|
</section>
|
|
572
575
|
|
|
573
576
|
{/* ── FEATURE GRID ─────────────────────────────────────────────────────────────────── */}
|
|
574
577
|
<section id="solutions" aria-labelledby="features-title">
|
|
575
578
|
<Flex as="div" direction="col" gap="none" surface="muted">
|
|
576
|
-
<
|
|
579
|
+
<Flex direction="col" gap="none" measure="wide" pad={{ inline: 6, block: 20 }}>
|
|
577
580
|
<Flex direction="col" gap="xl">
|
|
578
581
|
<Flex direction="col" gap="xs">
|
|
579
582
|
<Heading level={2} size="3xl" weight="bold" id="features-title">
|
|
@@ -613,13 +616,13 @@ export default function MarketingPage() {
|
|
|
613
616
|
))}
|
|
614
617
|
</ResponsiveGrid>
|
|
615
618
|
</Flex>
|
|
616
|
-
</
|
|
619
|
+
</Flex>
|
|
617
620
|
</Flex>
|
|
618
621
|
</section>
|
|
619
622
|
|
|
620
623
|
{/* ── TESTIMONIALS ─────────────────────────────────────────────────────────────────── */}
|
|
621
624
|
<section aria-labelledby="quotes-title">
|
|
622
|
-
<
|
|
625
|
+
<Flex direction="col" gap="none" measure="wide" pad={{ inline: 6, block: 20 }}>
|
|
623
626
|
<Flex direction="col" gap="lg">
|
|
624
627
|
<Heading level={2} size="3xl" weight="bold" id="quotes-title">
|
|
625
628
|
{t("marketingShowcase.testimonials.title")}
|
|
@@ -673,13 +676,13 @@ export default function MarketingPage() {
|
|
|
673
676
|
<CarouselDots />
|
|
674
677
|
</Carousel>
|
|
675
678
|
</Flex>
|
|
676
|
-
</
|
|
679
|
+
</Flex>
|
|
677
680
|
</section>
|
|
678
681
|
|
|
679
682
|
{/* ── PRICING ──────────────────────────────────────────────────────────────────────── */}
|
|
680
683
|
<section id="pricing" aria-labelledby="pricing-title">
|
|
681
684
|
<Flex as="div" direction="col" gap="none" surface="muted">
|
|
682
|
-
<
|
|
685
|
+
<Flex direction="col" gap="none" measure="wide" pad={{ inline: 6, block: 20 }}>
|
|
683
686
|
<Flex direction="col" gap="xl">
|
|
684
687
|
<Flex direction="col" gap="md">
|
|
685
688
|
<Heading level={2} size="3xl" weight="bold" id="pricing-title">
|
|
@@ -774,7 +777,7 @@ export default function MarketingPage() {
|
|
|
774
777
|
})}
|
|
775
778
|
</ResponsiveGrid>
|
|
776
779
|
</Flex>
|
|
777
|
-
</
|
|
780
|
+
</Flex>
|
|
778
781
|
</Flex>
|
|
779
782
|
</section>
|
|
780
783
|
|
|
@@ -785,7 +788,13 @@ export default function MarketingPage() {
|
|
|
785
788
|
className="ui-brand-glow"
|
|
786
789
|
style={{ ...GLOW_LAYER, ...CTA_GLOW }}
|
|
787
790
|
/>
|
|
788
|
-
<
|
|
791
|
+
<Flex
|
|
792
|
+
direction="col"
|
|
793
|
+
gap="none"
|
|
794
|
+
measure="wide"
|
|
795
|
+
pad={{ inline: 6, block: 20 }}
|
|
796
|
+
style={ABOVE_GLOW}
|
|
797
|
+
>
|
|
789
798
|
<Reveal on="view">
|
|
790
799
|
<Flex direction="col" gap="md" align="center">
|
|
791
800
|
<Heading level={2} size="4xl" weight="bold" align="center" id="cta-title">
|
|
@@ -810,14 +819,14 @@ export default function MarketingPage() {
|
|
|
810
819
|
</Text>
|
|
811
820
|
</Flex>
|
|
812
821
|
</Reveal>
|
|
813
|
-
</
|
|
822
|
+
</Flex>
|
|
814
823
|
</section>
|
|
815
824
|
</main>
|
|
816
825
|
|
|
817
826
|
{/* ── FOOTER ───────────────────────────────────────────────────────────────────────── */}
|
|
818
827
|
<footer>
|
|
819
828
|
<Flex as="div" direction="col" gap="none" surface="muted">
|
|
820
|
-
<
|
|
829
|
+
<Flex direction="col" gap="none" measure="wide" pad={{ inline: 6, block: 20 }}>
|
|
821
830
|
<Flex direction="col" gap="lg">
|
|
822
831
|
{/* ResponsiveGrid.Item span — the asymmetric footer both siblings hand-wrote a
|
|
823
832
|
`grid-template-columns: 1.4fr 1fr 1fr 1fr` for. It has shipped the whole time. */}
|
|
@@ -877,7 +886,7 @@ export default function MarketingPage() {
|
|
|
877
886
|
</Flex>
|
|
878
887
|
</Flex>
|
|
879
888
|
</Flex>
|
|
880
|
-
</
|
|
889
|
+
</Flex>
|
|
881
890
|
</Flex>
|
|
882
891
|
</footer>
|
|
883
892
|
</Flex>
|
|
@@ -299,7 +299,10 @@ function DetailPanel({ employee }: { employee: Employee }) {
|
|
|
299
299
|
{/* Per-day breakdown — quiet nested list, real Card chrome */}
|
|
300
300
|
<Card>
|
|
301
301
|
<CardHeader>
|
|
302
|
-
<CardTitle
|
|
302
|
+
<CardTitle
|
|
303
|
+
level={2}
|
|
304
|
+
className="text-[length:var(--font-size-xs, calc(var(--font-size-base) / var(--font-size-ratio)))]"
|
|
305
|
+
>
|
|
303
306
|
今週の打刻
|
|
304
307
|
</CardTitle>
|
|
305
308
|
<CardAction>
|
|
@@ -36,6 +36,7 @@ import {
|
|
|
36
36
|
import { Button, Text } from "@godxjp/ui/general";
|
|
37
37
|
import { Flex, PageContainer } from "@godxjp/ui/layout";
|
|
38
38
|
import { Pagination } from "@godxjp/ui/navigation";
|
|
39
|
+
import { useTranslation } from "@godxjp/ui/i18n";
|
|
39
40
|
import {
|
|
40
41
|
Select,
|
|
41
42
|
SelectContent,
|
|
@@ -91,8 +92,13 @@ function makeRows(count: number, prefix = "ATT"): Attendance[] {
|
|
|
91
92
|
date: `2026-06-${String((i % 28) + 1).padStart(2, "0")}`,
|
|
92
93
|
employee: NAMES[i % NAMES.length],
|
|
93
94
|
dept: DEPTS[i % DEPTS.length],
|
|
94
|
-
|
|
95
|
-
|
|
95
|
+
// `0${startHour}` hard-coded the leading zero and printed `010:07` at startHour 10 —
|
|
96
|
+
// the hand-built formatting this repo's own rules forbid. `padStart` on the hour, the
|
|
97
|
+
// same way the line above already does it for the date.
|
|
98
|
+
clockIn: `${String(startHour).padStart(2, "0")}:${["02", "58", "31", "07"][i % 4]}`,
|
|
99
|
+
clockOut: ot
|
|
100
|
+
? `${String(18 + Math.floor(ot / 60)).padStart(2, "0")}:${String(ot % 60).padStart(2, "0")}`
|
|
101
|
+
: "18:00",
|
|
96
102
|
overtime: ot,
|
|
97
103
|
status: statuses[i % statuses.length],
|
|
98
104
|
};
|
|
@@ -102,20 +108,38 @@ function makeRows(count: number, prefix = "ATT"): Attendance[] {
|
|
|
102
108
|
const minutesFmt = (m: number) => (m === 0 ? "—" : `${m}分`);
|
|
103
109
|
|
|
104
110
|
// Columns are shared between the cards (pure data — no per-card state inside).
|
|
111
|
+
/* EVERY column declares a width, not just the first (gh#849).
|
|
112
|
+
*
|
|
113
|
+
* Measured at 2000px before this, as empty pixels between one cell's ink and the next's:
|
|
114
|
+
*
|
|
115
|
+
* 部署 → 出勤 521 残業 → 状態 57
|
|
116
|
+
* 従業員 → 部署 411 勤務日 → 従業員 40
|
|
117
|
+
* 退勤 → 残業 280
|
|
118
|
+
*
|
|
119
|
+
* A 521px hole beside a 40px seam, in one row. Only `勤務日` carried a width, so
|
|
120
|
+
* `table-layout: auto` sized the rest from content and dumped ALL the slack of a 1952px card
|
|
121
|
+
* into the two free-text columns — and because those are start-aligned while the measures are
|
|
122
|
+
* end-aligned, the slack opened as craters BETWEEN columns rather than inside them.
|
|
123
|
+
*
|
|
124
|
+
* With a width on every column the engine distributes the excess proportionally instead, so the
|
|
125
|
+
* gaps scale together and no single one dominates. The numbers are a ratio, not a measurement:
|
|
126
|
+
* the two free-text columns get the larger share because they are the ones that actually wrap. */
|
|
105
127
|
const columns: ColumnDef<Attendance>[] = [
|
|
106
128
|
{ key: "date", header: "勤務日", width: "w-28" },
|
|
107
|
-
{ key: "employee", header: "従業員" },
|
|
108
|
-
{ key: "dept", header: "部署", hiddenOnMobile: true },
|
|
129
|
+
{ key: "employee", header: "従業員", width: "w-44" },
|
|
130
|
+
{ key: "dept", header: "部署", hiddenOnMobile: true, width: "w-36" },
|
|
109
131
|
{
|
|
110
132
|
key: "clockIn",
|
|
111
133
|
header: "出勤",
|
|
112
134
|
align: "right",
|
|
135
|
+
width: "w-24",
|
|
113
136
|
render: (row) => <Text tabular>{row.clockIn}</Text>,
|
|
114
137
|
},
|
|
115
138
|
{
|
|
116
139
|
key: "clockOut",
|
|
117
140
|
header: "退勤",
|
|
118
141
|
align: "right",
|
|
142
|
+
width: "w-24",
|
|
119
143
|
render: (row) => <Text tabular>{row.clockOut}</Text>,
|
|
120
144
|
},
|
|
121
145
|
{
|
|
@@ -123,12 +147,17 @@ const columns: ColumnDef<Attendance>[] = [
|
|
|
123
147
|
header: "残業",
|
|
124
148
|
align: "right",
|
|
125
149
|
hiddenOnMobile: true,
|
|
150
|
+
width: "w-24",
|
|
126
151
|
render: (row) => <Text tabular>{minutesFmt(row.overtime)}</Text>,
|
|
127
152
|
},
|
|
128
153
|
{
|
|
129
154
|
key: "status",
|
|
130
155
|
header: "状態",
|
|
131
|
-
|
|
156
|
+
/* END, not CENTER (gh#849). The status column carries the table's right edge; centring a
|
|
157
|
+
* ~90px pill in it put the badges 102px inboard while the header count, the column headers
|
|
158
|
+
* and the pagination all landed on 17px. */
|
|
159
|
+
align: "right",
|
|
160
|
+
width: "w-32",
|
|
132
161
|
render: (row) => <Badge status={row.status}>{STATUS_LABEL[row.status]}</Badge>,
|
|
133
162
|
},
|
|
134
163
|
];
|
|
@@ -138,6 +167,8 @@ const columns: ColumnDef<Attendance>[] = [
|
|
|
138
167
|
const NUMBERED_DATA = makeRows(83, "OFS");
|
|
139
168
|
|
|
140
169
|
function NumberedPaginationCard() {
|
|
170
|
+
const { t } = useTranslation();
|
|
171
|
+
const fmt = useFormatters();
|
|
141
172
|
const [page, setPage] = React.useState(2); // start mid-set so prev/next are both live
|
|
142
173
|
const [pageSize, setPageSize] = React.useState(10);
|
|
143
174
|
|
|
@@ -150,7 +181,7 @@ function NumberedPaginationCard() {
|
|
|
150
181
|
<CardTitle level={2}>番号付き + 件数選択</CardTitle>
|
|
151
182
|
<CardAction>
|
|
152
183
|
<Text size="xs" tone="muted" tabular>
|
|
153
|
-
|
|
184
|
+
{t("showcase.pagination.recordCount", { count: NUMBERED_DATA.length })}
|
|
154
185
|
</Text>
|
|
155
186
|
</CardAction>
|
|
156
187
|
</CardHeader>
|
|
@@ -164,7 +195,9 @@ function NumberedPaginationCard() {
|
|
|
164
195
|
pageSize={pageSize}
|
|
165
196
|
pageSizeOptions={[10, 20, 50]}
|
|
166
197
|
showSizeChanger
|
|
167
|
-
showTotal={(total, [from, to]) =>
|
|
198
|
+
showTotal={(total, [from, to]) =>
|
|
199
|
+
`${fmt.number(from)}–${fmt.number(to)} / ${t("showcase.pagination.recordCount", { count: total })}`
|
|
200
|
+
}
|
|
168
201
|
onValueChange={(nextPage, nextSize) => {
|
|
169
202
|
setPage(nextPage);
|
|
170
203
|
setPageSize(nextSize);
|
|
@@ -181,6 +214,8 @@ const LOADMORE_DATA = makeRows(42, "LDM");
|
|
|
181
214
|
const LOADMORE_STEP = 8;
|
|
182
215
|
|
|
183
216
|
function LoadMoreCard() {
|
|
217
|
+
const { t } = useTranslation();
|
|
218
|
+
const fmt = useFormatters();
|
|
184
219
|
// Mid-load state at rest: some rows already revealed, more remaining.
|
|
185
220
|
const [visible, setVisible] = React.useState(LOADMORE_STEP * 2);
|
|
186
221
|
const [loading, setLoading] = React.useState(false);
|
|
@@ -205,7 +240,8 @@ function LoadMoreCard() {
|
|
|
205
240
|
<CardTitle level={2}>もっと読む</CardTitle>
|
|
206
241
|
<CardAction>
|
|
207
242
|
<Text size="xs" tone="muted" tabular>
|
|
208
|
-
{rows.length} /
|
|
243
|
+
{fmt.number(rows.length)} /{" "}
|
|
244
|
+
{t("showcase.pagination.recordCount", { count: LOADMORE_DATA.length })}
|
|
209
245
|
</Text>
|
|
210
246
|
</CardAction>
|
|
211
247
|
</CardHeader>
|
|
@@ -225,11 +261,13 @@ function LoadMoreCard() {
|
|
|
225
261
|
{!done && <ChevronDown aria-hidden="true" />}
|
|
226
262
|
{done
|
|
227
263
|
? "すべて表示しました"
|
|
228
|
-
:
|
|
264
|
+
: t("showcase.pagination.loadMore", {
|
|
265
|
+
count: Math.min(LOADMORE_STEP, remaining),
|
|
266
|
+
})}
|
|
229
267
|
</Button>
|
|
230
268
|
{!done && (
|
|
231
269
|
<Text size="xs" tone="muted" tabular>
|
|
232
|
-
|
|
270
|
+
{t("showcase.pagination.recordCount", { count: remaining })}
|
|
233
271
|
</Text>
|
|
234
272
|
)}
|
|
235
273
|
</Flex>
|
|
@@ -242,11 +280,11 @@ function LoadMoreCard() {
|
|
|
242
280
|
|
|
243
281
|
// Time-series 勤怠 logs grouped by month — offset paging is meaningless here,
|
|
244
282
|
// so we use cursor first/next (DataTable.Pagination) + a period Select to jump.
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
] as const;
|
|
283
|
+
/* The VALUE is data; the LABEL is a formatting decision and belongs to the active locale.
|
|
284
|
+
* Hard-coded "2026年 6月" rendered beside the library's own "Đầu" / "Tiếp" — two languages in one
|
|
285
|
+
* row, which is what the owner photographed. `Intl.DateTimeFormat` gives 「2026年6月」, "June 2026"
|
|
286
|
+
* and "tháng 6 năm 2026" from the same value. */
|
|
287
|
+
const PERIODS = [{ value: "2026-06" }, { value: "2026-05" }, { value: "2026-04" }] as const;
|
|
250
288
|
|
|
251
289
|
const PERIOD_DATA: Record<string, Attendance[]> = {
|
|
252
290
|
"2026-06": makeRows(7, "CUR-06"),
|
|
@@ -255,6 +293,8 @@ const PERIOD_DATA: Record<string, Attendance[]> = {
|
|
|
255
293
|
};
|
|
256
294
|
|
|
257
295
|
function CursorPeriodCard() {
|
|
296
|
+
const { t } = useTranslation();
|
|
297
|
+
const fmt = useFormatters();
|
|
258
298
|
const [period, setPeriod] = React.useState<string>("2026-06");
|
|
259
299
|
// cursor === id of the last row of the previous page; undefined = first page.
|
|
260
300
|
const [cursor, setCursor] = React.useState<string | undefined>(undefined);
|
|
@@ -284,7 +324,7 @@ function CursorPeriodCard() {
|
|
|
284
324
|
<SelectContent>
|
|
285
325
|
{PERIODS.map((p) => (
|
|
286
326
|
<SelectItem key={p.value} value={p.value}>
|
|
287
|
-
{p.
|
|
327
|
+
{fmt.month(p.value)}
|
|
288
328
|
</SelectItem>
|
|
289
329
|
))}
|
|
290
330
|
</SelectContent>
|
|
@@ -294,10 +334,19 @@ function CursorPeriodCard() {
|
|
|
294
334
|
<CardContent flush>
|
|
295
335
|
<DataTable data={rows} columns={columns} getRowId={(row) => row.id} density="compact" />
|
|
296
336
|
</CardContent>
|
|
297
|
-
|
|
337
|
+
{/* `DataTable.Pagination` owns its own inline inset, because it is normally the lone child
|
|
338
|
+
* of a FLUSH container. Here it shares a padded `CardContent` with the period label, so the
|
|
339
|
+
* two insets stack — measured 28px against the columns' 16px before this. Zeroing the slot's
|
|
340
|
+
* own knob hands the inset to the container that already supplies it; that is what
|
|
341
|
+
* `--table-pagination-padding-x` is for, and an instance override is the right tier for one
|
|
342
|
+
* element in one arrangement (docs/CUSTOMER-THEMING.md, tier 4). */}
|
|
343
|
+
<CardContent
|
|
344
|
+
className="border-t"
|
|
345
|
+
style={{ "--table-pagination-padding-x": "0" } as React.CSSProperties}
|
|
346
|
+
>
|
|
298
347
|
<Flex direction="row" align="center" justify="between" wrap gap="sm">
|
|
299
348
|
<Text size="xs" tone="muted" tabular>
|
|
300
|
-
{
|
|
349
|
+
{fmt.month(period)} · {t("showcase.pagination.recordCount", { count: all.length })}
|
|
301
350
|
</Text>
|
|
302
351
|
<DataTable.Pagination
|
|
303
352
|
cursor={lastVisibleId}
|
|
@@ -310,6 +359,27 @@ function CursorPeriodCard() {
|
|
|
310
359
|
);
|
|
311
360
|
}
|
|
312
361
|
|
|
362
|
+
/* COUNTS AND MONTHS FOLLOW THE ACTIVE LOCALE, and this page used to hard-code them.
|
|
363
|
+
*
|
|
364
|
+
* The library's own chrome is localized — the page-size Select renders "10 / trang" under `vi` —
|
|
365
|
+
* so a page that hard-codes `件` puts two languages in one row and they can never agree. That is
|
|
366
|
+
* exactly what the owner photographed: "11–20 / 83 件" beside "10 / trang".
|
|
367
|
+
*
|
|
368
|
+
* `Intl.NumberFormat` groups the number for the locale; the unit word comes from the library's own
|
|
369
|
+
* message catalogue, which already carries a CLDR plural map per language. Nothing here is a
|
|
370
|
+
* hand-built string. */
|
|
371
|
+
const useFormatters = () => {
|
|
372
|
+
const { locale } = useTranslation();
|
|
373
|
+
return React.useMemo(() => {
|
|
374
|
+
const number = new Intl.NumberFormat(locale);
|
|
375
|
+
const month = new Intl.DateTimeFormat(locale, { year: "numeric", month: "long" });
|
|
376
|
+
return {
|
|
377
|
+
number: (n: number) => number.format(n),
|
|
378
|
+
month: (iso: string) => month.format(new Date(`${iso}-01T00:00:00`)),
|
|
379
|
+
};
|
|
380
|
+
}, [locale]);
|
|
381
|
+
};
|
|
382
|
+
|
|
313
383
|
export default function Demo() {
|
|
314
384
|
return (
|
|
315
385
|
<PageContainer
|
|
@@ -302,7 +302,13 @@ export default function ThemeCustomizationShowcase() {
|
|
|
302
302
|
const [corner, setCorner] = React.useState("default");
|
|
303
303
|
const [custom, setCustom] = React.useState("#0F62FE");
|
|
304
304
|
const [regions, setRegions] = React.useState<string[]>(["JP", "VN"]);
|
|
305
|
-
|
|
305
|
+
/* UNBREAKABLE FIRST, ON PURPOSE (gh#840). With `maxTagCount={2}` the tag in third place is the
|
|
306
|
+
* one that collapses into `+N` — so while the 71-character id sat last, the chip that painted
|
|
307
|
+
* outside the control was never actually drawn on this page, and `maxTagTextLength` had nothing
|
|
308
|
+
* to demonstrate. Held first, the row now draws all three of the issue's answers at once: the
|
|
309
|
+
* long id truncated to its ceiling (full value in `title` and in the remover's name), a normal
|
|
310
|
+
* chip beside it, and a `+1` that names the tag behind it. */
|
|
311
|
+
const [labels, setLabels] = React.useState<string[]>([UNBREAKABLE, "本番", "ổn định"]);
|
|
306
312
|
const [plan, setPlan] = React.useState("standard");
|
|
307
313
|
const [channels, setChannels] = React.useState<string[]>(["email"]);
|
|
308
314
|
const [cadence, setCadence] = React.useState("weekly");
|
|
@@ -565,7 +571,19 @@ function ComponentsBoard(props: {
|
|
|
565
571
|
} = props;
|
|
566
572
|
|
|
567
573
|
return (
|
|
568
|
-
|
|
574
|
+
/* PADDED HERE, because nothing above this pads it (gh#841 follow-up). The board sits in
|
|
575
|
+
`<CardContent flush>` so the tab STRIP can meet the card edge — which is right, a line
|
|
576
|
+
variant's underline should run edge to edge — and `bodied` is a no-op on `variant="line"`
|
|
577
|
+
by design (`tabs.tsx`: `const bodiedCard = card && Boolean(bodied)`). So the body has no
|
|
578
|
+
inline inset from either side, and the form sat 1px from the card border: measured label
|
|
579
|
+
at x=25 against a card at x=24. `pad` on the board is the composition answer — the same
|
|
580
|
+
step `CardContent` would have used. */
|
|
581
|
+
<ResponsiveGrid
|
|
582
|
+
columns={{ base: 1, sm: 2, lg: 3 }}
|
|
583
|
+
gap="lg"
|
|
584
|
+
align="start"
|
|
585
|
+
pad={{ inline: "lg", block: "md" }}
|
|
586
|
+
>
|
|
569
587
|
{/* 1 · text entry */}
|
|
570
588
|
<Flex direction="col" gap="md">
|
|
571
589
|
<FormField
|
|
@@ -618,6 +636,11 @@ function ComponentsBoard(props: {
|
|
|
618
636
|
value={labels}
|
|
619
637
|
onValueChange={setLabels}
|
|
620
638
|
maxTagCount={2}
|
|
639
|
+
/* The ceiling the 71-character id is measured against (gh#840). 16 is the widest cut
|
|
640
|
+
that still leaves room for a second chip and the `+1` on one row at 375px; the value
|
|
641
|
+
is untouched — it stays in the chip's `title` and in the remover's accessible name,
|
|
642
|
+
which is the only reason truncating an identifier is allowed at all. */
|
|
643
|
+
maxTagTextLength={16}
|
|
621
644
|
placeholder={t("themeShowcase.form.labelsPlaceholder")}
|
|
622
645
|
/>
|
|
623
646
|
</FormField>
|