@godxjp/ui 23.3.0 → 23.4.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (80) hide show
  1. package/dist/components/data-display/card.d.ts +87 -1
  2. package/dist/components/data-display/card.js +88 -15
  3. package/dist/components/data-display/index.d.ts +5 -1
  4. package/dist/components/data-display/index.js +5 -0
  5. package/dist/components/data-display/thought-chain.d.ts +42 -0
  6. package/dist/components/data-display/thought-chain.js +177 -0
  7. package/dist/components/data-display/welcome.d.ts +26 -0
  8. package/dist/components/data-display/welcome.js +37 -0
  9. package/dist/components/data-entry/attachments.d.ts +19 -0
  10. package/dist/components/data-entry/attachments.js +322 -0
  11. package/dist/components/data-entry/index.d.ts +2 -0
  12. package/dist/components/data-entry/index.js +2 -0
  13. package/dist/components/feedback/dialog.d.ts +10 -7
  14. package/dist/components/feedback/dialog.js +40 -17
  15. package/dist/components/feedback/index.d.ts +1 -1
  16. package/dist/components/general/actions.d.ts +59 -0
  17. package/dist/components/general/actions.js +253 -0
  18. package/dist/components/general/activity.js +1 -0
  19. package/dist/components/general/float-button.d.ts +41 -0
  20. package/dist/components/general/float-button.js +336 -0
  21. package/dist/components/general/index.d.ts +6 -2
  22. package/dist/components/general/index.js +12 -1
  23. package/dist/components/general/typography.d.ts +79 -4
  24. package/dist/components/general/typography.js +598 -54
  25. package/dist/components/layout/draggable-panel.d.ts +18 -0
  26. package/dist/components/layout/draggable-panel.js +189 -0
  27. package/dist/components/layout/index.d.ts +2 -0
  28. package/dist/components/layout/index.js +2 -0
  29. package/dist/components/navigation/conversations.d.ts +39 -0
  30. package/dist/components/navigation/conversations.js +371 -0
  31. package/dist/components/navigation/index.d.ts +2 -0
  32. package/dist/components/navigation/index.js +2 -0
  33. package/dist/components/ui/card.js +1 -0
  34. package/dist/i18n/messages/en.json +53 -0
  35. package/dist/i18n/messages/ja.json +53 -0
  36. package/dist/i18n/messages/vi.json +53 -0
  37. package/dist/props/components/data-display.prop.d.ts +143 -1
  38. package/dist/props/components/data-entry.prop.d.ts +76 -0
  39. package/dist/props/components/feedback.prop.d.ts +34 -1
  40. package/dist/props/components/general.prop.d.ts +440 -3
  41. package/dist/props/components/index.d.ts +2 -2
  42. package/dist/props/components/layout.prop.d.ts +63 -1
  43. package/dist/props/components/navigation.prop.d.ts +150 -0
  44. package/dist/props/registry.d.ts +426 -0
  45. package/dist/props/registry.js +546 -0
  46. package/dist/props/vocabulary/index.d.ts +1 -1
  47. package/dist/props/vocabulary/interaction.prop.d.ts +113 -0
  48. package/dist/styles/card-layout.css +30 -0
  49. package/dist/styles/core.css +1 -0
  50. package/dist/styles/data-display-layout.css +236 -0
  51. package/dist/styles/float-button-layout.css +168 -0
  52. package/dist/styles/index.css +1 -0
  53. package/dist/styles/layout.css +196 -0
  54. package/dist/styles/navigation-layout.css +139 -0
  55. package/dist/styles/text-layout.css +156 -0
  56. package/dist/tokens/base.css +7 -0
  57. package/dist/tokens/components/actions.css +14 -0
  58. package/dist/tokens/components/attachments.css +15 -0
  59. package/dist/tokens/components/conversations.css +28 -0
  60. package/dist/tokens/components/draggable-panel.css +25 -0
  61. package/dist/tokens/components/float-button.css +39 -0
  62. package/dist/tokens/components/text.css +13 -0
  63. package/dist/tokens/components/thought-chain.css +31 -0
  64. package/dist/tokens/components/welcome.css +19 -0
  65. package/dist/tokens/foundation.css +2 -0
  66. package/docs/DESIGN-AUTHORITY.md +89 -0
  67. package/docs/FRAME-COVERAGE-REPORT.md +5 -2
  68. package/docs/data-display/card/examples/tab-list.tsx +97 -0
  69. package/docs/data-display/thought-chain.tsx +176 -0
  70. package/docs/data-display/welcome.tsx +115 -0
  71. package/docs/data-entry/attachments.tsx +160 -0
  72. package/docs/feedback/dialog.tsx +50 -0
  73. package/docs/general/actions.tsx +175 -0
  74. package/docs/general/float-button.tsx +133 -0
  75. package/docs/general/typography.tsx +63 -1
  76. package/docs/layout/draggable-panel.tsx +127 -0
  77. package/docs/navigation/conversations.tsx +176 -0
  78. package/docs/roadmap/parity-audit-layout-navigation-general.md +17 -16
  79. package/package.json +3 -3
  80. package/scripts/ui-audit.mjs +66 -2
