@abuzareal/contour-ui 0.5.0 → 0.6.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/CHANGELOG.md +16 -0
- package/COMBOBOX.md +87 -0
- package/COMPOSITION.md +4 -0
- package/CONTRACTS.md +27 -27
- package/DOCS.md +86 -0
- package/FORMS.md +4 -0
- package/FOUNDATION.md +35 -0
- package/GETTING-STARTED.md +67 -0
- package/HELPERS.md +33 -0
- package/HOOKS.md +67 -0
- package/MANUAL-QA.md +38 -0
- package/MIGRATION.md +44 -0
- package/MODELS.md +18 -0
- package/NEXTJS.md +3 -1
- package/OVERLAYS.md +4 -0
- package/PROP-REFERENCE.md +37 -0
- package/PUBLIC-API.md +174 -164
- package/README.md +9 -5
- package/RECIPES.md +43 -0
- package/REFERENCE.md +193 -0
- package/RELEASING.md +8 -2
- package/SIZE.md +9 -3
- package/STYLING.md +4 -0
- package/SUPPORT.md +36 -0
- package/THREE.md +40 -0
- package/dist/components/ui/Combobox.d.ts +325 -0
- package/dist/components/ui/Combobox.js +135 -0
- package/dist/components/ui/Combobox.types.d.ts +46 -0
- package/dist/components/ui/Combobox.types.js +1 -0
- package/dist/components/ui/DataTable.js +5 -4
- package/dist/components/ui/Field.js +1 -1
- package/dist/components/ui/FloatingPortal.js +2 -40
- package/dist/components/ui/FloatingScope.d.ts +9 -0
- package/dist/components/ui/FloatingScope.js +43 -0
- package/dist/components/ui/Table.d.ts +20 -0
- package/dist/components/ui/Table.js +9 -0
- package/dist/hooks/useComboboxAnchor.d.ts +17 -0
- package/dist/hooks/useComboboxAnchor.js +61 -0
- package/dist/hooks/useInitialHashScroll.js +5 -1
- package/dist/hooks/usePrintPreparation.js +12 -1
- package/dist/hooks/useScrollSpy.js +6 -3
- package/dist/index.d.ts +2 -0
- package/dist/index.js +2 -0
- package/dist/kit/data-display.css +6 -0
- package/dist/kit/forms.css +34 -0
- package/dist/lib/componentClasses.js +1 -1
- package/dist/scoped/kit/data-display.css +6 -0
- package/dist/scoped/kit/forms.css +34 -0
- package/docs/prop-metadata.json +2668 -0
- package/examples/onboarding/SettingsForm.jsx +30 -0
- package/examples/onboarding/SettingsForm.tsx +50 -0
- package/examples/optional/LazyScene.tsx +78 -0
- package/examples/optional/OptionalSmoothScroll.tsx +25 -0
- package/examples/optional/SmoothScrollOwner.tsx +7 -0
- package/examples/optional/optional.css +25 -0
- package/examples/recipes/AsyncProjectPicker.tsx +82 -0
- package/examples/recipes/DeleteProjectConfirmation.tsx +97 -0
- package/examples/recipes/InputCompositions.tsx +49 -0
- package/examples/recipes/RemoteRecordsTable.tsx +256 -0
- package/examples/recipes/useRemoteResource.ts +81 -0
- package/examples/reference/HookExamples.tsx +146 -0
- package/examples/reproduction/README.md +9 -0
- package/examples/reproduction/index.html +14 -0
- package/examples/reproduction/package.json +27 -0
- package/examples/reproduction/src/main.tsx +33 -0
- package/examples/reproduction/src/style.css +19 -0
- package/examples/reproduction/tsconfig.json +18 -0
- package/examples/reproduction/vite.config.ts +19 -0
- package/examples/starters/next/README.md +18 -0
- package/examples/starters/next/app/Demo.tsx +65 -0
- package/examples/starters/next/app/layout.tsx +25 -0
- package/examples/starters/next/app/next-page/page.tsx +9 -0
- package/examples/starters/next/app/page.tsx +11 -0
- package/examples/starters/next/app/style.css +19 -0
- package/examples/starters/next/next-env.d.ts +2 -0
- package/examples/starters/next/next.config.mjs +1 -0
- package/examples/starters/next/package.json +27 -0
- package/examples/starters/next/proxy.ts +16 -0
- package/examples/starters/next/tsconfig.json +30 -0
- package/examples/starters/vite/README.md +16 -0
- package/examples/starters/vite/index.html +14 -0
- package/examples/starters/vite/package.json +27 -0
- package/examples/starters/vite/src/main.tsx +81 -0
- package/examples/starters/vite/src/style.css +19 -0
- package/examples/starters/vite/tsconfig.json +18 -0
- package/examples/starters/vite/vite.config.ts +19 -0
- package/package.json +37 -4
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
3
|
+
import { useLayoutEffect, useRef } from "react";
|
|
4
|
+
/** Carry the anchor's inherited theme into a portal without changing its host. */
|
|
5
|
+
export default function FloatingScope({ anchor, open, target, onDirectionChange, children, }) {
|
|
6
|
+
const scope = useRef(null);
|
|
7
|
+
useLayoutEffect(() => {
|
|
8
|
+
const root = scope.current;
|
|
9
|
+
const view = anchor?.ownerDocument.defaultView;
|
|
10
|
+
if (!open || !anchor || !root || !view)
|
|
11
|
+
return;
|
|
12
|
+
const synchronize = () => {
|
|
13
|
+
const computed = view.getComputedStyle(anchor);
|
|
14
|
+
for (let index = 0; index < computed.length; index++) {
|
|
15
|
+
const name = computed.item(index);
|
|
16
|
+
if (name.startsWith("--"))
|
|
17
|
+
root.style.setProperty(name, computed.getPropertyValue(name));
|
|
18
|
+
}
|
|
19
|
+
root.dir = computed.direction;
|
|
20
|
+
root.style.fontFamily = computed.fontFamily;
|
|
21
|
+
root.style.color = computed.color;
|
|
22
|
+
onDirectionChange?.(computed.direction === "rtl" ? "rtl" : "ltr");
|
|
23
|
+
};
|
|
24
|
+
synchronize();
|
|
25
|
+
const observer = new view.MutationObserver(synchronize);
|
|
26
|
+
let ancestor = anchor;
|
|
27
|
+
while (ancestor) {
|
|
28
|
+
observer.observe(ancestor, {
|
|
29
|
+
attributes: true,
|
|
30
|
+
attributeFilter: [
|
|
31
|
+
"style",
|
|
32
|
+
"class",
|
|
33
|
+
"dir",
|
|
34
|
+
"data-theme",
|
|
35
|
+
"data-contour-theme",
|
|
36
|
+
],
|
|
37
|
+
});
|
|
38
|
+
ancestor = ancestor.parentElement;
|
|
39
|
+
}
|
|
40
|
+
return () => observer.disconnect();
|
|
41
|
+
}, [anchor, target, open, onDirectionChange]);
|
|
42
|
+
return (_jsx("span", { ref: scope, className: "contour-floating-root contour-root", style: { display: "contents" }, children: children }));
|
|
43
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { type TableHTMLAttributes, type ReactNode } from "react";
|
|
2
|
+
import type { NativePartProps } from "../../lib/componentProps.js";
|
|
3
|
+
export type TableProps = Omit<TableHTMLAttributes<HTMLTableElement>, "children"> & {
|
|
4
|
+
/** Accessible table name. The component owns the caption element. */
|
|
5
|
+
caption: string;
|
|
6
|
+
children: ReactNode;
|
|
7
|
+
/** Show the caption visually; it remains accessible when false. @default false */
|
|
8
|
+
showCaption?: boolean;
|
|
9
|
+
scrollProps?: NativePartProps<"div">;
|
|
10
|
+
};
|
|
11
|
+
/** Semantic table styling for application-owned rows, cells and table engines. */
|
|
12
|
+
declare const Table: import("react").ForwardRefExoticComponent<Omit<TableHTMLAttributes<HTMLTableElement>, "children"> & {
|
|
13
|
+
/** Accessible table name. The component owns the caption element. */
|
|
14
|
+
caption: string;
|
|
15
|
+
children: ReactNode;
|
|
16
|
+
/** Show the caption visually; it remains accessible when false. @default false */
|
|
17
|
+
showCaption?: boolean;
|
|
18
|
+
scrollProps?: NativePartProps<"div">;
|
|
19
|
+
} & import("react").RefAttributes<HTMLTableElement>>;
|
|
20
|
+
export default Table;
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { forwardRef } from "react";
|
|
4
|
+
import { ccx } from "../../lib/classNames.js";
|
|
5
|
+
/** Semantic table styling for application-owned rows, cells and table engines. */
|
|
6
|
+
const Table = forwardRef(function Table({ caption, children, showCaption = false, scrollProps, className, ...rest }, ref) {
|
|
7
|
+
return (_jsx("div", { ...scrollProps, className: ccx("table-scroll", scrollProps?.className), tabIndex: scrollProps?.tabIndex ?? 0, role: scrollProps?.role ?? "region", "aria-label": scrollProps?.["aria-label"] ?? caption, children: _jsxs("table", { ...rest, ref: ref, className: ccx("data-table", className), children: [_jsx("caption", { className: showCaption ? undefined : ccx("visually-hidden"), children: caption }), children] }) }));
|
|
8
|
+
});
|
|
9
|
+
export default Table;
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { type RefObject } from "react";
|
|
2
|
+
/** Use public virtual-anchor/Rect extension points; keep upstream focus/selection and positioning. */
|
|
3
|
+
export default function useComboboxAnchor(input: HTMLInputElement | null, floating: RefObject<HTMLDivElement | null>): {
|
|
4
|
+
anchor?: undefined;
|
|
5
|
+
collisionBoundary?: undefined;
|
|
6
|
+
} | {
|
|
7
|
+
anchor: {
|
|
8
|
+
contextElement: HTMLElement;
|
|
9
|
+
getBoundingClientRect(): DOMRect;
|
|
10
|
+
};
|
|
11
|
+
collisionBoundary: {
|
|
12
|
+
readonly x: number;
|
|
13
|
+
readonly y: number;
|
|
14
|
+
readonly width: number;
|
|
15
|
+
readonly height: number;
|
|
16
|
+
};
|
|
17
|
+
};
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { useMemo } from "react";
|
|
3
|
+
import { platform } from "@floating-ui/dom";
|
|
4
|
+
/** Fixed children of a zoomed body need anchor and viewport in the same local CSS units. */
|
|
5
|
+
function windowScale(element) {
|
|
6
|
+
if (!element ||
|
|
7
|
+
platform.getOffsetParent?.(element) !== element.ownerDocument.defaultView)
|
|
8
|
+
return { x: 1, y: 1 };
|
|
9
|
+
const scale = platform.getScale?.(element);
|
|
10
|
+
if (!scale ||
|
|
11
|
+
!("x" in scale) ||
|
|
12
|
+
!("y" in scale) ||
|
|
13
|
+
!Number.isFinite(scale.x) ||
|
|
14
|
+
!Number.isFinite(scale.y) ||
|
|
15
|
+
scale.x <= 0 ||
|
|
16
|
+
scale.y <= 0)
|
|
17
|
+
return { x: 1, y: 1 };
|
|
18
|
+
return scale;
|
|
19
|
+
}
|
|
20
|
+
/** Use public virtual-anchor/Rect extension points; keep upstream focus/selection and positioning. */
|
|
21
|
+
export default function useComboboxAnchor(input, floating) {
|
|
22
|
+
return useMemo(() => {
|
|
23
|
+
const source = input?.parentElement ?? input;
|
|
24
|
+
if (!source)
|
|
25
|
+
return {};
|
|
26
|
+
return {
|
|
27
|
+
anchor: {
|
|
28
|
+
contextElement: source,
|
|
29
|
+
getBoundingClientRect() {
|
|
30
|
+
const rect = source.getBoundingClientRect(), scale = windowScale(floating.current);
|
|
31
|
+
rect.x /= scale.x;
|
|
32
|
+
rect.y /= scale.y;
|
|
33
|
+
rect.width /= scale.x;
|
|
34
|
+
rect.height /= scale.y;
|
|
35
|
+
return rect;
|
|
36
|
+
},
|
|
37
|
+
},
|
|
38
|
+
// Getters are evaluated on each upstream update, including scroll, zoom and mobile viewport resize.
|
|
39
|
+
collisionBoundary: {
|
|
40
|
+
get x() {
|
|
41
|
+
return ((source.ownerDocument.defaultView?.visualViewport?.offsetLeft ??
|
|
42
|
+
0) / windowScale(floating.current).x);
|
|
43
|
+
},
|
|
44
|
+
get y() {
|
|
45
|
+
return ((source.ownerDocument.defaultView?.visualViewport?.offsetTop ?? 0) /
|
|
46
|
+
windowScale(floating.current).y);
|
|
47
|
+
},
|
|
48
|
+
get width() {
|
|
49
|
+
return ((source.ownerDocument.defaultView?.visualViewport?.width ??
|
|
50
|
+
source.ownerDocument.documentElement.clientWidth) /
|
|
51
|
+
windowScale(floating.current).x);
|
|
52
|
+
},
|
|
53
|
+
get height() {
|
|
54
|
+
return ((source.ownerDocument.defaultView?.visualViewport?.height ??
|
|
55
|
+
source.ownerDocument.documentElement.clientHeight) /
|
|
56
|
+
windowScale(floating.current).y);
|
|
57
|
+
},
|
|
58
|
+
},
|
|
59
|
+
};
|
|
60
|
+
}, [input, floating]);
|
|
61
|
+
}
|
|
@@ -24,6 +24,10 @@ export default function useInitialHashScroll() {
|
|
|
24
24
|
align();
|
|
25
25
|
userScrollEvents.forEach((event) => window.addEventListener(event, markScrolled, { passive: true }));
|
|
26
26
|
document.fonts.ready.then(align);
|
|
27
|
-
return () =>
|
|
27
|
+
return () => {
|
|
28
|
+
// Font readiness cannot be cancelled; an unmounted owner must not scroll a later page.
|
|
29
|
+
userScrolled = true;
|
|
30
|
+
userScrollEvents.forEach((event) => window.removeEventListener(event, markScrolled));
|
|
31
|
+
};
|
|
28
32
|
}, []);
|
|
29
33
|
}
|
|
@@ -6,7 +6,12 @@ export default function usePrintPreparation() {
|
|
|
6
6
|
const root = document.documentElement;
|
|
7
7
|
let expanded = [];
|
|
8
8
|
let screenTheme;
|
|
9
|
+
let printing = false;
|
|
9
10
|
const prepare = () => {
|
|
11
|
+
// Some browsers repeat beforeprint while the same print preview is open.
|
|
12
|
+
if (printing)
|
|
13
|
+
return;
|
|
14
|
+
printing = true;
|
|
10
15
|
expanded = [
|
|
11
16
|
...document.querySelectorAll("details:not([open])"),
|
|
12
17
|
];
|
|
@@ -15,9 +20,14 @@ export default function usePrintPreparation() {
|
|
|
15
20
|
root.dataset.theme = "light";
|
|
16
21
|
};
|
|
17
22
|
const restore = () => {
|
|
23
|
+
if (!printing)
|
|
24
|
+
return;
|
|
25
|
+
printing = false;
|
|
18
26
|
expanded.forEach((details) => (details.open = false));
|
|
19
27
|
expanded = [];
|
|
20
|
-
if (screenTheme)
|
|
28
|
+
if (screenTheme === undefined)
|
|
29
|
+
delete root.dataset.theme;
|
|
30
|
+
else
|
|
21
31
|
root.dataset.theme = screenTheme;
|
|
22
32
|
};
|
|
23
33
|
window.addEventListener("beforeprint", prepare);
|
|
@@ -25,6 +35,7 @@ export default function usePrintPreparation() {
|
|
|
25
35
|
return () => {
|
|
26
36
|
window.removeEventListener("beforeprint", prepare);
|
|
27
37
|
window.removeEventListener("afterprint", restore);
|
|
38
|
+
restore();
|
|
28
39
|
};
|
|
29
40
|
}, []);
|
|
30
41
|
}
|
|
@@ -5,10 +5,13 @@ import { useEffect, useState } from "react";
|
|
|
5
5
|
const activationLine = 0.3;
|
|
6
6
|
export default function useScrollSpy(ids) {
|
|
7
7
|
const [activeId, setActiveId] = useState("");
|
|
8
|
-
const key =
|
|
8
|
+
const key = JSON.stringify(ids);
|
|
9
9
|
useEffect(() => {
|
|
10
|
-
const
|
|
11
|
-
|
|
10
|
+
const parsed = JSON.parse(key);
|
|
11
|
+
if (!Array.isArray(parsed) ||
|
|
12
|
+
!parsed.every((id) => typeof id === "string"))
|
|
13
|
+
return;
|
|
14
|
+
const elements = parsed
|
|
12
15
|
.map((id) => document.getElementById(id))
|
|
13
16
|
.filter((element) => element !== null);
|
|
14
17
|
let frame = 0;
|
package/dist/index.d.ts
CHANGED
|
@@ -132,3 +132,5 @@ export type * from "./components/ui/ThemeScope.js";
|
|
|
132
132
|
export type { FloatingOptions } from "./hooks/useFloatingPanel.js";
|
|
133
133
|
export type { PanelFocusOptions } from "./hooks/usePanelFocus.js";
|
|
134
134
|
export type { DismissReason } from "./lib/layers.js";
|
|
135
|
+
export { default as Table, type TableProps } from "./components/ui/Table.js";
|
|
136
|
+
export { default as Combobox, type ComboboxProps, type ComboboxOption, } from "./components/ui/Combobox.js";
|
package/dist/index.js
CHANGED
|
@@ -67,3 +67,5 @@ export * from "./lib/motion.js";
|
|
|
67
67
|
export { default as SceneBoundary } from "./components/hero/SceneBoundary.js";
|
|
68
68
|
export { safeHref } from "./lib/safeHref.js";
|
|
69
69
|
export { default as ThemeScope } from "./components/ui/ThemeScope.js";
|
|
70
|
+
export { default as Table } from "./components/ui/Table.js";
|
|
71
|
+
export { default as Combobox, } from "./components/ui/Combobox.js";
|
|
@@ -277,12 +277,18 @@
|
|
|
277
277
|
}
|
|
278
278
|
|
|
279
279
|
.data-table {
|
|
280
|
+
color: var(--fg);
|
|
280
281
|
width: 100%;
|
|
281
282
|
min-width: 560px;
|
|
282
283
|
border-collapse: collapse;
|
|
283
284
|
font-size: 14px;
|
|
284
285
|
}
|
|
285
286
|
|
|
287
|
+
.data-table caption {
|
|
288
|
+
color: inherit;
|
|
289
|
+
text-align: left;
|
|
290
|
+
}
|
|
291
|
+
|
|
286
292
|
.data-table th {
|
|
287
293
|
padding: 14px 16px 14px 0;
|
|
288
294
|
border-bottom: 1px solid var(--line);
|
package/dist/kit/forms.css
CHANGED
|
@@ -370,3 +370,37 @@ textarea {
|
|
|
370
370
|
var(--line-strong) var(--progress)
|
|
371
371
|
);
|
|
372
372
|
}
|
|
373
|
+
|
|
374
|
+
/* Combobox uses the same native field frame; the popup is an allowed-value list. */
|
|
375
|
+
.combobox { min-width: 0; }
|
|
376
|
+
.combobox-control { display: flex; align-items: center; gap: 8px; }
|
|
377
|
+
.combobox-control input { min-width: 0; flex: 1; }
|
|
378
|
+
.combobox-trigger, .combobox-retry {
|
|
379
|
+
border: 0; background: transparent; color: inherit; font: inherit; cursor: pointer;
|
|
380
|
+
}
|
|
381
|
+
.combobox-trigger { display: grid; place-items: center; min-width: 36px; min-height: 36px; flex: none; }
|
|
382
|
+
.combobox-trigger:disabled { cursor: default; opacity: .45; }
|
|
383
|
+
.combobox-positioner { z-index: 30; }
|
|
384
|
+
.combobox-popup {
|
|
385
|
+
width: var(--anchor-width, 16rem);
|
|
386
|
+
max-width: var(--available-width, calc(100vw - 16px));
|
|
387
|
+
max-height: min(320px, var(--available-height, 320px));
|
|
388
|
+
overflow: auto; overscroll-behavior: contain;
|
|
389
|
+
border: 1px solid var(--line); border-radius: var(--radius-sm);
|
|
390
|
+
background: var(--bg); color: var(--fg); padding: 4px;
|
|
391
|
+
box-shadow: 0 8px 28px rgb(0 0 0 / .12);
|
|
392
|
+
}
|
|
393
|
+
.combobox-list { outline: none; }
|
|
394
|
+
.combobox-option { display: flex; justify-content: space-between; align-items: center; gap: 12px; padding: 10px 12px; overflow-wrap: anywhere; cursor: default; }
|
|
395
|
+
.combobox-option[data-highlighted] { background: var(--invert-bg); color: var(--invert-fg); outline: 2px solid currentColor; outline-offset: -2px; }
|
|
396
|
+
.combobox-option[data-disabled] { opacity: .45; }
|
|
397
|
+
.combobox-status { padding: 12px; font-size: 14px; }
|
|
398
|
+
.combobox-retry { padding: 10px 12px; text-decoration: underline; }
|
|
399
|
+
.combobox-trigger:focus-visible, .combobox-retry:focus-visible { outline: 2px solid currentColor; outline-offset: 2px; }
|
|
400
|
+
@media (forced-colors: active) {
|
|
401
|
+
.combobox-popup { border-color: CanvasText; background: Canvas; color: CanvasText; }
|
|
402
|
+
.combobox-option[data-highlighted] { background: Highlight; color: HighlightText; }
|
|
403
|
+
.combobox-option[data-disabled] { color: GrayText; opacity: 1; }
|
|
404
|
+
}
|
|
405
|
+
/* Keep the live region mounted without a blank row when there is no message. */
|
|
406
|
+
.combobox-status:empty { padding: 0; }
|
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
// Generated compatibility aliases; unknown consumer classes pass through unchanged.
|
|
2
|
-
export const componentClasses = new Set(["alert", "alert-accent", "alert-action", "alert-banner", "alert-body", "alert-content", "alert-danger", "alert-dismiss", "alert-icon", "alert-info", "alert-success", "alert-title", "alert-warning", "avatar", "avatar-group", "avatar-initials", "avatar-lg", "avatar-sm", "avatar-status", "avatar-status-away", "avatar-status-online", "avatar-xl", "badge", "badge-danger", "badge-dot", "badge-info", "badge-invert", "badge-neutral", "badge-success", "badge-warning", "brand", "breadcrumbs", "button", "button-accent", "button-danger", "button-ghost", "button-icon", "button-lg", "button-secondary", "button-sm", "card", "card-acid", "card-body", "card-footer", "card-icon", "card-ink", "card-interactive", "card-title", "card-top", "choice", "choice-box", "choice-control", "choice-description", "choice-error", "choice-input", "choice-invalid", "choice-label", "choice-radio", "choice-text", "count-up", "css", "data-table", "data-table-idle", "depth-button", "depth-button-face", "dialog", "dialog-body", "dialog-close", "dialog-description", "dialog-footer", "dialog-header", "dialog-left", "dialog-panel", "dialog-right", "disclosure", "disclosure-icon", "divider", "divider-labelled", "empty-state", "empty-state-icon", "field", "field-chevron", "field-control", "field-counter", "field-disabled", "field-error", "field-footer", "field-icon", "field-invalid", "field-label", "field-required", "field-select", "field-trailing", "flip-back", "flip-card", "flip-face", "flip-front", "flip-stage", "flip-toggle", "hero-letter", "icon-button", "icon-button-accent", "icon-button-lg", "icon-button-sm", "icon-button-solid", "icon-button-spin", "inline-link", "is-flipped", "is-open", "is-visible", "kbd", "magnetic", "marquee", "marquee-inner", "marquee-track", "menu", "menu-chevron", "menu-end", "menu-item", "menu-item-danger", "menu-list", "menu-start", "menu-trigger", "mono", "opening-sequence", "page-width", "pagination", "pagination-gap", "pagination-page", "pagination-step", "popover", "popover-end", "popover-panel", "popover-start", "popover-title", "progress", "progress-accent", "progress-danger", "progress-fill", "progress-header", "progress-indeterminate", "progress-success", "progress-track", "radio-group", "radio-group-inline", "reveal", "scroll-progress", "sculpture-canvas", "section-label", "segmented", "segmented-option", "site-shell", "skeleton", "skeleton-block", "skeleton-circle", "skeleton-line", "skip-link", "slider", "slider-header", "spinner", "stage-canvas", "stat", "stat-suffix", "stepper", "stepper-complete", "stepper-current", "stepper-description", "stepper-label", "stepper-marker", "stepper-step", "stepper-text", "stepper-upcoming", "switch", "switch-thumb", "switch-track", "table-scroll", "tabs", "tabs-list", "tabs-panel", "tabs-tab", "tag", "tag-removable", "tag-remove", "text-link", "theme-fading", "theme-icon-moon", "theme-icon-sun", "theme-toggle", "tilt-card", "tilt-glare", "tilt-stage", "timeline", "timeline-body", "timeline-current", "timeline-date", "timeline-entry", "timeline-title", "toast", "toast-bar", "toast-close", "toast-content", "toast-danger", "toast-info", "toast-success", "toast-viewport", "toast-warning", "tooltip", "tooltip-anchor", "tooltip-bottom", "tooltip-left", "tooltip-right", "tooltip-top", "ts", "type-line", "type-run", "visually-hidden"]);
|
|
2
|
+
export const componentClasses = new Set(["alert", "alert-accent", "alert-action", "alert-banner", "alert-body", "alert-content", "alert-danger", "alert-dismiss", "alert-icon", "alert-info", "alert-success", "alert-title", "alert-warning", "avatar", "avatar-group", "avatar-initials", "avatar-lg", "avatar-sm", "avatar-status", "avatar-status-away", "avatar-status-online", "avatar-xl", "badge", "badge-danger", "badge-dot", "badge-info", "badge-invert", "badge-neutral", "badge-success", "badge-warning", "brand", "breadcrumbs", "button", "button-accent", "button-danger", "button-ghost", "button-icon", "button-lg", "button-secondary", "button-sm", "card", "card-acid", "card-body", "card-footer", "card-icon", "card-ink", "card-interactive", "card-title", "card-top", "choice", "choice-box", "choice-control", "choice-description", "choice-error", "choice-input", "choice-invalid", "choice-label", "choice-radio", "choice-text", "combobox", "combobox-control", "combobox-list", "combobox-option", "combobox-popup", "combobox-positioner", "combobox-retry", "combobox-status", "combobox-trigger", "count-up", "css", "data-table", "data-table-idle", "depth-button", "depth-button-face", "dialog", "dialog-body", "dialog-close", "dialog-description", "dialog-footer", "dialog-header", "dialog-left", "dialog-panel", "dialog-right", "disclosure", "disclosure-icon", "divider", "divider-labelled", "empty-state", "empty-state-icon", "field", "field-chevron", "field-control", "field-counter", "field-disabled", "field-error", "field-footer", "field-icon", "field-invalid", "field-label", "field-required", "field-select", "field-trailing", "flip-back", "flip-card", "flip-face", "flip-front", "flip-stage", "flip-toggle", "hero-letter", "icon-button", "icon-button-accent", "icon-button-lg", "icon-button-sm", "icon-button-solid", "icon-button-spin", "inline-link", "is-flipped", "is-open", "is-visible", "kbd", "magnetic", "marquee", "marquee-inner", "marquee-track", "menu", "menu-chevron", "menu-end", "menu-item", "menu-item-danger", "menu-list", "menu-start", "menu-trigger", "mono", "opening-sequence", "page-width", "pagination", "pagination-gap", "pagination-page", "pagination-step", "popover", "popover-end", "popover-panel", "popover-start", "popover-title", "progress", "progress-accent", "progress-danger", "progress-fill", "progress-header", "progress-indeterminate", "progress-success", "progress-track", "radio-group", "radio-group-inline", "reveal", "scroll-progress", "sculpture-canvas", "section-label", "segmented", "segmented-option", "site-shell", "skeleton", "skeleton-block", "skeleton-circle", "skeleton-line", "skip-link", "slider", "slider-header", "spinner", "stage-canvas", "stat", "stat-suffix", "stepper", "stepper-complete", "stepper-current", "stepper-description", "stepper-label", "stepper-marker", "stepper-step", "stepper-text", "stepper-upcoming", "switch", "switch-thumb", "switch-track", "table-scroll", "tabs", "tabs-list", "tabs-panel", "tabs-tab", "tag", "tag-removable", "tag-remove", "text-link", "theme-fading", "theme-icon-moon", "theme-icon-sun", "theme-toggle", "tilt-card", "tilt-glare", "tilt-stage", "timeline", "timeline-body", "timeline-current", "timeline-date", "timeline-entry", "timeline-title", "toast", "toast-bar", "toast-close", "toast-content", "toast-danger", "toast-info", "toast-success", "toast-viewport", "toast-warning", "tooltip", "tooltip-anchor", "tooltip-bottom", "tooltip-left", "tooltip-right", "tooltip-top", "ts", "type-line", "type-run", "visually-hidden"]);
|
|
@@ -273,12 +273,18 @@
|
|
|
273
273
|
}
|
|
274
274
|
|
|
275
275
|
.contour-data-table {
|
|
276
|
+
color: var(--contour-fg);
|
|
276
277
|
width: 100%;
|
|
277
278
|
min-width: 560px;
|
|
278
279
|
border-collapse: collapse;
|
|
279
280
|
font-size: 14px;
|
|
280
281
|
}
|
|
281
282
|
|
|
283
|
+
.contour-data-table caption {
|
|
284
|
+
color: inherit;
|
|
285
|
+
text-align: start;
|
|
286
|
+
}
|
|
287
|
+
|
|
282
288
|
.contour-data-table th {
|
|
283
289
|
padding: 14px 16px 14px 0;
|
|
284
290
|
border-bottom: 1px solid var(--contour-line);
|
|
@@ -368,3 +368,37 @@
|
|
|
368
368
|
var(--contour-line-strong) var(--contour-progress, var(--progress))
|
|
369
369
|
);
|
|
370
370
|
}
|
|
371
|
+
|
|
372
|
+
/* Combobox uses the same native field frame; the popup is an allowed-value list. */
|
|
373
|
+
.contour-combobox { min-width: 0; }
|
|
374
|
+
.contour-combobox-control { display: flex; align-items: center; gap: 8px; }
|
|
375
|
+
.contour-combobox-control input { min-width: 0; flex: 1; }
|
|
376
|
+
.contour-combobox-trigger, .contour-combobox-retry {
|
|
377
|
+
border: 0; background: transparent; color: inherit; font: inherit; cursor: pointer;
|
|
378
|
+
}
|
|
379
|
+
.contour-combobox-trigger { display: grid; place-items: center; min-width: 36px; min-height: 36px; flex: none; }
|
|
380
|
+
.contour-combobox-trigger:disabled { cursor: default; opacity: .45; }
|
|
381
|
+
.contour-combobox-positioner { z-index: 30; }
|
|
382
|
+
.contour-combobox-popup {
|
|
383
|
+
width: var(--anchor-width, 16rem);
|
|
384
|
+
max-width: var(--available-width, calc(100vw - 16px));
|
|
385
|
+
max-height: min(320px, var(--available-height, 320px));
|
|
386
|
+
overflow: auto; overscroll-behavior: contain;
|
|
387
|
+
border: 1px solid var(--contour-line); border-radius: var(--contour-radius-sm);
|
|
388
|
+
background: var(--contour-bg); color: var(--contour-fg); padding: 4px;
|
|
389
|
+
box-shadow: 0 8px 28px rgb(0 0 0 / .12);
|
|
390
|
+
}
|
|
391
|
+
.contour-combobox-list { outline: none; }
|
|
392
|
+
.contour-combobox-option { display: flex; justify-content: space-between; align-items: center; gap: 12px; padding: 10px 12px; overflow-wrap: anywhere; cursor: default; }
|
|
393
|
+
.contour-combobox-option[data-highlighted] { background: var(--contour-invert-bg); color: var(--contour-invert-fg); outline: 2px solid currentColor; outline-offset: -2px; }
|
|
394
|
+
.contour-combobox-option[data-disabled] { opacity: .45; }
|
|
395
|
+
.contour-combobox-status { padding: 12px; font-size: 14px; }
|
|
396
|
+
.contour-combobox-retry { padding: 10px 12px; text-decoration: underline; }
|
|
397
|
+
.contour-combobox-trigger:focus-visible, .contour-combobox-retry:focus-visible { outline: 2px solid currentColor; outline-offset: 2px; }
|
|
398
|
+
@media (forced-colors: active) {
|
|
399
|
+
.contour-combobox-popup { border-color: CanvasText; background: Canvas; color: CanvasText; }
|
|
400
|
+
.contour-combobox-option[data-highlighted] { background: Highlight; color: HighlightText; }
|
|
401
|
+
.contour-combobox-option[data-disabled] { color: GrayText; opacity: 1; }
|
|
402
|
+
}
|
|
403
|
+
/* Keep the live region mounted without a blank row when there is no message. */
|
|
404
|
+
.contour-combobox-status:empty { padding: 0; }
|