@godxjp/ui 33.8.1 → 33.9.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 +3 -3
- package/agent/START-HERE.md +2 -2
- package/agent/components/RichTextEditor.json +73 -0
- package/agent/components/Tabs.json +9 -2
- package/agent/components/TranslatableField.json +69 -0
- package/agent/components/TranslatableRichText.json +70 -0
- package/agent/components-index.json +15 -0
- package/agent/components.json +221 -2
- package/agent/index.json +4 -4
- package/agent/llms.txt +5 -5
- package/dist/components/data-entry/index.d.ts +2 -2
- package/dist/components/data-entry/index.js +2 -1
- package/dist/components/data-entry/translatable-input.d.ts +24 -20
- package/dist/components/data-entry/translatable-input.js +8 -21
- package/dist/components/navigation/tabs-scroll.d.ts +1 -5
- package/dist/components/navigation/tabs-scroll.js +31 -8
- package/dist/components/navigation/tabs.d.ts +1 -1
- package/dist/components/navigation/tabs.js +264 -89
- package/dist/contracts/measurement.json +1 -1
- package/dist/i18n/messages/en.json +2 -1
- package/dist/i18n/messages/ja.json +2 -1
- package/dist/i18n/messages/vi.json +2 -1
- package/dist/props/components/data-entry.prop.d.ts +49 -5
- package/dist/props/components/navigation.prop.d.ts +20 -1
- package/dist/props/registry.d.ts +59 -0
- package/dist/props/registry.js +86 -0
- package/dist/styles/data-display-layout.css +41 -0
- package/dist/styles/layers.json +39 -1
- package/dist/styles.css +1 -1
- package/package.json +2 -2
package/agent/index.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"counts": {
|
|
3
3
|
"anti-ai-tells": 35,
|
|
4
|
-
"components":
|
|
4
|
+
"components": 169,
|
|
5
5
|
"patterns": 28,
|
|
6
6
|
"rules": 44,
|
|
7
7
|
"tokens": 2235,
|
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
},
|
|
17
17
|
{
|
|
18
18
|
"file": "components-index.json",
|
|
19
|
-
"note": "
|
|
19
|
+
"note": "52 KB — name + group + tagline for all 169. FETCH THIS FIRST, then fetch only the components you chose.",
|
|
20
20
|
"url": "https://raw.githubusercontent.com/godx-jp/godxjp-ui/main/agent/components-index.json"
|
|
21
21
|
},
|
|
22
22
|
{
|
|
@@ -54,7 +54,7 @@
|
|
|
54
54
|
"note": "Pin to the tag that matches the @godxjp/ui version you installed. A catalog newer than your package describes props you do not have; older, and it hides props you do.",
|
|
55
55
|
"read": {
|
|
56
56
|
"live": "https://raw.githubusercontent.com/godx-jp/godxjp-ui/main/agent/index.json",
|
|
57
|
-
"pinned": "https://raw.githubusercontent.com/godx-jp/godxjp-ui/v33.
|
|
57
|
+
"pinned": "https://raw.githubusercontent.com/godx-jp/godxjp-ui/v33.9.1/agent/index.json"
|
|
58
58
|
},
|
|
59
59
|
"source": "mcp/src/data — the same data @godxjp/ui-mcp serves — plus the foundation and semantic token tiers, read from src/tokens/*.css",
|
|
60
60
|
"start": "https://raw.githubusercontent.com/godx-jp/godxjp-ui/main/agent/START-HERE.md",
|
|
@@ -68,5 +68,5 @@
|
|
|
68
68
|
"foundation": "the seeds a consumer is invited to set — --primary, --background, --radius",
|
|
69
69
|
"semantic": "named roles that follow the seeds — --ring, --text-link, --overlay-background"
|
|
70
70
|
},
|
|
71
|
-
"version": "33.
|
|
71
|
+
"version": "33.9.1"
|
|
72
72
|
}
|
package/agent/llms.txt
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
# @godxjp/ui
|
|
2
2
|
|
|
3
|
-
> A Japanese-enterprise React design system:
|
|
4
|
-
> 44 cardinal rules. This file is the entry point for AI agents. Catalog version 33.
|
|
3
|
+
> A Japanese-enterprise React design system: 169 components, 2235 design tokens,
|
|
4
|
+
> 44 cardinal rules. This file is the entry point for AI agents. Catalog version 33.9.1.
|
|
5
5
|
|
|
6
6
|
If your client can run a process, do not read these files — run the MCP server instead
|
|
7
|
-
(`npx @godxjp/ui-mcp@33.
|
|
7
|
+
(`npx @godxjp/ui-mcp@33.9.1`). It is searchable and version-locked. These files exist for agents
|
|
8
8
|
that can only fetch URLs.
|
|
9
9
|
|
|
10
10
|
## Start
|
|
@@ -16,7 +16,7 @@ that can only fetch URLs.
|
|
|
16
16
|
## Catalog
|
|
17
17
|
|
|
18
18
|
- [patterns-index.json](https://raw.githubusercontent.com/godx-jp/godxjp-ui/main/agent/patterns-index.json): 28 whole-task patterns (name, tagline, tags). Start here when the task is a TASK — "build a settings page" — then fetch `patterns/<name>.json` for complete code.
|
|
19
|
-
- [components-index.json](https://raw.githubusercontent.com/godx-jp/godxjp-ui/main/agent/components-index.json):
|
|
19
|
+
- [components-index.json](https://raw.githubusercontent.com/godx-jp/godxjp-ui/main/agent/components-index.json): 52 KB — all 169 components as name, group, tagline, plus `absorbed`: the names that do NOT exist and map to it (`Combobox` → `Select`).
|
|
20
20
|
- [components/<Name>.json](https://raw.githubusercontent.com/godx-jp/godxjp-ui/main/agent/components/Select.json): one file per component (1 KB–37 KB, median 6 KB). Read the index, then fetch only the ones you chose — this is the selective route, and the reason you do not need the blob.
|
|
21
21
|
- [components.json](https://raw.githubusercontent.com/godx-jp/godxjp-ui/main/agent/components.json): 1.3 MB — every entry in one file. Most URL fetchers truncate a response this size without saying so; prefer the per-component files.
|
|
22
22
|
- [tokens.json](https://raw.githubusercontent.com/godx-jp/godxjp-ui/main/agent/tokens.json): every design token with its value, the reason it exists, and its `tier` — 216 `foundation` seeds (`--primary`, `--background`, `--radius`: set these when you are handed a brand), 113 `semantic` roles, 1906 `component` knobs.
|
|
@@ -27,7 +27,7 @@ that can only fetch URLs.
|
|
|
27
27
|
## Pinning
|
|
28
28
|
|
|
29
29
|
Every URL above tracks `main`. To pin to the release a project actually installed, swap `main` for
|
|
30
|
-
the tag: `.../godx-jp/godxjp-ui/v33.
|
|
30
|
+
the tag: `.../godx-jp/godxjp-ui/v33.9.1/agent/...`. A catalog that does not match the installed
|
|
31
31
|
package describes props that are absent, or hides props that are present, and says nothing either way.
|
|
32
32
|
|
|
33
33
|
Pinned catalogs only exist for releases whose tag actually contains `agent/`. If `…/v<version>/agent/index.json` returns 404, that release predates this catalog: read `…/main/…` instead and compare `index.json` → `version` against the package you have, so you at least know which way it drifted.
|
|
@@ -9,8 +9,8 @@ export { CheckboxGroup } from "./checkbox-group.js";
|
|
|
9
9
|
export { Radio, RadioGroup, RadioItem, RadioGroupRoot } from "./radio.js";
|
|
10
10
|
export { Textarea } from "./textarea.js";
|
|
11
11
|
export type { TextareaProps } from "./textarea.js";
|
|
12
|
-
export { TranslatableInput, TranslatableTextarea } from "./translatable-input.js";
|
|
13
|
-
export type { TranslatableValue, TranslatableInputProp, TranslatableInputProps, TranslatableTextareaProp, TranslatableTextareaProps, } from "./translatable-input.js";
|
|
12
|
+
export { TranslatableField, TranslatableInput, TranslatableTextarea } from "./translatable-input.js";
|
|
13
|
+
export type { TranslatableValue, TranslatableLocaleProp, TranslatableFieldProp, TranslatableFieldProps, TranslatableFieldControlProp, TranslatableInputProp, TranslatableInputProps, TranslatableTextareaProp, TranslatableTextareaProps, } from "./translatable-input.js";
|
|
14
14
|
export { Form, useFormLayout, type FormLayoutContextValue } from "./form.js";
|
|
15
15
|
export type { FormProp, FormProps } from "./form.js";
|
|
16
16
|
export { FormField } from "./form-field.js";
|
|
@@ -17,7 +17,7 @@ import { Checkbox } from "./checkbox.js";
|
|
|
17
17
|
import { CheckboxGroup } from "./checkbox-group.js";
|
|
18
18
|
import { Radio, RadioGroup, RadioItem, RadioGroupRoot } from "./radio.js";
|
|
19
19
|
import { Textarea } from "./textarea.js";
|
|
20
|
-
import { TranslatableInput, TranslatableTextarea } from "./translatable-input.js";
|
|
20
|
+
import { TranslatableField, TranslatableInput, TranslatableTextarea } from "./translatable-input.js";
|
|
21
21
|
import { Form, useFormLayout } from "./form.js";
|
|
22
22
|
import { FormField } from "./form-field.js";
|
|
23
23
|
import { FormErrors, FormErrorsProvider } from "./form-errors.js";
|
|
@@ -126,6 +126,7 @@ export {
|
|
|
126
126
|
ToggleGroup,
|
|
127
127
|
ToggleGroupItem,
|
|
128
128
|
Transfer,
|
|
129
|
+
TranslatableField,
|
|
129
130
|
TranslatableInput,
|
|
130
131
|
TranslatableTextarea,
|
|
131
132
|
TreeSelect,
|
|
@@ -1,6 +1,26 @@
|
|
|
1
1
|
import * as React from "react";
|
|
2
|
-
import type {
|
|
3
|
-
export type { TranslatableValue, TranslatableInputProp, TranslatableInputProp as TranslatableInputProps, TranslatableTextareaProp, TranslatableTextareaProp as TranslatableTextareaProps, } from "../../props/components/data-entry.prop.js";
|
|
2
|
+
import type { TranslatableFieldProp } from "../../props/components/data-entry.prop.js";
|
|
3
|
+
export type { TranslatableValue, TranslatableLocaleProp, TranslatableFieldProp, TranslatableFieldProp as TranslatableFieldProps, TranslatableFieldControlProp, TranslatableInputProp, TranslatableInputProp as TranslatableInputProps, TranslatableTextareaProp, TranslatableTextareaProp as TranslatableTextareaProps, } from "../../props/components/data-entry.prop.js";
|
|
4
|
+
/**
|
|
5
|
+
* TranslatableField — the shared engine of {@link TranslatableInput}, {@link TranslatableTextarea}
|
|
6
|
+
* and `@godxjp/block-editor`'s `TranslatableRichText` (gh#1365): it owns the locale list, the active
|
|
7
|
+
* tab, the per-locale map, the fallback placeholder, the error marks and the FormField error report,
|
|
8
|
+
* and renders the ONE control in the panel through `renderControl`. Every prop it does not own is
|
|
9
|
+
* passed through to that control.
|
|
10
|
+
*
|
|
11
|
+
* WHY THE SWITCHER IS `Tabs`, NOT `Segmented`. The WAI-ARIA APG radio group is for CHOOSING A VALUE
|
|
12
|
+
* — a segmented control's choice is form state and is submitted with the form. The locale here is
|
|
13
|
+
* not form state: every locale's text is submitted together, and the switcher only decides which
|
|
14
|
+
* of them the one editing region shows. That is the APG tabs pattern — a tablist whose selected
|
|
15
|
+
* tab controls one tabpanel — so a screen reader announces "English, tab, 2 of 3" and the region
|
|
16
|
+
* that follows, instead of offering a "language" question the form never asks.
|
|
17
|
+
*
|
|
18
|
+
* ONE CONTROL, NOT ONE PER LOCALE. The panel holds a single control whose value, `lang` and
|
|
19
|
+
* placeholder follow the active tab (`Tabs.controls` points the tab at it, gh#1021). Swapping
|
|
20
|
+
* elements per tab would hand the forwarded `ref`, the FormField `id` and an IME composition to a
|
|
21
|
+
* new node on every switch.
|
|
22
|
+
*/
|
|
23
|
+
export declare function TranslatableField({ renderControl, toPlaceholder, value: valueProp, defaultValue, onValueChange, locales: localesProp, defaultLocale, fallbackLocale: fallbackLocaleProp, errors, size, placeholder, className, ...rest }: TranslatableFieldProp): React.JSX.Element;
|
|
4
24
|
/**
|
|
5
25
|
* TranslatableInput — ONE text field that holds a value per locale (`{ ja, en, vi }`), switched by
|
|
6
26
|
* locale tabs above it (gh#1336). The locale list and the first tab come from `AppProvider`
|
|
@@ -8,22 +28,6 @@ export type { TranslatableValue, TranslatableInputProp, TranslatableInputProp as
|
|
|
8
28
|
* its placeholder; a locale with an error is marked on its tab. Every other `Input` prop applies to
|
|
9
29
|
* the single control, and `ref` is that control's `<input>`.
|
|
10
30
|
*/
|
|
11
|
-
export declare const TranslatableInput: React.ForwardRefExoticComponent<Omit<import("./input.js").InputProp, "defaultValue" | "lang" | "onValueChange" | "value"> &
|
|
12
|
-
value?: import("../../props/index.js").ValueProp<TranslatableValue>;
|
|
13
|
-
defaultValue?: import("../../props/index.js").DefaultValueProp<TranslatableValue>;
|
|
14
|
-
onValueChange?: import("../../props/index.js").OnValueChangeProp<TranslatableValue>;
|
|
15
|
-
locales?: readonly string[];
|
|
16
|
-
defaultLocale?: string;
|
|
17
|
-
fallbackLocale?: string;
|
|
18
|
-
errors?: Partial<Record<string, import("../../props/index.js").ErrorProp>>;
|
|
19
|
-
} & React.RefAttributes<HTMLInputElement>>;
|
|
31
|
+
export declare const TranslatableInput: React.ForwardRefExoticComponent<Omit<import("./input.js").InputProp, "defaultValue" | "lang" | "onValueChange" | "value"> & import("./translatable-input.js").TranslatableLocaleProp & React.RefAttributes<HTMLInputElement>>;
|
|
20
32
|
/** TranslatableTextarea — {@link TranslatableInput} over a `Textarea`; `ref` is the `<textarea>`. */
|
|
21
|
-
export declare const TranslatableTextarea: React.ForwardRefExoticComponent<Omit<import("./textarea.js").TextareaProp, "defaultValue" | "lang" | "onValueChange" | "value"> &
|
|
22
|
-
value?: import("../../props/index.js").ValueProp<TranslatableValue>;
|
|
23
|
-
defaultValue?: import("../../props/index.js").DefaultValueProp<TranslatableValue>;
|
|
24
|
-
onValueChange?: import("../../props/index.js").OnValueChangeProp<TranslatableValue>;
|
|
25
|
-
locales?: readonly string[];
|
|
26
|
-
defaultLocale?: string;
|
|
27
|
-
fallbackLocale?: string;
|
|
28
|
-
errors?: Partial<Record<string, import("../../props/index.js").ErrorProp>>;
|
|
29
|
-
} & React.RefAttributes<HTMLTextAreaElement>>;
|
|
33
|
+
export declare const TranslatableTextarea: React.ForwardRefExoticComponent<Omit<import("./textarea.js").TextareaProp, "defaultValue" | "lang" | "onValueChange" | "value"> & import("./translatable-input.js").TranslatableLocaleProp & React.RefAttributes<HTMLTextAreaElement>>;
|
|
@@ -21,7 +21,8 @@ function localeName(code, uiLocale) {
|
|
|
21
21
|
}
|
|
22
22
|
const idPart = (code) => code.replace(/[^A-Za-z0-9_-]/g, "_");
|
|
23
23
|
function TranslatableField({
|
|
24
|
-
|
|
24
|
+
renderControl,
|
|
25
|
+
toPlaceholder,
|
|
25
26
|
value: valueProp,
|
|
26
27
|
defaultValue,
|
|
27
28
|
onValueChange,
|
|
@@ -32,7 +33,6 @@ function TranslatableField({
|
|
|
32
33
|
size,
|
|
33
34
|
placeholder,
|
|
34
35
|
className,
|
|
35
|
-
forwardedRef,
|
|
36
36
|
...rest
|
|
37
37
|
}) {
|
|
38
38
|
const { t, locale: uiLocale, fallbackLocale: appFallbackLocale } = useTranslation();
|
|
@@ -74,11 +74,11 @@ function TranslatableField({
|
|
|
74
74
|
const showOwnError = hasActiveError && !insideFormField;
|
|
75
75
|
const controlProps = {
|
|
76
76
|
...rest,
|
|
77
|
-
|
|
77
|
+
size,
|
|
78
78
|
lang: active,
|
|
79
79
|
value: activeText,
|
|
80
80
|
onValueChange: handleText,
|
|
81
|
-
placeholder: fallbackText ? fallbackText : placeholder,
|
|
81
|
+
placeholder: fallbackText ? toPlaceholder?.(fallbackText) ?? fallbackText : placeholder,
|
|
82
82
|
// The control is named by the field AND the locale it holds: "商品名 English".
|
|
83
83
|
"aria-labelledby": labelledBy ? `${labelledBy} ${localeNameId}` : void 0,
|
|
84
84
|
"aria-invalid": hasActiveError ? true : rest["aria-invalid"],
|
|
@@ -139,19 +139,7 @@ function TranslatableField({
|
|
|
139
139
|
id: panelId,
|
|
140
140
|
"aria-labelledby": tabId(active),
|
|
141
141
|
className: "ui-translatable-field-panel",
|
|
142
|
-
children:
|
|
143
|
-
Input,
|
|
144
|
-
{
|
|
145
|
-
...controlProps,
|
|
146
|
-
size
|
|
147
|
-
}
|
|
148
|
-
) : /* @__PURE__ */ jsx(
|
|
149
|
-
Textarea,
|
|
150
|
-
{
|
|
151
|
-
...controlProps,
|
|
152
|
-
size
|
|
153
|
-
}
|
|
154
|
-
)
|
|
142
|
+
children: renderControl(controlProps)
|
|
155
143
|
}
|
|
156
144
|
),
|
|
157
145
|
showOwnError ? /* @__PURE__ */ jsx("p", { id: ownErrorId, role: "alert", className: "ui-form-field-note text-error-strong text-xs", children: activeError }) : null
|
|
@@ -164,8 +152,7 @@ const TranslatableInput = React.forwardRef(
|
|
|
164
152
|
TranslatableField,
|
|
165
153
|
{
|
|
166
154
|
...props,
|
|
167
|
-
|
|
168
|
-
forwardedRef: ref
|
|
155
|
+
renderControl: (control) => /* @__PURE__ */ jsx(Input, { ...control, ref })
|
|
169
156
|
}
|
|
170
157
|
)
|
|
171
158
|
);
|
|
@@ -175,13 +162,13 @@ const TranslatableTextarea = React.forwardRef(
|
|
|
175
162
|
TranslatableField,
|
|
176
163
|
{
|
|
177
164
|
...props,
|
|
178
|
-
|
|
179
|
-
forwardedRef: ref
|
|
165
|
+
renderControl: (control) => /* @__PURE__ */ jsx(Textarea, { ...control, ref })
|
|
180
166
|
}
|
|
181
167
|
)
|
|
182
168
|
);
|
|
183
169
|
TranslatableTextarea.displayName = "TranslatableTextarea";
|
|
184
170
|
export {
|
|
171
|
+
TranslatableField,
|
|
185
172
|
TranslatableInput,
|
|
186
173
|
TranslatableTextarea
|
|
187
174
|
};
|
|
@@ -15,10 +15,6 @@ export type TabsScrollBox = {
|
|
|
15
15
|
* coordinates, so it is direction- and orientation-agnostic (LTR, RTL, vertical rail alike).
|
|
16
16
|
*/
|
|
17
17
|
export declare function isTabFullyVisible(scrollport: TabsScrollBox, trigger: TabsScrollBox): boolean;
|
|
18
|
-
/**
|
|
19
|
-
* The trigger the strip must keep reachable: the focused trigger when the roving focus is inside
|
|
20
|
-
* this list (manual activation can park focus off the selected tab), otherwise the active one.
|
|
21
|
-
*/
|
|
22
18
|
export declare function resolveTabScrollTarget(list: HTMLElement): HTMLElement | null;
|
|
23
19
|
/** `true` when the user has asked for reduced motion (falsy/unsupported environments say no). */
|
|
24
20
|
export declare function prefersReducedMotion(): boolean;
|
|
@@ -80,7 +76,7 @@ export declare function useTabsScrollReporter(listRef: React.RefObject<HTMLEleme
|
|
|
80
76
|
* removed) and the strip scrolling (the user swiping, or `useKeepActiveTabVisible` re-pinning a
|
|
81
77
|
* trigger).
|
|
82
78
|
*
|
|
83
|
-
* `values` is
|
|
79
|
+
* `values` is serialized into the effect key rather than passed as a dependency, so a consumer that
|
|
84
80
|
* rebuilds its `items` array on every render does not re-arm the observers on every render.
|
|
85
81
|
*
|
|
86
82
|
* The first measurement ALWAYS reports, even when nothing is hidden. Suppressing it looks harmless
|
|
@@ -7,12 +7,15 @@ function isTabFullyVisible(scrollport, trigger) {
|
|
|
7
7
|
if (width <= 0 && height <= 0) return true;
|
|
8
8
|
return trigger.left >= scrollport.left - VISIBILITY_EPSILON && trigger.right <= scrollport.right + VISIBILITY_EPSILON && trigger.top >= scrollport.top - VISIBILITY_EPSILON && trigger.bottom <= scrollport.bottom + VISIBILITY_EPSILON;
|
|
9
9
|
}
|
|
10
|
+
const TAB_TRIGGER_SELECTOR = '[role="tab"], [data-slot="tabs-trigger"]';
|
|
10
11
|
function resolveTabScrollTarget(list) {
|
|
11
12
|
const focused = list.ownerDocument?.activeElement;
|
|
12
|
-
if (focused instanceof HTMLElement && focused !== list && list.contains(focused) && focused.
|
|
13
|
+
if (focused instanceof HTMLElement && focused !== list && list.contains(focused) && focused.matches(TAB_TRIGGER_SELECTOR)) {
|
|
13
14
|
return focused;
|
|
14
15
|
}
|
|
15
|
-
return list.querySelector(
|
|
16
|
+
return list.querySelector(
|
|
17
|
+
'[role="tab"][data-state="active"], [data-slot="tabs-trigger"][data-state="active"]'
|
|
18
|
+
);
|
|
16
19
|
}
|
|
17
20
|
function prefersReducedMotion() {
|
|
18
21
|
if (typeof window === "undefined" || typeof window.matchMedia !== "function") return false;
|
|
@@ -69,7 +72,7 @@ function useKeepActiveTabVisible(listRef) {
|
|
|
69
72
|
}
|
|
70
73
|
function resolveHiddenTabValues(list, values) {
|
|
71
74
|
const scrollport = list.getBoundingClientRect();
|
|
72
|
-
const triggers = [...list.querySelectorAll(
|
|
75
|
+
const triggers = [...list.querySelectorAll(TAB_TRIGGER_SELECTOR)];
|
|
73
76
|
const hidden = [];
|
|
74
77
|
triggers.forEach((trigger, index) => {
|
|
75
78
|
const value = values[index];
|
|
@@ -115,16 +118,30 @@ function useTabsOverflowValues(listRef, values, onChange) {
|
|
|
115
118
|
React.useEffect(() => {
|
|
116
119
|
onChangeRef.current = onChange;
|
|
117
120
|
});
|
|
118
|
-
const key = values === void 0 ? null :
|
|
119
|
-
React.
|
|
121
|
+
const key = values === void 0 ? null : JSON.stringify(values);
|
|
122
|
+
React.useLayoutEffect(() => {
|
|
120
123
|
if (key === null) return void 0;
|
|
121
124
|
const list = listRef.current;
|
|
122
125
|
if (!list) return void 0;
|
|
123
|
-
const all =
|
|
126
|
+
const all = JSON.parse(key);
|
|
124
127
|
let last = null;
|
|
125
128
|
const measure = () => {
|
|
126
|
-
const
|
|
127
|
-
const
|
|
129
|
+
const triggers = [...list.querySelectorAll(TAB_TRIGGER_SELECTOR)];
|
|
130
|
+
const menu = list.parentElement?.querySelector(".ui-tabs-overflow");
|
|
131
|
+
const vertical = list.dataset.orientation === "vertical";
|
|
132
|
+
const style = getComputedStyle(list);
|
|
133
|
+
const boxes = triggers.map((trigger) => trigger.getBoundingClientRect());
|
|
134
|
+
const extent = boxes.length ? Math.max(...boxes.map((box2) => vertical ? box2.bottom : box2.right)) - Math.min(...boxes.map((box2) => vertical ? box2.top : box2.left)) : 0;
|
|
135
|
+
const padding = vertical ? parseFloat(style.paddingTop) + parseFloat(style.paddingBottom) : parseFloat(style.paddingLeft) + parseFloat(style.paddingRight);
|
|
136
|
+
const box = list.getBoundingClientRect();
|
|
137
|
+
const menuBox = menu?.getBoundingClientRect();
|
|
138
|
+
const gap = menu && list.parentElement ? parseFloat(getComputedStyle(list.parentElement).gap) || 0 : 0;
|
|
139
|
+
const fitsWithoutMenu = menuBox && extent + padding <= (vertical ? box.height + menuBox.height : box.width + menuBox.width) + gap;
|
|
140
|
+
const hidden = fitsWithoutMenu ? [] : resolveHiddenTabValues(list, all);
|
|
141
|
+
triggers.forEach((trigger, index) => {
|
|
142
|
+
trigger.toggleAttribute("data-overflow-hidden", hidden.includes(all[index]));
|
|
143
|
+
});
|
|
144
|
+
const next = JSON.stringify(hidden);
|
|
128
145
|
if (next === last) return;
|
|
129
146
|
last = next;
|
|
130
147
|
onChangeRef.current(hidden);
|
|
@@ -139,12 +156,18 @@ function useTabsOverflowValues(listRef, values, onChange) {
|
|
|
139
156
|
if (typeof ResizeObserver !== "undefined") {
|
|
140
157
|
const resizeObserver = new ResizeObserver(measure);
|
|
141
158
|
resizeObserver.observe(list);
|
|
159
|
+
list.querySelectorAll(TAB_TRIGGER_SELECTOR).forEach((trigger) => {
|
|
160
|
+
resizeObserver.observe(trigger);
|
|
161
|
+
});
|
|
142
162
|
cleanups.push(() => {
|
|
143
163
|
resizeObserver.disconnect();
|
|
144
164
|
});
|
|
145
165
|
}
|
|
146
166
|
return () => {
|
|
147
167
|
for (const cleanup of cleanups) cleanup();
|
|
168
|
+
list.querySelectorAll(TAB_TRIGGER_SELECTOR).forEach((trigger) => {
|
|
169
|
+
trigger.removeAttribute("data-overflow-hidden");
|
|
170
|
+
});
|
|
148
171
|
};
|
|
149
172
|
}, [key, listRef]);
|
|
150
173
|
}
|
|
@@ -8,7 +8,7 @@ export type TabsProps = Omit<React.ComponentPropsWithoutRef<"div">, "defaultValu
|
|
|
8
8
|
dir?: "ltr" | "rtl";
|
|
9
9
|
activationMode?: "manual" | "automatic";
|
|
10
10
|
};
|
|
11
|
-
export declare function Tabs({ className, orientation, items, value, defaultValue, onValueChange, activationMode, dir, variant, tabPlacement, size, centered, bodied, extra, destroyOnHidden, onEdit, addIcon, hideAdd, closeIcon, onTabClick, overflow, animated, indicator, moreIcon, onTabScroll, controls, listClassName, contentClassName, children, "aria-label": ariaLabel, "aria-labelledby": ariaLabelledby, ...props }: TabsProps): React.JSX.Element;
|
|
11
|
+
export declare function Tabs({ className, orientation, items, value, defaultValue, onValueChange, activationMode, dir, variant, tabPlacement, size, centered, bodied, extra, destroyOnHidden, onEdit, addIcon, hideAdd, closeIcon, onTabClick, overflow, animated, indicator, moreIcon, onTabScroll, controls, linkComponent, listClassName, contentClassName, children, "aria-label": ariaLabel, "aria-labelledby": ariaLabelledby, ...props }: TabsProps): React.JSX.Element;
|
|
12
12
|
export declare const TabsList: React.ForwardRefExoticComponent<Omit<React.DetailedHTMLProps<React.HTMLAttributes<HTMLDivElement>, HTMLDivElement>, "ref"> & {
|
|
13
13
|
variant?: "default" | "line";
|
|
14
14
|
/**
|