@@ -0,0 +1,253 @@
1
+ "use client";
2
+ import { Fragment, jsx, jsxs } from "react/jsx-runtime";
3
+ import * as React from "react";
4
+ import { Check, Copy, MoreHorizontal, ThumbsDown, ThumbsUp, TriangleAlert } from "lucide-react";
5
+ import { useTranslation } from "../../i18n/use-translation.js";
6
+ import { cn } from "../../lib/utils.js";
7
+ import { Tooltip, TooltipContent, TooltipTrigger } from "../feedback/tooltip.js";
8
+ import {
9
+ DropdownMenu,
10
+ DropdownMenuContent,
11
+ DropdownMenuItem,
12
+ DropdownMenuTrigger
13
+ } from "../navigation/dropdown-menu.js";
14
+ import { Button } from "./button.js";
15
+ import { VisuallyHidden } from "./visually-hidden.js";
16
+ function isRtl(element) {
17
+ return element?.closest("[dir]")?.getAttribute("dir")?.toLowerCase() === "rtl";
18
+ }
19
+ function LabelledIconButton({
20
+ label,
21
+ children,
22
+ ...props
23
+ }) {
24
+ return /* @__PURE__ */ jsxs(Tooltip, { children: [
25
+ /* @__PURE__ */ jsx(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ jsx(Button, { type: "button", size: "icon-sm", variant: "ghost", "aria-label": label, ...props, children }) }),
26
+ /* @__PURE__ */ jsx(TooltipContent, { children: label })
27
+ ] });
28
+ }
29
+ const Actions = React.forwardRef(
30
+ ({ items, onClick, variant = "borderless", fadeIn, fadeInLeft, label, id, className, ...rest }, ref) => {
31
+ const { t } = useTranslation();
32
+ const nodes = React.useRef(/* @__PURE__ */ new Map());
33
+ const [focusKey, setFocusKey] = React.useState(null);
34
+ const navKeys = items.filter((item) => !item.actionRender).map((item) => item.key);
35
+ const tabStop = focusKey && navKeys.includes(focusKey) ? focusKey : navKeys[0];
36
+ const moveTo = (key) => {
37
+ if (!key) return;
38
+ setFocusKey(key);
39
+ nodes.current.get(key)?.focus();
40
+ };
41
+ const handleKeyDown = (key) => (event) => {
42
+ const rtl = isRtl(event.currentTarget);
43
+ const forward = rtl ? "ArrowLeft" : "ArrowRight";
44
+ const backward = rtl ? "ArrowRight" : "ArrowLeft";
45
+ const index = navKeys.indexOf(key);
46
+ if (event.key === forward) {
47
+ event.preventDefault();
48
+ moveTo(navKeys[(index + 1) % navKeys.length]);
49
+ } else if (event.key === backward) {
50
+ event.preventDefault();
51
+ moveTo(navKeys[(index - 1 + navKeys.length) % navKeys.length]);
52
+ } else if (event.key === "Home") {
53
+ event.preventDefault();
54
+ moveTo(navKeys[0]);
55
+ } else if (event.key === "End") {
56
+ event.preventDefault();
57
+ moveTo(navKeys[navKeys.length - 1]);
58
+ }
59
+ };
60
+ const dispatch = (item, keyPath, domEvent) => {
61
+ if (item.onItemClick) {
62
+ item.onItemClick(item);
63
+ return;
64
+ }
65
+ onClick?.({ item, key: keyPath[0], keyPath, domEvent });
66
+ };
67
+ return /* @__PURE__ */ jsx(
68
+ "div",
69
+ {
70
+ ref,
71
+ id,
72
+ role: "toolbar",
73
+ "aria-label": label ?? t("general.actions.label"),
74
+ "aria-orientation": "horizontal",
75
+ "data-slot": "actions",
76
+ "data-variant": variant,
77
+ "data-fade-in": fadeIn || fadeInLeft ? "" : void 0,
78
+ "data-fade-in-inline": fadeInLeft ? "" : void 0,
79
+ className: cn("ui-actions", className),
80
+ ...rest,
81
+ children: items.map((item) => {
82
+ if (item.actionRender) {
83
+ return /* @__PURE__ */ jsx(React.Fragment, { children: typeof item.actionRender === "function" ? item.actionRender(item) : item.actionRender }, item.key);
84
+ }
85
+ const name = item.label ?? item.key;
86
+ const shared = {
87
+ ref: (node) => {
88
+ if (node) nodes.current.set(item.key, node);
89
+ else nodes.current.delete(item.key);
90
+ },
91
+ tabIndex: tabStop === item.key ? 0 : -1,
92
+ onFocus: () => setFocusKey(item.key),
93
+ onKeyDown: handleKeyDown(item.key),
94
+ variant: item.danger ? "destructive" : "ghost"
95
+ };
96
+ if (item.subItems && item.subItems.length > 0) {
97
+ return /* @__PURE__ */ jsxs(DropdownMenu, { children: [
98
+ /* @__PURE__ */ jsx(DropdownMenuTrigger, { asChild: true, children: /* @__PURE__ */ jsx(Button, { type: "button", size: "icon-sm", "aria-label": name, ...shared, children: item.icon ?? /* @__PURE__ */ jsx(MoreHorizontal, { "aria-hidden": "true" }) }) }),
99
+ /* @__PURE__ */ jsx(DropdownMenuContent, { align: "start", children: item.subItems.map((sub) => /* @__PURE__ */ jsxs(
100
+ DropdownMenuItem,
101
+ {
102
+ variant: sub.danger ? "destructive" : void 0,
103
+ onSelect: (event) => dispatch(
104
+ sub,
105
+ [sub.key, item.key],
106
+ event
107
+ ),
108
+ children: [
109
+ sub.icon,
110
+ sub.label ?? sub.key
111
+ ]
112
+ },
113
+ sub.key
114
+ )) })
115
+ ] }, item.key);
116
+ }
117
+ return /* @__PURE__ */ jsx(
118
+ LabelledIconButton,
119
+ {
120
+ label: name,
121
+ onClick: (event) => dispatch(item, [item.key], event),
122
+ ...shared,
123
+ children: item.icon
124
+ },
125
+ item.key
126
+ );
127
+ })
128
+ }
129
+ );
130
+ }
131
+ );
132
+ Actions.displayName = "Actions";
133
+ const ActionsItem = React.forwardRef(
134
+ ({ status = "default", defaultIcon, runningIcon, label, id, className, ...rest }, ref) => {
135
+ const { t } = useTranslation();
136
+ const base = label ?? t("general.actions.label");
137
+ const name = status === "default" ? base : `${base} \xB7 ${t(`general.actions.status.${status}`)}`;
138
+ const glyph = status === "loading" ? null : status === "error" ? /* @__PURE__ */ jsx(TriangleAlert, { "aria-hidden": "true" }) : status === "running" ? runningIcon ?? defaultIcon : defaultIcon;
139
+ return /* @__PURE__ */ jsxs(Tooltip, { children: [
140
+ /* @__PURE__ */ jsx(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ jsx(
141
+ Button,
142
+ {
143
+ ref,
144
+ id,
145
+ type: "button",
146
+ size: "icon-sm",
147
+ variant: "ghost",
148
+ "data-slot": "actions-item",
149
+ "data-status": status,
150
+ "aria-label": name,
151
+ "aria-busy": status === "loading" || void 0,
152
+ loading: status === "loading",
153
+ className: cn("ui-actions-item", className),
154
+ ...rest,
155
+ children: glyph
156
+ }
157
+ ) }),
158
+ /* @__PURE__ */ jsx(TooltipContent, { children: name })
159
+ ] });
160
+ }
161
+ );
162
+ ActionsItem.displayName = "ActionsItem";
163
+ const ActionsCopy = React.forwardRef(
164
+ ({ text = "", icon, label, onCopy, id, className, ...rest }, ref) => {
165
+ const { t } = useTranslation();
166
+ const [copied, setCopied] = React.useState(false);
167
+ const timer = React.useRef(void 0);
168
+ React.useEffect(() => () => clearTimeout(timer.current), []);
169
+ const name = label ?? t("general.actions.copy");
170
+ const run = async () => {
171
+ try {
172
+ await navigator.clipboard?.writeText(text);
173
+ } catch {
174
+ return;
175
+ }
176
+ onCopy?.(text);
177
+ setCopied(true);
178
+ clearTimeout(timer.current);
179
+ timer.current = setTimeout(() => setCopied(false), 3e3);
180
+ };
181
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
182
+ /* @__PURE__ */ jsxs(Tooltip, { children: [
183
+ /* @__PURE__ */ jsx(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ jsx(
184
+ Button,
185
+ {
186
+ ref,
187
+ id,
188
+ type: "button",
189
+ size: "icon-sm",
190
+ variant: "ghost",
191
+ "data-slot": "actions-copy",
192
+ "data-copied": copied ? "" : void 0,
193
+ "aria-label": name,
194
+ className: cn("ui-actions-copy", className),
195
+ onClick: run,
196
+ ...rest,
197
+ children: copied ? /* @__PURE__ */ jsx(Check, { "aria-hidden": "true" }) : icon ?? /* @__PURE__ */ jsx(Copy, { "aria-hidden": "true" })
198
+ }
199
+ ) }),
200
+ /* @__PURE__ */ jsx(TooltipContent, { children: name })
201
+ ] }),
202
+ /* @__PURE__ */ jsx(VisuallyHidden, { role: "status", "aria-live": "polite", children: copied ? t("general.actions.copied") : "" })
203
+ ] });
204
+ }
205
+ );
206
+ ActionsCopy.displayName = "ActionsCopy";
207
+ const ActionsFeedback = React.forwardRef(
208
+ ({ value = "default", onChange, label, likeLabel, dislikeLabel, id, className, ...rest }, ref) => {
209
+ const { t } = useTranslation();
210
+ const toggle = (next) => onChange?.(value === next ? "default" : next);
211
+ return /* @__PURE__ */ jsxs(
212
+ "div",
213
+ {
214
+ ref,
215
+ id,
216
+ role: "group",
217
+ "aria-label": label ?? t("general.actions.feedback"),
218
+ "data-slot": "actions-feedback",
219
+ className: cn("ui-actions-feedback", className),
220
+ ...rest,
221
+ children: [
222
+ /* @__PURE__ */ jsx(
223
+ LabelledIconButton,
224
+ {
225
+ label: likeLabel ?? t("general.actions.like"),
226
+ "aria-pressed": value === "like",
227
+ "data-pressed": value === "like" ? "" : void 0,
228
+ onClick: () => toggle("like"),
229
+ children: /* @__PURE__ */ jsx(ThumbsUp, { "aria-hidden": "true" })
230
+ }
231
+ ),
232
+ /* @__PURE__ */ jsx(
233
+ LabelledIconButton,
234
+ {
235
+ label: dislikeLabel ?? t("general.actions.dislike"),
236
+ "aria-pressed": value === "dislike",
237
+ "data-pressed": value === "dislike" ? "" : void 0,
238
+ onClick: () => toggle("dislike"),
239
+ children: /* @__PURE__ */ jsx(ThumbsDown, { "aria-hidden": "true" })
240
+ }
241
+ )
242
+ ]
243
+ }
244
+ );
245
+ }
246
+ );
247
+ ActionsFeedback.displayName = "ActionsFeedback";
248
+ export {
249
+ Actions,
250
+ ActionsCopy,
251
+ ActionsFeedback,
252
+ ActionsItem
253
+ };
@@ -1,3 +1,4 @@
1
+ "use client";
1
2
  import { Fragment, jsx, jsxs } from "react/jsx-runtime";
