@adecore/ui 0.25.9 → 0.26.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/dist/tabs/overflow.d.ts +1 -1
- package/dist/tabs/overflow.js +9 -11
- package/dist/tabs/parts.d.ts +2 -1
- package/dist/tabs/parts.js +7 -4
- package/package.json +1 -1
- package/src/tabs/overflow.ts +9 -11
- package/src/tabs/parts.tsx +14 -4
package/dist/tabs/overflow.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export declare const shownTabs: (widths: readonly number[], room: number, gap: number, more: number,
|
|
1
|
+
export declare const shownTabs: (widths: readonly number[], room: number, gap: number, more: number, kept: readonly number[]) => number[];
|
package/dist/tabs/overflow.js
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
/*
|
|
2
2
|
* The tabs a strip shows, by index, given the width of every tab, the room the strip has, the gap
|
|
3
3
|
* between two tabs and the width of the button that opens the rest. Everything fits, or the tabs fit
|
|
4
|
-
* from the start up to the button. The
|
|
5
|
-
* the last ones that no longer fit beside
|
|
4
|
+
* from the start up to the button. The kept tabs (the picked one, the pinned ones) are never left to
|
|
5
|
+
* the menu: they take the place of the last ones that no longer fit beside them.
|
|
6
6
|
*/
|
|
7
|
-
export const shownTabs = (widths, room, gap, more,
|
|
7
|
+
export const shownTabs = (widths, room, gap, more, kept) => {
|
|
8
8
|
const span = (indices) => indices.reduce((sum, index) => sum + widths[index], 0) + gap * Math.max(0, indices.length - 1);
|
|
9
9
|
const all = widths.map((_, index) => index);
|
|
10
10
|
// Half a pixel of slack, since a measured width is fractional and the room is not.
|
|
@@ -12,18 +12,16 @@ export const shownTabs = (widths, room, gap, more, picked) => {
|
|
|
12
12
|
return all;
|
|
13
13
|
}
|
|
14
14
|
const limit = room - more - gap + 0.5;
|
|
15
|
+
const held = all.filter((index) => kept.includes(index));
|
|
15
16
|
const shown = [];
|
|
16
17
|
for (const index of all) {
|
|
17
|
-
if (
|
|
18
|
+
if (held.includes(index)) {
|
|
19
|
+
continue;
|
|
20
|
+
}
|
|
21
|
+
if (span([...held, ...shown, index]) > limit) {
|
|
18
22
|
break;
|
|
19
23
|
}
|
|
20
24
|
shown.push(index);
|
|
21
25
|
}
|
|
22
|
-
|
|
23
|
-
return shown;
|
|
24
|
-
}
|
|
25
|
-
while (shown.length > 0 && span([...shown, picked]) > limit) {
|
|
26
|
-
shown.pop();
|
|
27
|
-
}
|
|
28
|
-
return [...shown, picked];
|
|
26
|
+
return [...held, ...shown].sort((left, right) => left - right);
|
|
29
27
|
};
|
package/dist/tabs/parts.d.ts
CHANGED
|
@@ -19,8 +19,9 @@ export declare function TabsList({ className, children, start, end, after, varia
|
|
|
19
19
|
type TabProps = ComponentProps<typeof BaseTabs.Tab>;
|
|
20
20
|
export type TabsTabProps = TabProps & {
|
|
21
21
|
onClose?(): void;
|
|
22
|
+
pinned?: boolean;
|
|
22
23
|
};
|
|
23
|
-
export declare function TabsTab({ className, onClose, children, ...props }: TabsTabProps): import("react").JSX.Element;
|
|
24
|
+
export declare function TabsTab({ className, onClose, pinned, children, ...props }: TabsTabProps): import("react").JSX.Element;
|
|
24
25
|
export declare const TabsPanel: import("react").ForwardRefExoticComponent<Omit<import("@base-ui/react").TabsPanelProps, "ref"> & import("react").RefAttributes<HTMLDivElement>>;
|
|
25
26
|
export type TabsCountProps = Omit<useRender.ComponentProps<'span'>, 'children'> & {
|
|
26
27
|
value: number;
|
package/dist/tabs/parts.js
CHANGED
|
@@ -70,7 +70,7 @@ export function TabsList({ className, children, start, end, after, variant = 'li
|
|
|
70
70
|
// Null until the first measure, which runs before the first paint.
|
|
71
71
|
const [shown, setShown] = useState(null);
|
|
72
72
|
const tabs = Children.toArray(children).filter((child) => isValidElement(child) && child.type === TabsTab);
|
|
73
|
-
const
|
|
73
|
+
const kept = tabs.flatMap((tab, index) => (tab.props.value === value || tab.props.pinned === true ? [index] : []));
|
|
74
74
|
useLayoutEffect(() => {
|
|
75
75
|
const host = strip.current;
|
|
76
76
|
const row = measure.current;
|
|
@@ -84,7 +84,7 @@ export function TabsList({ className, children, start, end, after, variant = 'li
|
|
|
84
84
|
const room = host.clientWidth - parseFloat(style.paddingLeft) - parseFloat(style.paddingRight) - taken;
|
|
85
85
|
const cells = [...row.children].map((cell) => cell.getBoundingClientRect().width);
|
|
86
86
|
const more = cells.pop() ?? 0;
|
|
87
|
-
const next = shownTabs(cells, room, parseFloat(getComputedStyle(row).columnGap) || 0, more,
|
|
87
|
+
const next = shownTabs(cells, room, parseFloat(getComputedStyle(row).columnGap) || 0, more, kept);
|
|
88
88
|
setShown((current) => (sameIndices(current, next) ? current : next));
|
|
89
89
|
};
|
|
90
90
|
fit();
|
|
@@ -143,12 +143,15 @@ export function TabsList({ className, children, start, end, after, variant = 'li
|
|
|
143
143
|
const visible = shown === null ? tabs : shown.map((index) => tabs[index]);
|
|
144
144
|
const hidden = shown === null ? [] : tabs.filter((_, index) => !shown.includes(index));
|
|
145
145
|
const more = (_jsxs(_Fragment, { children: [t('tabs.more'), _jsx(Icon, { icon: ChevronDown, size: 14 })] }));
|
|
146
|
-
return (_jsx(VariantContext, { value: variant, children: _jsxs("div", { ref: strip, className: clsx('relative flex items-center', style.strip, className), children: [start !== undefined && start !== null && (_jsx("div", { ref: head, className: "flex shrink-0 items-center gap-1", children: start })), _jsx(BaseTabs.List, { ref: list, className: clsx('flex min-w-0', style.list, scrolls && 'tabs-scroll scroll-fade-x'), "data-fade-start": scrolls && edges.start ? '' : undefined, "data-fade-end": scrolls && edges.end ? '' : undefined, ...props, children: visible }), hidden.length > 0 && (_jsxs(MenuRoot, { children: [_jsx(MenuTrigger, { className: style.more, children: more }), _jsx(MenuPopup, { align: "end", className: "min-w-48", children: _jsx(InMenuContext, { value: true, children: hidden.map((tab) => (_jsx(MenuItem, { disabled: tab.props.disabled, onClick: () => select(tab.props.value), children: tab.props.children }, tab.key))) }) })] })), after !== undefined && after !== null && (_jsx("div", { ref: beside, className: "flex shrink-0 items-center gap-1", children: after })), end !== undefined && end !== null && (_jsx("div", { ref: tail, className: clsx('flex shrink-0 items-center gap-1', hidden.length === 0 && 'ml-auto'), children: end })), !scrolls && (_jsxs("div", { ref: measure, "aria-hidden": true, inert: true, className: clsx('pointer-events-none invisible absolute top-0 left-0 flex', style.row), children: [tabs.map((tab) => (_jsxs("span", { className: style.tab, children: [tab.props.children, tab.props.onClose !== undefined && _jsx("span", { className: "size-5 shrink-0" })] }, tab.key))), _jsx("span", { className: style.more, children: more })] }))] }) }));
|
|
146
|
+
return (_jsx(VariantContext, { value: variant, children: _jsxs("div", { ref: strip, className: clsx('relative flex items-center', style.strip, className), children: [start !== undefined && start !== null && (_jsx("div", { ref: head, className: "flex shrink-0 items-center gap-1", children: start })), _jsx(BaseTabs.List, { ref: list, className: clsx('flex min-w-0', style.list, scrolls && 'tabs-scroll scroll-fade-x'), "data-fade-start": scrolls && edges.start ? '' : undefined, "data-fade-end": scrolls && edges.end ? '' : undefined, ...props, children: visible }), hidden.length > 0 && (_jsxs(MenuRoot, { children: [_jsx(MenuTrigger, { className: style.more, children: more }), _jsx(MenuPopup, { align: "end", className: "min-w-48", children: _jsx(InMenuContext, { value: true, children: hidden.map((tab) => (_jsx(MenuItem, { disabled: tab.props.disabled, onClick: () => select(tab.props.value), children: tab.props.children }, tab.key))) }) })] })), after !== undefined && after !== null && (_jsx("div", { ref: beside, className: "flex shrink-0 items-center gap-1", children: after })), end !== undefined && end !== null && (_jsx("div", { ref: tail, className: clsx('flex shrink-0 items-center gap-1', hidden.length === 0 && 'ml-auto'), children: end })), !scrolls && (_jsxs("div", { ref: measure, "aria-hidden": true, inert: true, className: clsx('pointer-events-none invisible absolute top-0 left-0 flex', style.row), children: [tabs.map((tab) => (_jsxs("span", { className: style.tab, children: [tab.props.children, tab.props.onClose !== undefined && tab.props.pinned !== true && _jsx("span", { className: "size-5 shrink-0" })] }, tab.key))), _jsx("span", { className: style.more, children: more })] }))] }) }));
|
|
147
147
|
}
|
|
148
148
|
/* The close button stays out of the tab order, so a closable tab is still one stop; the keys close it from the tab itself. */
|
|
149
|
-
export function TabsTab({ className, onClose, children, ...props }) {
|
|
149
|
+
export function TabsTab({ className, onClose, pinned = false, children, ...props }) {
|
|
150
150
|
const { t } = useTranslation('ui');
|
|
151
151
|
const tab = STYLES[useContext(VariantContext)].tab;
|
|
152
|
+
if (pinned) {
|
|
153
|
+
return (_jsx(BaseTabs.Tab, { className: withClass(tab, className), "data-pinned": "", ...props, children: children }));
|
|
154
|
+
}
|
|
152
155
|
if (onClose === undefined) {
|
|
153
156
|
return (_jsx(BaseTabs.Tab, { className: withClass(tab, className), ...props, children: children }));
|
|
154
157
|
}
|
package/package.json
CHANGED
package/src/tabs/overflow.ts
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
/*
|
|
2
2
|
* The tabs a strip shows, by index, given the width of every tab, the room the strip has, the gap
|
|
3
3
|
* between two tabs and the width of the button that opens the rest. Everything fits, or the tabs fit
|
|
4
|
-
* from the start up to the button. The
|
|
5
|
-
* the last ones that no longer fit beside
|
|
4
|
+
* from the start up to the button. The kept tabs (the picked one, the pinned ones) are never left to
|
|
5
|
+
* the menu: they take the place of the last ones that no longer fit beside them.
|
|
6
6
|
*/
|
|
7
|
-
export const shownTabs = (widths: readonly number[], room: number, gap: number, more: number,
|
|
7
|
+
export const shownTabs = (widths: readonly number[], room: number, gap: number, more: number, kept: readonly number[]): number[] => {
|
|
8
8
|
const span = (indices: readonly number[]): number => indices.reduce((sum, index) => sum + widths[index]!, 0) + gap * Math.max(0, indices.length - 1);
|
|
9
9
|
const all = widths.map((_, index) => index);
|
|
10
10
|
// Half a pixel of slack, since a measured width is fractional and the room is not.
|
|
@@ -12,18 +12,16 @@ export const shownTabs = (widths: readonly number[], room: number, gap: number,
|
|
|
12
12
|
return all;
|
|
13
13
|
}
|
|
14
14
|
const limit = room - more - gap + 0.5;
|
|
15
|
+
const held = all.filter((index) => kept.includes(index));
|
|
15
16
|
const shown: number[] = [];
|
|
16
17
|
for (const index of all) {
|
|
17
|
-
if (
|
|
18
|
+
if (held.includes(index)) {
|
|
19
|
+
continue;
|
|
20
|
+
}
|
|
21
|
+
if (span([...held, ...shown, index]) > limit) {
|
|
18
22
|
break;
|
|
19
23
|
}
|
|
20
24
|
shown.push(index);
|
|
21
25
|
}
|
|
22
|
-
|
|
23
|
-
return shown;
|
|
24
|
-
}
|
|
25
|
-
while (shown.length > 0 && span([...shown, picked]) > limit) {
|
|
26
|
-
shown.pop();
|
|
27
|
-
}
|
|
28
|
-
return [...shown, picked];
|
|
26
|
+
return [...held, ...shown].sort((left, right) => left - right);
|
|
29
27
|
};
|
package/src/tabs/parts.tsx
CHANGED
|
@@ -130,7 +130,7 @@ export function TabsList({ className, children, start, end, after, variant = 'li
|
|
|
130
130
|
const tabs = Children.toArray(children).filter(
|
|
131
131
|
(child): child is ReactElement<ComponentProps<typeof TabsTab>> => isValidElement(child) && child.type === TabsTab
|
|
132
132
|
);
|
|
133
|
-
const
|
|
133
|
+
const kept = tabs.flatMap((tab, index) => (tab.props.value === value || tab.props.pinned === true ? [index] : []));
|
|
134
134
|
|
|
135
135
|
useLayoutEffect(() => {
|
|
136
136
|
const host = strip.current;
|
|
@@ -148,7 +148,7 @@ export function TabsList({ className, children, start, end, after, variant = 'li
|
|
|
148
148
|
const room = host.clientWidth - parseFloat(style.paddingLeft) - parseFloat(style.paddingRight) - taken;
|
|
149
149
|
const cells = [...row.children].map((cell) => cell.getBoundingClientRect().width);
|
|
150
150
|
const more = cells.pop() ?? 0;
|
|
151
|
-
const next = shownTabs(cells, room, parseFloat(getComputedStyle(row).columnGap) || 0, more,
|
|
151
|
+
const next = shownTabs(cells, room, parseFloat(getComputedStyle(row).columnGap) || 0, more, kept);
|
|
152
152
|
setShown((current) => (sameIndices(current, next) ? current : next));
|
|
153
153
|
};
|
|
154
154
|
fit();
|
|
@@ -263,7 +263,7 @@ export function TabsList({ className, children, start, end, after, variant = 'li
|
|
|
263
263
|
{tabs.map((tab) => (
|
|
264
264
|
<span key={tab.key} className={style.tab}>
|
|
265
265
|
{tab.props.children}
|
|
266
|
-
{tab.props.onClose !== undefined && <span className="size-5 shrink-0" />}
|
|
266
|
+
{tab.props.onClose !== undefined && tab.props.pinned !== true && <span className="size-5 shrink-0" />}
|
|
267
267
|
</span>
|
|
268
268
|
))}
|
|
269
269
|
<span className={style.more}>{more}</span>
|
|
@@ -279,13 +279,23 @@ type TabProps = ComponentProps<typeof BaseTabs.Tab>;
|
|
|
279
279
|
export type TabsTabProps = TabProps & {
|
|
280
280
|
/* Makes the tab closable: a close button in it, a middle click, and Delete or Backspace while the tab has focus. */
|
|
281
281
|
onClose?(): void;
|
|
282
|
+
/* Keeps the tab in the strip, never in the menu, and leaves it unclosable even with `onClose`. */
|
|
283
|
+
pinned?: boolean;
|
|
282
284
|
};
|
|
283
285
|
|
|
284
286
|
/* The close button stays out of the tab order, so a closable tab is still one stop; the keys close it from the tab itself. */
|
|
285
|
-
export function TabsTab({ className, onClose, children, ...props }: TabsTabProps) {
|
|
287
|
+
export function TabsTab({ className, onClose, pinned = false, children, ...props }: TabsTabProps) {
|
|
286
288
|
const { t } = useTranslation('ui');
|
|
287
289
|
const tab = STYLES[useContext(VariantContext)].tab;
|
|
288
290
|
|
|
291
|
+
if (pinned) {
|
|
292
|
+
return (
|
|
293
|
+
<BaseTabs.Tab className={withClass(tab, className)} data-pinned="" {...props}>
|
|
294
|
+
{children}
|
|
295
|
+
</BaseTabs.Tab>
|
|
296
|
+
);
|
|
297
|
+
}
|
|
298
|
+
|
|
289
299
|
if (onClose === undefined) {
|
|
290
300
|
return (
|
|
291
301
|
<BaseTabs.Tab className={withClass(tab, className)} {...props}>
|