2
3
  import * as React from "react";
3
4
  import { cn } from "../../lib/utils.js";
@@ -0,0 +1,41 @@
1
+ import * as React from "react";
2
+ import type { FloatButtonBackTopProp, FloatButtonGroupProp, FloatButtonShapeProp, FloatButtonTooltipProp, FloatButtonTypeProp } from "../../props/components/general.prop.js";
3
+ export type { FloatButtonBackTopProp, FloatButtonBackTopProp as FloatButtonBackTopProps, FloatButtonBadgeProp, FloatButtonGroupProp, FloatButtonGroupProp as FloatButtonGroupProps, FloatButtonPlacementProp, FloatButtonProp, FloatButtonProp as FloatButtonProps, FloatButtonShapeProp, FloatButtonTooltipProp, FloatButtonTriggerProp, FloatButtonTypeProp, } from "../../props/components/general.prop.js";
4
+ /**
5
+ * FloatButton.Group — a stack of corner actions, optionally behind one trigger.
6
+ *
7
+ * Two modes, and `trigger` is the switch, exactly as in antd: WITHOUT it every child is visible and
8
+ * no trigger is drawn; WITH it the children collapse behind a trigger that opens them on click or
9
+ * on hover.
10
+ */
11
+ declare function FloatButtonGroup({ children, trigger, open, onOpenChange, closeIcon, placement, shape, type, icon, disabled, className, onClick, ...rest }: FloatButtonGroupProp): React.JSX.Element;
12
+ /**
13
+ * FloatButton.BackTop — the corner action that returns a scroll container to the top.
14
+ *
15
+ * `target` is antd's, and it is the answer to the objection that a document-bound BackTop competes
16
+ * with a shell that owns its own scroll: pass `() => element` and this watches that element, not
17
+ * the document. Under `prefers-reduced-motion` the scroll is instant rather than tweened
18
+ * (WCAG 2.3.3), which is antd's behaviour too.
19
+ */
20
+ declare function FloatButtonBackTop({ visibilityHeight, target, duration, showProgress, onClick, icon, tooltip, className, ...rest }: FloatButtonBackTopProp): React.JSX.Element | null;
21
+ /**
22
+ * The compound, exactly as antd exports it: one name, two sub-parts on it. A consumer porting a
23
+ * screen writes `<FloatButton.Group>` / `<FloatButton.BackTop>` unchanged.
24
+ */
25
+ export declare const FloatButton: React.ForwardRefExoticComponent<Omit<React.ButtonHTMLAttributes<HTMLButtonElement>, "color" | "content" | "type"> & {
26
+ icon?: import("../../props/index.js").ChildrenProp;
27
+ description?: import("../../props/index.js").DescriptionProp;
28
+ content?: import("../../props/index.js").ChildrenProp;
29
+ type?: FloatButtonTypeProp;
30
+ shape?: FloatButtonShapeProp;
31
+ tooltip?: import("../../props/index.js").ChildrenProp | FloatButtonTooltipProp;
32
+ href?: string;
33
+ target?: React.HTMLAttributeAnchorTarget;
34
+ badge?: import("./float-button.js").FloatButtonBadgeProp;
35
+ disabled?: import("../../props/index.js").DisabledProp;
36
+ htmlType?: "button" | "submit" | "reset";
37
+ className?: import("../../props/index.js").ClassNameProp;
38
+ } & React.RefAttributes<HTMLElement>> & {
39
+ Group: typeof FloatButtonGroup;
40
+ BackTop: typeof FloatButtonBackTop;
41
+ };
@@ -0,0 +1,336 @@
1
+ "use client";
2
+ import { Fragment, jsx, jsxs } from "react/jsx-runtime";
3
+ import * as React from "react";
4
+ import { ChevronUp, FileText, X } from "lucide-react";
5
+ import { useTranslation } from "../../i18n/use-translation.js";
6
+ import { useMediaQuery } from "../../lib/hooks.js";
7
+ import { numberFormat } from "../../lib/intl-cache.js";
8
+ import { cn } from "../../lib/utils.js";
9
+ import { Tooltip, TooltipContent, TooltipTrigger } from "../feedback/tooltip.js";
10
+ import { Button } from "./button.js";
11
+ const GroupContext = React.createContext(null);
12
+ const DEFAULT_ICON = /* @__PURE__ */ jsx(FileText, { "aria-hidden": "true" });
13
+ const BUTTON_VARIANT = {
14
+ default: "outline",
15
+ primary: "default"
16
+ };
17
+ function isRenderable(node) {
18
+ return node != null && node !== false && node !== true && node !== "";
19
+ }
20
+ function toTooltipProps(tooltip) {
21
+ if (tooltip == null) return null;
22
+ if (typeof tooltip === "object" && !React.isValidElement(tooltip) && "title" in tooltip) {
23
+ return tooltip;
24
+ }
25
+ return { title: tooltip };
26
+ }
27
+ function FloatButtonBadge({
28
+ badge,
29
+ locale
30
+ }) {
31
+ const { count, dot, overflowCount = 99, showZero = false, color } = badge;
32
+ if (!dot) {
33
+ if (count == null) return null;
34
+ if (count === 0 && !showZero) return null;
35
+ }
36
+ const label = dot ? null : count != null && count > overflowCount ? `${numberFormat(locale).format(overflowCount)}+` : numberFormat(locale).format(count ?? 0);
37
+ return /* @__PURE__ */ jsx(
38
+ "span",
39
+ {
40
+ "data-slot": "float-button-badge",
41
+ "data-dot": dot ? "" : void 0,
42
+ className: "ui-float-button-badge",
43
+ style: color ? { "--float-button-badge-background": color } : void 0,
44
+ children: label
45
+ }
46
+ );
47
+ }
48
+ const FloatButtonRoot = React.forwardRef(function FloatButton({
49
+ icon,
50
+ description,
51
+ content,
52
+ type = "default",
53
+ shape = "circle",
54
+ tooltip,
55
+ href,
56
+ target,
57
+ badge,
58
+ disabled,
59
+ htmlType = "button",
60
+ className,
61
+ children,
62
+ ...rest
63
+ }, ref) {
64
+ const { locale } = useTranslation();
65
+ const group = React.useContext(GroupContext);
66
+ const mergedShape = group?.shape ?? shape;
67
+ const mergedContent = isRenderable(content) ? content : description;
68
+ const hasContent = isRenderable(mergedContent);
69
+ const mergedIcon = !hasContent && icon == null ? DEFAULT_ICON : icon;
70
+ if (process.env.NODE_ENV !== "production" && mergedShape === "circle" && hasContent) {
71
+ console.warn(
72
+ "[@godxjp/ui] FloatButton: `content` is supported only when `shape` is `square` \u2014 a circle has no room for a line of text."
73
+ );
74
+ }
75
+ const tooltipProps = toTooltipProps(tooltip);
76
+ const ariaLabel = rest["aria-label"] ?? (!hasContent && typeof tooltipProps?.title === "string" ? tooltipProps.title : void 0);
77
+ const inner = /* @__PURE__ */ jsxs(Fragment, { children: [
78
+ isRenderable(mergedIcon) ? /* @__PURE__ */ jsx("span", { "data-slot": "float-button-icon", className: "ui-float-button-icon", children: mergedIcon }) : null,
79
+ hasContent ? /* @__PURE__ */ jsx("span", { "data-slot": "float-button-content", className: "ui-float-button-content", children: mergedContent }) : null,
80
+ children,
81
+ badge ? /* @__PURE__ */ jsx(FloatButtonBadge, { badge, locale }) : null
82
+ ] });
83
+ const shared = {
84
+ "data-slot": "float-button",
85
+ "data-type": type,
86
+ "data-shape": mergedShape,
87
+ "data-individual": group?.individual ?? true ? "" : void 0,
88
+ "data-icon-only": hasContent ? void 0 : "",
89
+ className: cn("ui-float-button", className),
90
+ variant: BUTTON_VARIANT[type],
91
+ shape: mergedShape === "circle" ? "pill" : "default",
92
+ size: "icon-lg"
93
+ };
94
+ const node = href ? /* @__PURE__ */ jsx(Button, { ...shared, asChild: true, children: /* @__PURE__ */ jsx(
95
+ "a",
96
+ {
97
+ ...rest,
98
+ ref,
99
+ href: disabled ? void 0 : href,
100
+ target,
101
+ "aria-label": ariaLabel,
102
+ "aria-disabled": disabled || void 0,
103
+ "data-disabled": disabled ? "" : void 0,
104
+ children: inner
105
+ }
106
+ ) }) : /* @__PURE__ */ jsx(
107
+ Button,
108
+ {
109
+ ...shared,
110
+ ...rest,
111
+ ref,
112
+ type: htmlType,
113
+ disabled,
114
+ "aria-label": ariaLabel,
115
+ children: inner
116
+ }
117
+ );
118
+ if (!tooltipProps || !isRenderable(tooltipProps.title)) return node;
119
+ return /* @__PURE__ */ jsxs(Tooltip, { children: [
120
+ /* @__PURE__ */ jsx(TooltipTrigger, { asChild: true, children: node }),
121
+ /* @__PURE__ */ jsx(
122
+ TooltipContent,
123
+ {
124
+ side: tooltipProps.side,
125
+ align: tooltipProps.align,
126
+ sideOffset: tooltipProps.sideOffset,
127
+ children: tooltipProps.title
128
+ }
129
+ )
130
+ ] });
131
+ });
132
+ const PLACEMENTS = ["top", "left", "right", "bottom"];
133
+ function FloatButtonGroup({
134
+ children,
135
+ trigger,
136
+ open,
137
+ onOpenChange,
138
+ closeIcon,
139
+ placement,
140
+ shape = "circle",
141
+ type = "default",
142
+ icon,
143
+ disabled,
144
+ className,
145
+ onClick,
146
+ ...rest
147
+ }) {
148
+ const { t } = useTranslation();
149
+ const rootRef = React.useRef(null);
150
+ const isMenuMode = trigger === "click" || trigger === "hover";
151
+ const mergedPlacement = PLACEMENTS.includes(placement) ? placement : "top";
152
+ const individual = shape === "circle";
153
+ const [uncontrolledOpen, setUncontrolledOpen] = React.useState(false);
154
+ const isControlled = open != null;
155
+ const isOpen = isControlled ? open : uncontrolledOpen;
156
+ if (process.env.NODE_ENV !== "production" && isControlled && !trigger) {
157
+ console.warn(
158
+ "[@godxjp/ui] FloatButton.Group: `open` needs to be used together with `trigger`."
159
+ );
160
+ }
161
+ const setOpen = React.useCallback(
162
+ (next) => {
163
+ if (disabled || next === isOpen) return;
164
+ if (!isControlled) setUncontrolledOpen(next);
165
+ onOpenChange?.(next);
166
+ },
167
+ [disabled, isOpen, isControlled, onOpenChange]
168
+ );
169
+ React.useEffect(() => {
170
+ if (trigger !== "click") return;
171
+ const root = rootRef.current;
172
+ if (!root) return;
173
+ const doc = root.ownerDocument;
174
+ const onDocClick = (event) => {
175
+ if (event.composedPath().includes(root)) return;
176
+ setOpen(false);
177
+ };
178
+ doc.addEventListener("click", onDocClick, { capture: true });
179
+ return () => doc.removeEventListener("click", onDocClick, { capture: true });
180
+ }, [trigger, setOpen]);
181
+ const listContext = React.useMemo(() => ({ shape, individual }), [shape, individual]);
182
+ const triggerContext = React.useMemo(() => ({ shape, individual: true }), [shape]);
183
+ const list = /* @__PURE__ */ jsx(GroupContext.Provider, { value: listContext, children: /* @__PURE__ */ jsx("div", { "data-slot": "float-button-list", className: "ui-float-button-list", children }) });
184
+ return /* @__PURE__ */ jsxs(
185
+ "div",
186
+ {
187
+ ref: rootRef,
188
+ "data-slot": "float-button-group",
189
+ "data-placement": isMenuMode ? mergedPlacement : void 0,
190
+ "data-menu-mode": isMenuMode ? "" : void 0,
191
+ "data-individual": individual ? "" : void 0,
192
+ "data-state": isMenuMode ? isOpen ? "open" : "closed" : void 0,
193
+ className: cn("ui-float-button-group", className),
194
+ onMouseEnter: trigger === "hover" ? () => setOpen(true) : void 0,
195
+ onMouseLeave: trigger === "hover" ? () => setOpen(false) : void 0,
196
+ children: [
197
+ isMenuMode ? isOpen ? list : null : list,
198
+ isMenuMode ? /* @__PURE__ */ jsx(GroupContext.Provider, { value: triggerContext, children: /* @__PURE__ */ jsx(
199
+ FloatButtonRoot,
200
+ {
201
+ ...rest,
202
+ type,
203
+ disabled,
204
+ icon: isOpen ? closeIcon ?? /* @__PURE__ */ jsx(X, { "aria-hidden": "true" }) : icon,
205
+ "aria-label": rest["aria-label"] ?? t("ui.floatButton.actions"),
206
+ "aria-expanded": isOpen,
207
+ className: "ui-float-button-trigger",
208
+ onClick: (event) => {
209
+ if (trigger === "click") setOpen(!isOpen);
210
+ onClick?.(event);
211
+ }
212
+ }
213
+ ) }) : null
214
+ ]
215
+ }
216
+ );
217
+ }
218
+ function easeInOutCubic(t, b, c, d) {
219
+ const cc = c - b;
220
+ let tt = t / (d / 2);
221
+ if (tt < 1) return cc / 2 * tt * tt * tt + b;
222
+ tt -= 2;
223
+ return cc / 2 * (tt * tt * tt + 2) + b;
224
+ }
225
+ function readScrollTop(target) {
226
+ if (!target) return 0;
227
+ if (target === window) return window.pageYOffset || window.document.documentElement.scrollTop;
228
+ if ("documentElement" in target && target.documentElement) {
229
+ return target.documentElement.scrollTop;
230
+ }
231
+ return target.scrollTop;
232
+ }
233
+ function writeScrollTop(target, value) {
234
+ if (!target) return;
235
+ if (target === window) {
236
+ window.scrollTo(window.pageXOffset, value);
237
+ } else if ("documentElement" in target && target.documentElement) {
238
+ target.documentElement.scrollTop = value;
239
+ } else {
240
+ target.scrollTop = value;
241
+ }
242
+ }
243
+ function readScrollProgress(target) {
244
+ if (!target) return 0;
245
+ const element = target === window ? window.document.documentElement : "documentElement" in target && target.documentElement ? target.documentElement : target;
246
+ if (!element) return 0;
247
+ const max = Math.max(element.scrollHeight - element.clientHeight, 0);
248
+ if (max <= 0) return 0;
249
+ return Math.min(Math.max(readScrollTop(target) / max, 0), 1);
250
+ }
251
+ function FloatButtonBackTop({
252
+ visibilityHeight = 400,
253
+ target,
254
+ duration = 450,
255
+ showProgress = false,
256
+ onClick,
257
+ icon,
258
+ tooltip,
259
+ className,
260
+ ...rest
261
+ }) {
262
+ const { t } = useTranslation();
263
+ const reduceMotion = useMediaQuery("(prefers-reduced-motion: reduce)");
264
+ const nodeRef = React.useRef(null);
265
+ const [visible, setVisible] = React.useState(visibilityHeight === 0);
266
+ const [progress, setProgress] = React.useState(0);
267
+ const getTarget = React.useCallback(
268
+ () => target ? target() : nodeRef.current?.ownerDocument ?? window,
269
+ [target]
270
+ );
271
+ React.useEffect(() => {
272
+ const container = getTarget();
273
+ let frame = 0;
274
+ const sync = () => {
275
+ frame = 0;
276
+ setVisible(readScrollTop(container) >= visibilityHeight);
277
+ if (showProgress) setProgress(readScrollProgress(container));
278
+ };
279
+ const onScroll = () => {
280
+ if (frame) return;
281
+ frame = requestAnimationFrame(sync);
282
+ };
283
+ sync();
284
+ container?.addEventListener("scroll", onScroll);
285
+ return () => {
286
+ if (frame) cancelAnimationFrame(frame);
287
+ container?.removeEventListener("scroll", onScroll);
288
+ };
289
+ }, [getTarget, showProgress, visibilityHeight]);
290
+ const scrollToTop = (event) => {
291
+ const container = getTarget();
292
+ const from = readScrollTop(container);
293
+ if (reduceMotion || duration <= 0) {
294
+ writeScrollTop(container, 0);
295
+ } else {
296
+ const startedAt = Date.now();
297
+ const step = () => {
298
+ const elapsed = Date.now() - startedAt;
299
+ if (elapsed >= duration) {
300
+ writeScrollTop(container, 0);
301
+ return;
302
+ }
303
+ writeScrollTop(container, easeInOutCubic(elapsed, from, 0, duration));
304
+ requestAnimationFrame(step);
305
+ };
306
+ requestAnimationFrame(step);
307
+ }
308
+ onClick?.(event);
309
+ };
310
+ if (!visible) return null;
311
+ return /* @__PURE__ */ jsx(
312
+ FloatButtonRoot,
313
+ {
314
+ ...rest,
315
+ ref: nodeRef,
316
+ className: cn("ui-float-button-back-top", className),
317
+ "data-progress": showProgress ? "" : void 0,
318
+ icon: icon ?? /* @__PURE__ */ jsx(ChevronUp, { "aria-hidden": "true" }),
319
+ tooltip: tooltip ?? t("ui.floatButton.backTop"),
320
+ onClick: scrollToTop,
321
+ style: showProgress ? {
322
+ ...rest.style,
323
+ "--float-button-progress-offset": `${progress}turn`
324
+ } : rest.style
325
+ }
326
+ );
327
+ }
328
+ FloatButtonGroup.displayName = "FloatButton.Group";
329
+ FloatButtonBackTop.displayName = "FloatButton.BackTop";
330
+ const FloatButton2 = Object.assign(FloatButtonRoot, {
331
+ Group: FloatButtonGroup,
332
+ BackTop: FloatButtonBackTop
333
+ });
334
+ export {
335
+ FloatButton2 as FloatButton
336
+ };
@@ -1,11 +1,15 @@
1
1
  export { Button, buttonVariants } from "./button.js";
2
2
  export type { ButtonProps } from "./button.js";
3
- export { Text, Heading } from "./typography.js";
4
- export type { TextProps, HeadingProps, TextProp, HeadingProp } from "./typography.js";
3
+ export { Text, Heading, Typography, Title, Paragraph, Link } from "./typography.js";
4
+ export type { TextProps, HeadingProps, TextProp, HeadingProp, TypographyProp, TypographyProps, TypographyBlockProp, TypographyTitleProp, TitleProps, ParagraphProp, ParagraphProps, LinkProp, LinkProps, } from "./typography.js";
5
5
  export { Logo } from "./logo.js";
6
6
  export type { LogoMark, LogoProps, LogoSize, LogoTone } from "./logo.js";
7
7
  export { Reveal } from "./reveal.js";
8
8
  export type { RevealProp, RevealProps } from "./reveal.js";
9
+ export { FloatButton } from "./float-button.js";
10
+ export type { FloatButtonBackTopProp, FloatButtonBackTopProps, FloatButtonBadgeProp, FloatButtonGroupProp, FloatButtonGroupProps, FloatButtonPlacementProp, FloatButtonProp, FloatButtonProps, FloatButtonShapeProp, FloatButtonTooltipProp, FloatButtonTriggerProp, FloatButtonTypeProp, } from "./float-button.js";
9
11
  export { Activity } from "./activity.js";
10
12
  export type { ActivityProp, ActivityProps } from "./activity.js";
11
13
  export { VisuallyHidden } from "./visually-hidden.js";
14
+ export { Actions, ActionsItem, ActionsCopy, ActionsFeedback } from "./actions.js";
15
+ export type { ActionsProp, ActionsProps, ActionsItemsProp, ActionsItemProp, ActionsItemProps, ActionsCopyProp, ActionsCopyProps, ActionsFeedbackProp, ActionsFeedbackProps, ActionsVariantProp, ActionsStatusProp, ActionsFeedbackValueProp, } from "./actions.js";
@@ -1,16 +1,27 @@
1
1
  import { Button, buttonVariants } from "./button.js";
2
- import { Text, Heading } from "./typography.js";
2
+ import { Text, Heading, Typography, Title, Paragraph, Link } from "./typography.js";
3
3
  import { Logo } from "./logo.js";
4
4
  import { Reveal } from "./reveal.js";
5
+ import { FloatButton } from "./float-button.js";
5
6
  import { Activity } from "./activity.js";
6
7
  import { VisuallyHidden } from "./visually-hidden.js";
8
+ import { Actions, ActionsItem, ActionsCopy, ActionsFeedback } from "./actions.js";
7
9
  export {
10
+ Actions,
11
+ ActionsCopy,
12
+ ActionsFeedback,
13
+ ActionsItem,
8
14
  Activity,
9
15
  Button,
16
+ FloatButton,
10
17
  Heading,
18
+ Link,
11
19
  Logo,
20
+ Paragraph,
12
21
  Reveal,
13
22
  Text,
23
+ Title,
24
+ Typography,
14
25
  VisuallyHidden,
15
26
  buttonVariants
16
27
  };