@stackone/malachite 2.24.2 → 2.25.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
|
@@ -1,56 +1,58 @@
|
|
|
1
1
|
import { jsxs as i, jsx as t } from "react/jsx-runtime";
|
|
2
|
-
import { useId as
|
|
3
|
-
import { DisclosureArrow as
|
|
2
|
+
import { useId as $, useState as b } from "react";
|
|
3
|
+
import { DisclosureArrow as E } from "./DisclosureArrow.js";
|
|
4
4
|
import { FOCUS_RING as _, FOCUS_RING_INSET as A } from "./focusRing.js";
|
|
5
5
|
import { isRenderableNode as F } from "./renderableNode.js";
|
|
6
6
|
const K = ({
|
|
7
7
|
title: x,
|
|
8
|
-
header:
|
|
9
|
-
className:
|
|
10
|
-
children:
|
|
8
|
+
header: u,
|
|
9
|
+
className: f,
|
|
10
|
+
children: a,
|
|
11
11
|
trailing: c,
|
|
12
|
-
variant:
|
|
13
|
-
defaultExpanded:
|
|
12
|
+
variant: v = "default",
|
|
13
|
+
defaultExpanded: h = !1,
|
|
14
14
|
isExpanded: n,
|
|
15
15
|
onToggle: d,
|
|
16
16
|
disabled: s = !1,
|
|
17
17
|
stickyHeader: p = !1,
|
|
18
|
-
stickyTopOffset:
|
|
18
|
+
stickyTopOffset: g = 0,
|
|
19
19
|
animate: m = !0
|
|
20
20
|
}) => {
|
|
21
|
-
const r =
|
|
22
|
-
const
|
|
23
|
-
n === void 0 &&
|
|
24
|
-
},
|
|
21
|
+
const r = v === "sunken", l = `${$()}-body`, [w, N] = b(h), e = n !== void 0 ? n : w, k = m || e && !!a, [y, I] = b(!1), C = () => {
|
|
22
|
+
const o = !e;
|
|
23
|
+
n === void 0 && N(o), d == null || d(o);
|
|
24
|
+
}, R = [
|
|
25
25
|
"flex flex-col w-full text-left",
|
|
26
26
|
r ? "rounded-xl bg-[var(--malachite-card-dimmed-background)] p-[4px]" : "",
|
|
27
|
-
|
|
28
|
-
].filter(Boolean).join(" "),
|
|
27
|
+
f
|
|
28
|
+
].filter(Boolean).join(" "), S = [
|
|
29
29
|
// `group` only when enabled, so the arrow doesn't darken on hover while disabled.
|
|
30
30
|
s ? "" : "group",
|
|
31
31
|
"flex items-center w-full transition-colors",
|
|
32
32
|
r ? "" : "border-0 border-b border-solid border-[var(--malachite-card-border)]",
|
|
33
33
|
p ? "sticky z-10" : "",
|
|
34
34
|
r ? "bg-transparent" : e ? "bg-[var(--malachite-buttonlist-hover-background)]" : s ? "bg-transparent" : "bg-transparent hover:bg-[var(--malachite-buttonlist-hover-background)]"
|
|
35
|
-
].filter(Boolean).join(" "),
|
|
35
|
+
].filter(Boolean).join(" "), B = [
|
|
36
36
|
"flex flex-1 items-center min-w-0 appearance-none border-0 bg-transparent text-left cursor-pointer select-none disabled:cursor-not-allowed",
|
|
37
37
|
r ? `gap-[16px] px-[16px] py-[12px] rounded-lg ${_}` : `gap-[12px] p-[16px] ${A}`
|
|
38
|
-
].join(" "),
|
|
39
|
-
|
|
40
|
-
|
|
38
|
+
].join(" "), j = p ? {
|
|
39
|
+
top: `${g}px`,
|
|
40
|
+
backgroundColor: r ? "var(--malachite-card-dimmed-background)" : "var(--malachite-card-background)"
|
|
41
|
+
} : {};
|
|
42
|
+
return /* @__PURE__ */ i("div", { className: R, children: [
|
|
43
|
+
/* @__PURE__ */ i("div", { className: S, style: j, "data-accordion-header": "", children: [
|
|
41
44
|
/* @__PURE__ */ i(
|
|
42
45
|
"button",
|
|
43
46
|
{
|
|
44
47
|
type: "button",
|
|
45
|
-
className:
|
|
46
|
-
onClick:
|
|
48
|
+
className: B,
|
|
49
|
+
onClick: C,
|
|
47
50
|
"aria-expanded": e,
|
|
48
51
|
"aria-controls": k ? l : void 0,
|
|
49
52
|
disabled: s,
|
|
50
53
|
children: [
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
r ? /* @__PURE__ */ t(u, { expanded: e }) : null
|
|
54
|
+
/* @__PURE__ */ t(E, { expanded: e }),
|
|
55
|
+
u ?? /* @__PURE__ */ t("span", { className: "flex-1 min-w-0 truncate font-medium text-[13px] tracking-[0.13px] text-[var(--malachite-text-primary)]", children: x })
|
|
54
56
|
]
|
|
55
57
|
}
|
|
56
58
|
),
|
|
@@ -67,18 +69,18 @@ const K = ({
|
|
|
67
69
|
id: l,
|
|
68
70
|
className: "grid grid-cols-[minmax(0,1fr)] w-full transition-[grid-template-rows] duration-200 ease-in-out",
|
|
69
71
|
style: { gridTemplateRows: e ? "1fr" : "0fr" },
|
|
70
|
-
onTransitionEnd: (
|
|
71
|
-
|
|
72
|
+
onTransitionEnd: (o) => {
|
|
73
|
+
o.propertyName === "grid-template-rows" && I(e);
|
|
72
74
|
},
|
|
73
75
|
children: /* @__PURE__ */ t(
|
|
74
76
|
"div",
|
|
75
77
|
{
|
|
76
|
-
className:
|
|
77
|
-
children:
|
|
78
|
+
className: y && e ? "overflow-visible" : "overflow-hidden",
|
|
79
|
+
children: a && /* @__PURE__ */ t(
|
|
78
80
|
"div",
|
|
79
81
|
{
|
|
80
82
|
className: r ? "pt-[4px]" : "border-b border-solid border-[var(--malachite-card-border)]",
|
|
81
|
-
children:
|
|
83
|
+
children: a
|
|
82
84
|
}
|
|
83
85
|
)
|
|
84
86
|
}
|
|
@@ -87,12 +89,12 @@ const K = ({
|
|
|
87
89
|
)
|
|
88
90
|
) : (
|
|
89
91
|
/* Instant open — no clip, so a sticky child inside pins immediately. */
|
|
90
|
-
e &&
|
|
92
|
+
e && a && /* @__PURE__ */ t(
|
|
91
93
|
"div",
|
|
92
94
|
{
|
|
93
95
|
id: l,
|
|
94
96
|
className: r ? "pt-[4px]" : "border-b border-solid border-[var(--malachite-card-border)]",
|
|
95
|
-
children:
|
|
97
|
+
children: a
|
|
96
98
|
}
|
|
97
99
|
)
|
|
98
100
|
)
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Accordion.js","sources":["../../src/components/Accordion.tsx"],"sourcesContent":["import React, { useId, useState } from 'react';\nimport DisclosureArrow from './DisclosureArrow';\nimport { FOCUS_RING, FOCUS_RING_INSET } from './focusRing';\nimport { isRenderableNode } from './renderableNode';\n\ninterface AccordionBaseProps {\n className?: string;\n children?: React.ReactNode;\n /**\n * Visual style. `default` is the flat list row (arrow leading, row dividers,\n * hover background). `sunken` draws the section as a sunken card (dimmed\n * rounded container, arrow pinned right, no dividers/hover) — used for stacked\n * detail sections in side panels and detail pages (see `CollapsibleSection`).\n */\n variant?: 'default' | 'sunken';\n /**\n * Content pinned to the right of the header, OUTSIDE the toggle button — so it may\n * be interactive (e.g. a Checkbox) without nesting a button inside a button.\n */\n trailing?: React.ReactNode;\n defaultExpanded?: boolean;\n isExpanded?: boolean;\n onToggle?: (expanded: boolean) => void;\n /** Disables the toggle (and its hover). Trailing controls manage their own state. */\n disabled?: boolean;\n /**\n * Pin the header row to the top of the nearest scroll container so it stays\n * reachable while the body scrolls. Needs a scroll ancestor that clips without\n * trapping sticky — e.g. a `<Surface overflow=\"clip\">` (`overflow: hidden` traps it).\n */\n stickyHeader?: boolean;\n /** Offset (px) from the top of the scroll container when `stickyHeader` is set. */\n stickyTopOffset?: number;\n /**\n * Animate the expand/collapse height (default `true`). Set `false` for an instant\n * open — the body isn't clipped, so a `position: sticky` child pins immediately with\n * no post-animation settle.\n */\n animate?: boolean;\n}\n\n/**\n * Requires at least one of `title` (simple text header) or `header` (rich ReactNode\n * content for the toggle area, e.g. avatar + badges) so the toggle is never empty.\n */\ntype AccordionProps = AccordionBaseProps &\n ({ title: string; header?: React.ReactNode } | { title?: string; header: React.ReactNode });\n\nconst Accordion: React.FC<AccordionProps> = ({\n title,\n header,\n className,\n children,\n trailing,\n variant = 'default',\n defaultExpanded = false,\n isExpanded,\n onToggle,\n disabled = false,\n stickyHeader = false,\n stickyTopOffset = 0,\n animate = true,\n}) => {\n const sunken = variant === 'sunken';\n const instanceId = useId();\n const bodyId = `${instanceId}-body`;\n const [internalExpanded, setInternalExpanded] = useState(defaultExpanded);\n\n // Use controlled state if isExpanded is provided, otherwise use internal state\n const expanded = isExpanded !== undefined ? isExpanded : internalExpanded;\n\n // The instant path only renders the body while expanded; don't advertise\n // aria-controls when the referenced element isn't in the DOM.\n const bodyRendered = animate || (expanded && !!children);\n\n // The body clips overflow so the expand/collapse height animation works. Once fully\n // expanded we release the clip (after the transition) so a `position: sticky` child\n // inside can pin — `overflow: hidden` would otherwise trap it.\n //\n // Owned entirely by the transition: the same `onTransitionEnd` that sets it on expand\n // clears it on collapse, so no effect has to mirror `expanded` into it.\n const [bodySettled, setBodySettled] = useState(false);\n\n const handleToggle = () => {\n const newExpanded = !expanded;\n if (isExpanded === undefined) {\n setInternalExpanded(newExpanded);\n }\n onToggle?.(newExpanded);\n };\n\n const containerClasses = [\n 'flex flex-col w-full text-left',\n sunken ? 'rounded-xl bg-[var(--malachite-card-dimmed-background)] p-[4px]' : '',\n className,\n ]\n .filter(Boolean)\n .join(' ');\n\n // The whole header row is a `group` so the disclosure arrow darkens on hover; the\n // hover background and arrow react to hovering anywhere on the row. The sunken\n // variant carries its own card chrome instead of row dividers/hover.\n const rowBackground = sunken\n ? 'bg-transparent'\n : expanded\n ? 'bg-[var(--malachite-buttonlist-hover-background)]'\n : disabled\n ? 'bg-transparent'\n : 'bg-transparent hover:bg-[var(--malachite-buttonlist-hover-background)]';\n const headerRowClasses = [\n // `group` only when enabled, so the arrow doesn't darken on hover while disabled.\n disabled ? '' : 'group',\n 'flex items-center w-full transition-colors',\n sunken ? '' : 'border-0 border-b border-solid border-[var(--malachite-card-border)]',\n stickyHeader ? 'sticky z-10' : '',\n rowBackground,\n ]\n .filter(Boolean)\n .join(' ');\n // Full-bleed default rows use an inset ring; the padded sunken toggle fits an outset one.\n const toggleButtonClasses = [\n 'flex flex-1 items-center min-w-0 appearance-none border-0 bg-transparent text-left cursor-pointer select-none disabled:cursor-not-allowed',\n sunken\n ? `gap-[16px] px-[16px] py-[12px] rounded-lg ${FOCUS_RING}`\n : `gap-[12px] p-[16px] ${FOCUS_RING_INSET}`,\n ].join(' ');\n\n // A pinned header needs an opaque background so the scrolling body doesn't bleed\n // through. Inline (not a Tailwind arbitrary class) so the offset always applies.\n const headerRowStyle: React.CSSProperties = stickyHeader\n ? { top: `${stickyTopOffset}px`, backgroundColor: 'var(--malachite-card-background)' }\n : {};\n\n return (\n <div className={containerClasses}>\n <div className={headerRowClasses} style={headerRowStyle} data-accordion-header=\"\">\n {/* Toggle area — arrow + header/title. Interactive trailing sits outside it. */}\n <button\n type=\"button\"\n className={toggleButtonClasses}\n onClick={handleToggle}\n aria-expanded={expanded}\n aria-controls={bodyRendered ? bodyId : undefined}\n disabled={disabled}\n >\n {sunken ? null : <DisclosureArrow expanded={expanded} />}\n {header ?? (\n <span className=\"flex-1 min-w-0 truncate font-medium text-[13px] tracking-[0.13px] text-[var(--malachite-text-primary)]\">\n {title}\n </span>\n )}\n {sunken ? <DisclosureArrow expanded={expanded} /> : null}\n </button>\n {isRenderableNode(trailing) && (\n <div className=\"shrink-0 flex items-center pr-[16px] text-[var(--malachite-text-secondary)]\">\n {trailing}\n </div>\n )}\n </div>\n\n {animate ? (\n /* Height animates via grid-template-rows (0fr → 1fr); body clips during.\n grid-cols-[minmax(0,1fr)] pins the single column to the track width so\n wide, non-wrapping children (e.g. a table with long cell text) can't grow\n the implicit `auto` column to their max-content and overflow the container. */\n <div\n id={bodyId}\n className=\"grid grid-cols-[minmax(0,1fr)] w-full transition-[grid-template-rows] duration-200 ease-in-out\"\n style={{ gridTemplateRows: expanded ? '1fr' : '0fr' }}\n onTransitionEnd={(event) => {\n if (event.propertyName === 'grid-template-rows') {\n setBodySettled(expanded);\n }\n }}\n >\n <div\n className={bodySettled && expanded ? 'overflow-visible' : 'overflow-hidden'}\n >\n {children && (\n <div\n className={\n sunken\n ? 'pt-[4px]'\n : 'border-b border-solid border-[var(--malachite-card-border)]'\n }\n >\n {children}\n </div>\n )}\n </div>\n </div>\n ) : (\n /* Instant open — no clip, so a sticky child inside pins immediately. */\n expanded &&\n children && (\n <div\n id={bodyId}\n className={\n sunken\n ? 'pt-[4px]'\n : 'border-b border-solid border-[var(--malachite-card-border)]'\n }\n >\n {children}\n </div>\n )\n )}\n </div>\n );\n};\n\nexport default Accordion;\nexport { Accordion };\nexport type { AccordionProps };\n"],"names":["Accordion","title","header","className","children","trailing","variant","defaultExpanded","isExpanded","onToggle","disabled","stickyHeader","stickyTopOffset","animate","sunken","bodyId","useId","internalExpanded","setInternalExpanded","useState","expanded","bodyRendered","bodySettled","setBodySettled","handleToggle","newExpanded","containerClasses","headerRowClasses","toggleButtonClasses","FOCUS_RING","FOCUS_RING_INSET","headerRowStyle","jsxs","jsx","DisclosureArrow","isRenderableNode","event"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"Accordion.js","sources":["../../src/components/Accordion.tsx"],"sourcesContent":["import React, { useId, useState } from 'react';\nimport DisclosureArrow from './DisclosureArrow';\nimport { FOCUS_RING, FOCUS_RING_INSET } from './focusRing';\nimport { isRenderableNode } from './renderableNode';\n\ninterface AccordionBaseProps {\n className?: string;\n children?: React.ReactNode;\n /**\n * Visual style. `default` is the flat list row (row dividers, hover background).\n * `sunken` draws the section as a sunken card (dimmed rounded container, no\n * dividers/hover) — used for stacked detail sections in side panels and detail\n * pages (see `CollapsibleSection`). Both lead with the arrow: it is the affordance\n * that says the section opens, and a reader scanning a stack of them finds it in\n * one column rather than at whatever width each header happens to end.\n */\n variant?: 'default' | 'sunken';\n /**\n * Content pinned to the right of the header, OUTSIDE the toggle button — so it may\n * be interactive (e.g. a Checkbox) without nesting a button inside a button.\n */\n trailing?: React.ReactNode;\n defaultExpanded?: boolean;\n isExpanded?: boolean;\n onToggle?: (expanded: boolean) => void;\n /** Disables the toggle (and its hover). Trailing controls manage their own state. */\n disabled?: boolean;\n /**\n * Pin the header row to the top of the nearest scroll container so it stays\n * reachable while the body scrolls. Needs a scroll ancestor that clips without\n * trapping sticky — e.g. a `<Surface overflow=\"clip\">` (`overflow: hidden` traps it).\n */\n stickyHeader?: boolean;\n /** Offset (px) from the top of the scroll container when `stickyHeader` is set. */\n stickyTopOffset?: number;\n /**\n * Animate the expand/collapse height (default `true`). Set `false` for an instant\n * open — the body isn't clipped, so a `position: sticky` child pins immediately with\n * no post-animation settle.\n */\n animate?: boolean;\n}\n\n/**\n * Requires at least one of `title` (simple text header) or `header` (rich ReactNode\n * content for the toggle area, e.g. avatar + badges) so the toggle is never empty.\n */\ntype AccordionProps = AccordionBaseProps &\n ({ title: string; header?: React.ReactNode } | { title?: string; header: React.ReactNode });\n\nconst Accordion: React.FC<AccordionProps> = ({\n title,\n header,\n className,\n children,\n trailing,\n variant = 'default',\n defaultExpanded = false,\n isExpanded,\n onToggle,\n disabled = false,\n stickyHeader = false,\n stickyTopOffset = 0,\n animate = true,\n}) => {\n const sunken = variant === 'sunken';\n const instanceId = useId();\n const bodyId = `${instanceId}-body`;\n const [internalExpanded, setInternalExpanded] = useState(defaultExpanded);\n\n // Use controlled state if isExpanded is provided, otherwise use internal state\n const expanded = isExpanded !== undefined ? isExpanded : internalExpanded;\n\n // The instant path only renders the body while expanded; don't advertise\n // aria-controls when the referenced element isn't in the DOM.\n const bodyRendered = animate || (expanded && !!children);\n\n // The body clips overflow so the expand/collapse height animation works. Once fully\n // expanded we release the clip (after the transition) so a `position: sticky` child\n // inside can pin — `overflow: hidden` would otherwise trap it.\n //\n // Owned entirely by the transition: the same `onTransitionEnd` that sets it on expand\n // clears it on collapse, so no effect has to mirror `expanded` into it.\n const [bodySettled, setBodySettled] = useState(false);\n\n const handleToggle = () => {\n const newExpanded = !expanded;\n if (isExpanded === undefined) {\n setInternalExpanded(newExpanded);\n }\n onToggle?.(newExpanded);\n };\n\n const containerClasses = [\n 'flex flex-col w-full text-left',\n sunken ? 'rounded-xl bg-[var(--malachite-card-dimmed-background)] p-[4px]' : '',\n className,\n ]\n .filter(Boolean)\n .join(' ');\n\n // The whole header row is a `group` so the disclosure arrow darkens on hover; the\n // hover background and arrow react to hovering anywhere on the row. The sunken\n // variant carries its own card chrome instead of row dividers/hover.\n const rowBackground = sunken\n ? 'bg-transparent'\n : expanded\n ? 'bg-[var(--malachite-buttonlist-hover-background)]'\n : disabled\n ? 'bg-transparent'\n : 'bg-transparent hover:bg-[var(--malachite-buttonlist-hover-background)]';\n const headerRowClasses = [\n // `group` only when enabled, so the arrow doesn't darken on hover while disabled.\n disabled ? '' : 'group',\n 'flex items-center w-full transition-colors',\n sunken ? '' : 'border-0 border-b border-solid border-[var(--malachite-card-border)]',\n stickyHeader ? 'sticky z-10' : '',\n rowBackground,\n ]\n .filter(Boolean)\n .join(' ');\n // Full-bleed default rows use an inset ring; the padded sunken toggle fits an outset one.\n const toggleButtonClasses = [\n 'flex flex-1 items-center min-w-0 appearance-none border-0 bg-transparent text-left cursor-pointer select-none disabled:cursor-not-allowed',\n sunken\n ? `gap-[16px] px-[16px] py-[12px] rounded-lg ${FOCUS_RING}`\n : `gap-[12px] p-[16px] ${FOCUS_RING_INSET}`,\n ].join(' ');\n\n // A pinned header needs an opaque background so the scrolling body doesn't bleed\n // through. Inline (not a Tailwind arbitrary class) so the offset always applies. It is\n // the variant's own ground, not the card's: a sunken section pinned in card white is a\n // white bar floating over the grey it belongs to.\n const headerRowStyle: React.CSSProperties = stickyHeader\n ? {\n top: `${stickyTopOffset}px`,\n backgroundColor: sunken\n ? 'var(--malachite-card-dimmed-background)'\n : 'var(--malachite-card-background)',\n }\n : {};\n\n return (\n <div className={containerClasses}>\n <div className={headerRowClasses} style={headerRowStyle} data-accordion-header=\"\">\n {/* Toggle area — arrow + header/title. Interactive trailing sits outside it. */}\n <button\n type=\"button\"\n className={toggleButtonClasses}\n onClick={handleToggle}\n aria-expanded={expanded}\n aria-controls={bodyRendered ? bodyId : undefined}\n disabled={disabled}\n >\n <DisclosureArrow expanded={expanded} />\n {header ?? (\n <span className=\"flex-1 min-w-0 truncate font-medium text-[13px] tracking-[0.13px] text-[var(--malachite-text-primary)]\">\n {title}\n </span>\n )}\n </button>\n {isRenderableNode(trailing) && (\n <div className=\"shrink-0 flex items-center pr-[16px] text-[var(--malachite-text-secondary)]\">\n {trailing}\n </div>\n )}\n </div>\n\n {animate ? (\n /* Height animates via grid-template-rows (0fr → 1fr); body clips during.\n grid-cols-[minmax(0,1fr)] pins the single column to the track width so\n wide, non-wrapping children (e.g. a table with long cell text) can't grow\n the implicit `auto` column to their max-content and overflow the container. */\n <div\n id={bodyId}\n className=\"grid grid-cols-[minmax(0,1fr)] w-full transition-[grid-template-rows] duration-200 ease-in-out\"\n style={{ gridTemplateRows: expanded ? '1fr' : '0fr' }}\n onTransitionEnd={(event) => {\n if (event.propertyName === 'grid-template-rows') {\n setBodySettled(expanded);\n }\n }}\n >\n <div\n className={bodySettled && expanded ? 'overflow-visible' : 'overflow-hidden'}\n >\n {children && (\n <div\n className={\n sunken\n ? 'pt-[4px]'\n : 'border-b border-solid border-[var(--malachite-card-border)]'\n }\n >\n {children}\n </div>\n )}\n </div>\n </div>\n ) : (\n /* Instant open — no clip, so a sticky child inside pins immediately. */\n expanded &&\n children && (\n <div\n id={bodyId}\n className={\n sunken\n ? 'pt-[4px]'\n : 'border-b border-solid border-[var(--malachite-card-border)]'\n }\n >\n {children}\n </div>\n )\n )}\n </div>\n );\n};\n\nexport default Accordion;\nexport { Accordion };\nexport type { AccordionProps };\n"],"names":["Accordion","title","header","className","children","trailing","variant","defaultExpanded","isExpanded","onToggle","disabled","stickyHeader","stickyTopOffset","animate","sunken","bodyId","useId","internalExpanded","setInternalExpanded","useState","expanded","bodyRendered","bodySettled","setBodySettled","handleToggle","newExpanded","containerClasses","headerRowClasses","toggleButtonClasses","FOCUS_RING","FOCUS_RING_INSET","headerRowStyle","jsxs","jsx","DisclosureArrow","isRenderableNode","event"],"mappings":"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"}
|
|
@@ -1,56 +1,67 @@
|
|
|
1
|
-
import { jsx as o, jsxs as
|
|
2
|
-
import { FOCUS_RING_INSET as
|
|
3
|
-
import { isRenderableNode as
|
|
4
|
-
import { FOREGROUND_RESET as
|
|
5
|
-
const
|
|
1
|
+
import { jsx as o, jsxs as C } from "react/jsx-runtime";
|
|
2
|
+
import { FOCUS_RING_INSET as $ } from "./focusRing.js";
|
|
3
|
+
import { isRenderableNode as k } from "./renderableNode.js";
|
|
4
|
+
import { FOREGROUND_RESET as D, SELECTED_TEXT_CONTEXT as j, SELECTED_ROW_ACCENT as F, SELECTED_ROW_FOREGROUND as A, SELECTED_ROW_BACKGROUND as y } from "./selectedRow.js";
|
|
5
|
+
const G = {
|
|
6
|
+
default: "",
|
|
7
|
+
muted: "bg-[var(--malachite-card-dimmed-background)]",
|
|
8
|
+
success: "bg-[var(--malachite-tag-success-background)]",
|
|
9
|
+
error: "bg-[var(--malachite-tag-error-background)]"
|
|
10
|
+
}, U = {
|
|
11
|
+
success: "var(--malachite-tag-success-foreground)",
|
|
12
|
+
error: "var(--malachite-tag-error-foreground)"
|
|
13
|
+
}, I = "0px 0px 4px 0px rgba(0,0,0,0.08), 0px 4px 4px 0px rgba(0,0,0,0.08), 0px 4px 10px 0px rgba(0,0,0,0.05)", W = ({
|
|
6
14
|
children: t,
|
|
7
|
-
divider:
|
|
15
|
+
divider: p = !1,
|
|
8
16
|
disabled: a = !1,
|
|
9
17
|
onClick: e,
|
|
10
18
|
href: l,
|
|
11
|
-
selected:
|
|
19
|
+
selected: c,
|
|
12
20
|
selection: b,
|
|
13
21
|
trailing: u,
|
|
14
|
-
tone:
|
|
22
|
+
tone: m = "default",
|
|
15
23
|
role: r,
|
|
16
24
|
"aria-label": n,
|
|
17
|
-
"aria-labelledby":
|
|
25
|
+
"aria-labelledby": i,
|
|
18
26
|
className: E = "",
|
|
19
|
-
style:
|
|
27
|
+
style: R
|
|
20
28
|
}) => {
|
|
21
|
-
const
|
|
29
|
+
const f = !!(e || l), w = a && f ? "" : "transition-colors hover:bg-[var(--malachite-buttonlist-hover-background)]", O = m !== "default", S = [
|
|
22
30
|
"w-full text-left border-0",
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
].filter(Boolean).join(" "),
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
].filter(Boolean).join(" "),
|
|
30
|
-
backgroundImage: `linear-gradient(${
|
|
31
|
-
color:
|
|
32
|
-
boxShadow: `inset 2px 0 0 ${
|
|
33
|
-
...
|
|
34
|
-
} : {},
|
|
31
|
+
G[m],
|
|
32
|
+
p ? "border-b border-solid border-[var(--malachite-card-border)]" : "",
|
|
33
|
+
w
|
|
34
|
+
].filter(Boolean).join(" "), v = [
|
|
35
|
+
f ? `appearance-none ${O ? "" : "bg-transparent"} p-0 ${$}` : "",
|
|
36
|
+
f && !a ? "cursor-pointer" : ""
|
|
37
|
+
].filter(Boolean).join(" "), h = [S, v, E].filter(Boolean).join(" "), T = c && (b ? b === "highlight" : r !== "checkbox") ? {
|
|
38
|
+
backgroundImage: `linear-gradient(${y}, ${y})`,
|
|
39
|
+
color: A,
|
|
40
|
+
boxShadow: `inset 2px 0 0 ${F}`,
|
|
41
|
+
...j
|
|
42
|
+
} : {}, g = U[m], x = { ...g ? {
|
|
43
|
+
color: g,
|
|
44
|
+
"--malachite-ctx-text-primary": g
|
|
45
|
+
} : {}, ...T, ...R }, s = r === "radio" || r === "checkbox" ? !!c : void 0, d = s === void 0 && c ? !0 : void 0, N = {
|
|
35
46
|
role: r,
|
|
36
|
-
"aria-checked":
|
|
47
|
+
"aria-checked": s,
|
|
37
48
|
"aria-current": d,
|
|
38
49
|
"aria-label": n,
|
|
39
|
-
"aria-labelledby":
|
|
50
|
+
"aria-labelledby": i,
|
|
40
51
|
"aria-disabled": r && a ? !0 : void 0
|
|
41
52
|
};
|
|
42
|
-
if (
|
|
43
|
-
const
|
|
53
|
+
if (k(u)) {
|
|
54
|
+
const _ = l && !a ? /* @__PURE__ */ o(
|
|
44
55
|
"a",
|
|
45
56
|
{
|
|
46
57
|
href: l,
|
|
47
58
|
onClick: e,
|
|
48
|
-
className: `${
|
|
59
|
+
className: `${v} border-0 flex-1 min-w-0 text-left no-underline text-inherit`,
|
|
49
60
|
role: r,
|
|
50
|
-
"aria-checked":
|
|
61
|
+
"aria-checked": s,
|
|
51
62
|
"aria-current": d,
|
|
52
63
|
"aria-label": n,
|
|
53
|
-
"aria-labelledby":
|
|
64
|
+
"aria-labelledby": i,
|
|
54
65
|
children: t
|
|
55
66
|
}
|
|
56
67
|
) : e ? /* @__PURE__ */ o(
|
|
@@ -59,26 +70,26 @@ const A = "0px 0px 4px 0px rgba(0,0,0,0.08), 0px 4px 4px 0px rgba(0,0,0,0.08), 0
|
|
|
59
70
|
type: "button",
|
|
60
71
|
onClick: e,
|
|
61
72
|
disabled: a,
|
|
62
|
-
className: `${
|
|
73
|
+
className: `${v} border-0 flex-1 min-w-0 text-left`,
|
|
63
74
|
role: r,
|
|
64
|
-
"aria-checked":
|
|
75
|
+
"aria-checked": s,
|
|
65
76
|
"aria-current": d,
|
|
66
77
|
"aria-label": n,
|
|
67
|
-
"aria-labelledby":
|
|
78
|
+
"aria-labelledby": i,
|
|
68
79
|
children: t
|
|
69
80
|
}
|
|
70
81
|
) : (
|
|
71
82
|
// The set goes here, not on the wrapper: `radio` and `checkbox` make their
|
|
72
83
|
// descendants presentational, which would bury a control in the trailing slot.
|
|
73
|
-
/* @__PURE__ */ o("div", { className: "flex-1 min-w-0", ...
|
|
84
|
+
/* @__PURE__ */ o("div", { className: "flex-1 min-w-0", ...N, children: t })
|
|
74
85
|
);
|
|
75
|
-
return /* @__PURE__ */
|
|
86
|
+
return /* @__PURE__ */ C(
|
|
76
87
|
"div",
|
|
77
88
|
{
|
|
78
|
-
className: [
|
|
89
|
+
className: [S, "flex items-center", E].filter(Boolean).join(" "),
|
|
79
90
|
style: x,
|
|
80
91
|
children: [
|
|
81
|
-
|
|
92
|
+
_,
|
|
82
93
|
/* @__PURE__ */ o("div", { className: "shrink-0 flex items-center", children: u })
|
|
83
94
|
]
|
|
84
95
|
}
|
|
@@ -89,13 +100,13 @@ const A = "0px 0px 4px 0px rgba(0,0,0,0.08), 0px 4px 4px 0px rgba(0,0,0,0.08), 0
|
|
|
89
100
|
{
|
|
90
101
|
href: l,
|
|
91
102
|
onClick: e,
|
|
92
|
-
className: `${
|
|
103
|
+
className: `${h} no-underline text-inherit`,
|
|
93
104
|
style: x,
|
|
94
105
|
role: r,
|
|
95
|
-
"aria-checked":
|
|
106
|
+
"aria-checked": s,
|
|
96
107
|
"aria-current": d,
|
|
97
108
|
"aria-label": n,
|
|
98
|
-
"aria-labelledby":
|
|
109
|
+
"aria-labelledby": i,
|
|
99
110
|
children: t
|
|
100
111
|
}
|
|
101
112
|
) : e ? /* @__PURE__ */ o(
|
|
@@ -104,40 +115,40 @@ const A = "0px 0px 4px 0px rgba(0,0,0,0.08), 0px 4px 4px 0px rgba(0,0,0,0.08), 0
|
|
|
104
115
|
type: "button",
|
|
105
116
|
onClick: e,
|
|
106
117
|
disabled: a,
|
|
107
|
-
className:
|
|
118
|
+
className: h,
|
|
108
119
|
style: x,
|
|
109
120
|
role: r,
|
|
110
|
-
"aria-checked":
|
|
121
|
+
"aria-checked": s,
|
|
111
122
|
"aria-current": d,
|
|
112
123
|
"aria-label": n,
|
|
113
|
-
"aria-labelledby":
|
|
124
|
+
"aria-labelledby": i,
|
|
114
125
|
children: t
|
|
115
126
|
}
|
|
116
|
-
) : /* @__PURE__ */ o("div", { className:
|
|
117
|
-
},
|
|
127
|
+
) : /* @__PURE__ */ o("div", { className: h, style: x, ...N, children: t });
|
|
128
|
+
}, B = ({
|
|
118
129
|
children: t,
|
|
119
|
-
padding:
|
|
130
|
+
padding: p = !1,
|
|
120
131
|
overflow: a = "hidden",
|
|
121
132
|
role: e,
|
|
122
133
|
"aria-label": l,
|
|
123
|
-
"aria-labelledby":
|
|
134
|
+
"aria-labelledby": c,
|
|
124
135
|
className: b = "",
|
|
125
136
|
style: u
|
|
126
137
|
}) => /* @__PURE__ */ o(
|
|
127
138
|
"div",
|
|
128
139
|
{
|
|
129
|
-
className: `w-full rounded-[var(--malachite-radius-xxl,14px)] ${a === "clip" ? "overflow-clip" : a === "visible" ? "overflow-visible" : "overflow-hidden"} bg-[var(--malachite-card-background)] border border-solid border-[var(--malachite-card-border)] ${
|
|
130
|
-
style: { boxShadow:
|
|
140
|
+
className: `w-full rounded-[var(--malachite-radius-xxl,14px)] ${a === "clip" ? "overflow-clip" : a === "visible" ? "overflow-visible" : "overflow-hidden"} bg-[var(--malachite-card-background)] border border-solid border-[var(--malachite-card-border)] ${p ? "py-3 px-4" : ""} ${b}`,
|
|
141
|
+
style: { boxShadow: I, ...D, ...u },
|
|
131
142
|
role: e,
|
|
132
143
|
"aria-label": l,
|
|
133
|
-
"aria-labelledby":
|
|
144
|
+
"aria-labelledby": c,
|
|
134
145
|
children: t
|
|
135
146
|
}
|
|
136
147
|
);
|
|
137
|
-
|
|
148
|
+
B.Row = W;
|
|
138
149
|
export {
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
150
|
+
B as Surface,
|
|
151
|
+
W as SurfaceRow,
|
|
152
|
+
B as default
|
|
142
153
|
};
|
|
143
154
|
//# sourceMappingURL=Surface.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Surface.js","sources":["../../src/components/Surface.tsx"],"sourcesContent":["import React from 'react';\nimport { FOCUS_RING_INSET } from './focusRing';\nimport { isRenderableNode } from './renderableNode';\nimport {\n FOREGROUND_RESET,\n SELECTED_ROW_ACCENT,\n SELECTED_ROW_BACKGROUND,\n SELECTED_ROW_FOREGROUND,\n SELECTED_TEXT_CONTEXT,\n} from './selectedRow';\n\nexport interface SurfaceProps {\n children?: React.ReactNode;\n padding?: boolean;\n /**\n * Content overflow. Defaults to `hidden` so children are clipped to the rounded\n * corners. Use `clip` to keep the rounded clipping WITHOUT creating a scroll\n * container — so a `position: sticky` descendant still pins (unlike `hidden`,\n * which traps it). `visible` disables clipping entirely.\n */\n overflow?: 'hidden' | 'visible' | 'clip';\n role?: React.AriaRole;\n 'aria-label'?: string;\n 'aria-labelledby'?: string;\n className?: string;\n style?: React.CSSProperties;\n}\n\nexport interface SurfaceRowProps {\n children?: React.ReactNode;\n /** Renders a bottom divider — omit on the last row in a Surface. */\n divider?: boolean;\n /** Removes the hover affordance / disables the control (e.g. while loading/busy). */\n disabled?: boolean;\n /**\n * When provided, the whole row becomes clickable: it renders as a `<button>`\n * (or `<a>` when `href` is set), so the entire row is the click target with\n * native focus + Enter/Space — no manual tabIndex/key handlers. For a\n * selectable row, put a presentational `<Radio>`/`<Checkbox>` inside as the\n * visual indicator; do NOT nest an interactive control (invalid + double tab stop).\n */\n onClick?: (event: React.MouseEvent) => void;\n /** Renders the clickable row as an anchor instead of a button. */\n href?: string;\n /** Announces the row as selected — `aria-checked` under a `radio`/`checkbox` role,\n * `aria-current` otherwise — and paints `Table`'s selected treatment unless `selection`\n * says not to. */\n selected?: boolean;\n /** How a `selected` row shows it. `highlight` paints the treatment; `announce` leaves the\n * visual to a control inside the row and only sets the ARIA state. Defaults to `announce`\n * under `role=\"checkbox\"` and `highlight` otherwise. */\n selection?: 'highlight' | 'announce';\n /** Content pinned right, outside the row button, so it may hold its own control. */\n trailing?: React.ReactNode;\n /** `muted` tints a row whose subject is inactive — not disabled: it stays interactive. */\n tone?: 'default' | 'muted';\n /** ARIA role for a clickable row, e.g. `radio`/`checkbox` (wrap the list in a `radiogroup`). */\n role?: React.AriaRole;\n 'aria-label'?: string;\n 'aria-labelledby'?: string;\n className?: string;\n style?: React.CSSProperties;\n}\n\nconst TRIPLE_SHADOW =\n '0px 0px 4px 0px rgba(0,0,0,0.08), 0px 4px 4px 0px rgba(0,0,0,0.08), 0px 4px 10px 0px rgba(0,0,0,0.05)';\n\n/**\n * A single full-bleed row inside a <Surface>. Owns the row chrome — hover\n * highlight, colour transition and an optional bottom divider — so consumers\n * compose their own content inside without re-implementing design-system styling.\n * Pass `onClick`/`href` to make the entire row clickable (rendered as a real\n * button/anchor, so keyboard navigation works natively).\n */\nconst SurfaceRow: React.FC<SurfaceRowProps> = ({\n children,\n divider = false,\n disabled = false,\n onClick,\n href,\n selected,\n selection,\n trailing,\n tone = 'default',\n role,\n 'aria-label': ariaLabel,\n 'aria-labelledby': ariaLabelledBy,\n className = '',\n style,\n}) => {\n const interactive = Boolean(onClick || href);\n const hover =\n disabled && interactive\n ? ''\n : 'transition-colors hover:bg-[var(--malachite-buttonlist-hover-background)]';\n\n // A class, not an inline colour, which would outrank the hover utility. It replaces the\n // button reset's `bg-transparent` rather than joining it: same specificity, so the winner\n // would be whichever Tailwind emitted last.\n const muted = tone === 'muted';\n const toneClass = muted ? 'bg-[var(--malachite-card-dimmed-background)]' : '';\n\n const chromeClasses = [\n 'w-full text-left border-0',\n toneClass,\n divider ? 'border-b border-solid border-[var(--malachite-card-border)]' : '',\n hover,\n ]\n .filter(Boolean)\n .join(' ');\n\n const controlClasses = [\n interactive\n ? `appearance-none ${muted ? '' : 'bg-transparent'} p-0 ${FOCUS_RING_INSET}`\n : '',\n interactive && !disabled ? 'cursor-pointer' : '',\n ]\n .filter(Boolean)\n .join(' ');\n\n const classes = [chromeClasses, controlClasses, className].filter(Boolean).join(' ');\n\n const paints = selection ? selection === 'highlight' : role !== 'checkbox';\n\n // A gradient layer so the tint composites over hover, and an inset shadow because\n // `ring-*` silently doesn't paint in a shadow root.\n const selectedStyle: React.CSSProperties =\n selected && paints\n ? {\n backgroundImage: `linear-gradient(${SELECTED_ROW_BACKGROUND}, ${SELECTED_ROW_BACKGROUND})`,\n color: SELECTED_ROW_FOREGROUND,\n boxShadow: `inset 2px 0 0 ${SELECTED_ROW_ACCENT}`,\n ...SELECTED_TEXT_CONTEXT,\n }\n : {};\n\n const rowStyle = { ...selectedStyle, ...style };\n\n const ariaChecked = role === 'radio' || role === 'checkbox' ? Boolean(selected) : undefined;\n // `aria-current`, not `aria-pressed`: a selected row is one of many choices, not a toggle\n // (same call as `Dropdown`'s options). A role that carries its own state uses that instead.\n const ariaCurrent = ariaChecked === undefined && selected ? true : undefined;\n\n // Everything a control would have carried, for the branches that render none: the row\n // element is then the only thing assistive tech sees. `aria-disabled` because there is no\n // control to be natively disabled — and it is inert without a role, like the name.\n const rowFallbackAria = {\n role,\n 'aria-checked': ariaChecked,\n 'aria-current': ariaCurrent,\n 'aria-label': ariaLabel,\n 'aria-labelledby': ariaLabelledBy,\n 'aria-disabled': role && disabled ? true : undefined,\n };\n\n if (isRenderableNode(trailing)) {\n const control =\n href && !disabled ? (\n <a\n href={href}\n onClick={onClick}\n className={`${controlClasses} border-0 flex-1 min-w-0 text-left no-underline text-inherit`}\n role={role}\n aria-checked={ariaChecked}\n aria-current={ariaCurrent}\n aria-label={ariaLabel}\n aria-labelledby={ariaLabelledBy}\n >\n {children}\n </a>\n ) : onClick ? (\n <button\n type=\"button\"\n onClick={onClick}\n disabled={disabled}\n className={`${controlClasses} border-0 flex-1 min-w-0 text-left`}\n role={role}\n aria-checked={ariaChecked}\n aria-current={ariaCurrent}\n aria-label={ariaLabel}\n aria-labelledby={ariaLabelledBy}\n >\n {children}\n </button>\n ) : (\n // The set goes here, not on the wrapper: `radio` and `checkbox` make their\n // descendants presentational, which would bury a control in the trailing slot.\n <div className=\"flex-1 min-w-0\" {...rowFallbackAria}>\n {children}\n </div>\n );\n\n return (\n <div\n className={[chromeClasses, 'flex items-center', className]\n .filter(Boolean)\n .join(' ')}\n style={rowStyle}\n >\n {control}\n <div className=\"shrink-0 flex items-center\">{trailing}</div>\n </div>\n );\n }\n\n if (href && !disabled) {\n return (\n <a\n href={href}\n onClick={onClick}\n className={`${classes} no-underline text-inherit`}\n style={rowStyle}\n role={role}\n aria-checked={ariaChecked}\n aria-current={ariaCurrent}\n aria-label={ariaLabel}\n aria-labelledby={ariaLabelledBy}\n >\n {children}\n </a>\n );\n }\n\n if (onClick) {\n return (\n <button\n type=\"button\"\n onClick={onClick}\n disabled={disabled}\n className={classes}\n style={rowStyle}\n role={role}\n aria-checked={ariaChecked}\n aria-current={ariaCurrent}\n aria-label={ariaLabel}\n aria-labelledby={ariaLabelledBy}\n >\n {children}\n </button>\n );\n }\n\n return (\n <div className={classes} style={rowStyle} {...rowFallbackAria}>\n {children}\n </div>\n );\n};\n\ninterface SurfaceComponent extends React.FC<SurfaceProps> {\n Row: React.FC<SurfaceRowProps>;\n}\n\nconst Surface = (({\n children,\n padding = false,\n overflow = 'hidden',\n role,\n 'aria-label': ariaLabel,\n 'aria-labelledby': ariaLabelledBy,\n className = '',\n style,\n}: SurfaceProps) => {\n // Literal class names (not interpolated) so Tailwind's JIT emits them.\n const overflowClass =\n overflow === 'clip'\n ? 'overflow-clip'\n : overflow === 'visible'\n ? 'overflow-visible'\n : 'overflow-hidden';\n return (\n <div\n className={`w-full rounded-[var(--malachite-radius-xxl,14px)] ${overflowClass} bg-[var(--malachite-card-background)] border border-solid border-[var(--malachite-card-border)] ${padding ? 'py-3 px-4' : ''} ${className}`}\n style={{ boxShadow: TRIPLE_SHADOW, ...FOREGROUND_RESET, ...style }}\n role={role}\n aria-label={ariaLabel}\n aria-labelledby={ariaLabelledBy}\n >\n {children}\n </div>\n );\n}) as SurfaceComponent;\n\nSurface.Row = SurfaceRow;\n\nexport default Surface;\nexport { Surface, SurfaceRow };\n"],"names":["TRIPLE_SHADOW","SurfaceRow","children","divider","disabled","onClick","href","selected","selection","trailing","tone","role","ariaLabel","ariaLabelledBy","className","style","interactive","hover","muted","chromeClasses","controlClasses","FOCUS_RING_INSET","classes","rowStyle","SELECTED_ROW_BACKGROUND","SELECTED_ROW_FOREGROUND","SELECTED_ROW_ACCENT","SELECTED_TEXT_CONTEXT","ariaChecked","ariaCurrent","rowFallbackAria","isRenderableNode","control","jsx","jsxs","Surface","padding","overflow","FOREGROUND_RESET"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"Surface.js","sources":["../../src/components/Surface.tsx"],"sourcesContent":["import React from 'react';\nimport { FOCUS_RING_INSET } from './focusRing';\nimport { isRenderableNode } from './renderableNode';\nimport {\n FOREGROUND_RESET,\n SELECTED_ROW_ACCENT,\n SELECTED_ROW_BACKGROUND,\n SELECTED_ROW_FOREGROUND,\n SELECTED_TEXT_CONTEXT,\n} from './selectedRow';\n\nexport interface SurfaceProps {\n children?: React.ReactNode;\n padding?: boolean;\n /**\n * Content overflow. Defaults to `hidden` so children are clipped to the rounded\n * corners. Use `clip` to keep the rounded clipping WITHOUT creating a scroll\n * container — so a `position: sticky` descendant still pins (unlike `hidden`,\n * which traps it). `visible` disables clipping entirely.\n */\n overflow?: 'hidden' | 'visible' | 'clip';\n role?: React.AriaRole;\n 'aria-label'?: string;\n 'aria-labelledby'?: string;\n className?: string;\n style?: React.CSSProperties;\n}\n\nexport interface SurfaceRowProps {\n children?: React.ReactNode;\n /** Renders a bottom divider — omit on the last row in a Surface. */\n divider?: boolean;\n /** Removes the hover affordance / disables the control (e.g. while loading/busy). */\n disabled?: boolean;\n /**\n * When provided, the whole row becomes clickable: it renders as a `<button>`\n * (or `<a>` when `href` is set), so the entire row is the click target with\n * native focus + Enter/Space — no manual tabIndex/key handlers. For a\n * selectable row, put a presentational `<Radio>`/`<Checkbox>` inside as the\n * visual indicator; do NOT nest an interactive control (invalid + double tab stop).\n */\n onClick?: (event: React.MouseEvent) => void;\n /** Renders the clickable row as an anchor instead of a button. */\n href?: string;\n /** Announces the row as selected — `aria-checked` under a `radio`/`checkbox` role,\n * `aria-current` otherwise — and paints `Table`'s selected treatment unless `selection`\n * says not to. */\n selected?: boolean;\n /** How a `selected` row shows it. `highlight` paints the treatment; `announce` leaves the\n * visual to a control inside the row and only sets the ARIA state. Defaults to `announce`\n * under `role=\"checkbox\"` and `highlight` otherwise. */\n selection?: 'highlight' | 'announce';\n /** Content pinned right, outside the row button, so it may hold its own control. */\n trailing?: React.ReactNode;\n /**\n * Tints the whole row by what it reports. `muted` is a row whose subject is inactive —\n * not disabled: it stays interactive. `success` and `error` are for a row that carries\n * an outcome, where the result belongs to the whole line rather than to one cell in it.\n */\n tone?: 'default' | 'muted' | 'success' | 'error';\n /** ARIA role for a clickable row, e.g. `radio`/`checkbox` (wrap the list in a `radiogroup`). */\n role?: React.AriaRole;\n 'aria-label'?: string;\n 'aria-labelledby'?: string;\n className?: string;\n style?: React.CSSProperties;\n}\n\n/**\n * The outcome tones reuse the tag tokens rather than minting row ones, so a row saying \"this\n * passed\" and a tag saying it are the same green wherever the two sit together.\n */\nconst TONE_CLASSES: Record<NonNullable<SurfaceRowProps['tone']>, string> = {\n default: '',\n muted: 'bg-[var(--malachite-card-dimmed-background)]',\n success: 'bg-[var(--malachite-tag-success-background)]',\n error: 'bg-[var(--malachite-tag-error-background)]',\n};\n\n/**\n * The text an outcome tone carries with it, through the same context channel a selected row\n * uses, so `Typography` inside the row follows the tint instead of staying near-black on it.\n * `color` as well, for row content that is not Typography.\n *\n * Only the outcome tones. `muted` dims its subject rather than recolouring it, and its\n * foreground is the ordinary one.\n */\nconst TONE_FOREGROUNDS: Partial<Record<NonNullable<SurfaceRowProps['tone']>, string>> = {\n success: 'var(--malachite-tag-success-foreground)',\n error: 'var(--malachite-tag-error-foreground)',\n};\n\nconst TRIPLE_SHADOW =\n '0px 0px 4px 0px rgba(0,0,0,0.08), 0px 4px 4px 0px rgba(0,0,0,0.08), 0px 4px 10px 0px rgba(0,0,0,0.05)';\n\n/**\n * A single full-bleed row inside a <Surface>. Owns the row chrome — hover\n * highlight, colour transition and an optional bottom divider — so consumers\n * compose their own content inside without re-implementing design-system styling.\n * Pass `onClick`/`href` to make the entire row clickable (rendered as a real\n * button/anchor, so keyboard navigation works natively).\n */\nconst SurfaceRow: React.FC<SurfaceRowProps> = ({\n children,\n divider = false,\n disabled = false,\n onClick,\n href,\n selected,\n selection,\n trailing,\n tone = 'default',\n role,\n 'aria-label': ariaLabel,\n 'aria-labelledby': ariaLabelledBy,\n className = '',\n style,\n}) => {\n const interactive = Boolean(onClick || href);\n const hover =\n disabled && interactive\n ? ''\n : 'transition-colors hover:bg-[var(--malachite-buttonlist-hover-background)]';\n\n // A class, not an inline colour, which would outrank the hover utility. It replaces the\n // button reset's `bg-transparent` rather than joining it: same specificity, so the winner\n // would be whichever Tailwind emitted last.\n const tinted = tone !== 'default';\n const toneClass = TONE_CLASSES[tone];\n\n const chromeClasses = [\n 'w-full text-left border-0',\n toneClass,\n divider ? 'border-b border-solid border-[var(--malachite-card-border)]' : '',\n hover,\n ]\n .filter(Boolean)\n .join(' ');\n\n const controlClasses = [\n interactive\n ? `appearance-none ${tinted ? '' : 'bg-transparent'} p-0 ${FOCUS_RING_INSET}`\n : '',\n interactive && !disabled ? 'cursor-pointer' : '',\n ]\n .filter(Boolean)\n .join(' ');\n\n const classes = [chromeClasses, controlClasses, className].filter(Boolean).join(' ');\n\n const paints = selection ? selection === 'highlight' : role !== 'checkbox';\n\n // A gradient layer so the tint composites over hover, and an inset shadow because\n // `ring-*` silently doesn't paint in a shadow root.\n const selectedStyle: React.CSSProperties =\n selected && paints\n ? {\n backgroundImage: `linear-gradient(${SELECTED_ROW_BACKGROUND}, ${SELECTED_ROW_BACKGROUND})`,\n color: SELECTED_ROW_FOREGROUND,\n boxShadow: `inset 2px 0 0 ${SELECTED_ROW_ACCENT}`,\n ...SELECTED_TEXT_CONTEXT,\n }\n : {};\n\n const toneForeground = TONE_FOREGROUNDS[tone];\n const toneStyle: React.CSSProperties = toneForeground\n ? ({\n color: toneForeground,\n '--malachite-ctx-text-primary': toneForeground,\n } as React.CSSProperties)\n : {};\n\n // `selected` last of the two: a row can be both, and selection is the state the reader is\n // acting on, so it wins the colour.\n const rowStyle = { ...toneStyle, ...selectedStyle, ...style };\n\n const ariaChecked = role === 'radio' || role === 'checkbox' ? Boolean(selected) : undefined;\n // `aria-current`, not `aria-pressed`: a selected row is one of many choices, not a toggle\n // (same call as `Dropdown`'s options). A role that carries its own state uses that instead.\n const ariaCurrent = ariaChecked === undefined && selected ? true : undefined;\n\n // Everything a control would have carried, for the branches that render none: the row\n // element is then the only thing assistive tech sees. `aria-disabled` because there is no\n // control to be natively disabled — and it is inert without a role, like the name.\n const rowFallbackAria = {\n role,\n 'aria-checked': ariaChecked,\n 'aria-current': ariaCurrent,\n 'aria-label': ariaLabel,\n 'aria-labelledby': ariaLabelledBy,\n 'aria-disabled': role && disabled ? true : undefined,\n };\n\n if (isRenderableNode(trailing)) {\n const control =\n href && !disabled ? (\n <a\n href={href}\n onClick={onClick}\n className={`${controlClasses} border-0 flex-1 min-w-0 text-left no-underline text-inherit`}\n role={role}\n aria-checked={ariaChecked}\n aria-current={ariaCurrent}\n aria-label={ariaLabel}\n aria-labelledby={ariaLabelledBy}\n >\n {children}\n </a>\n ) : onClick ? (\n <button\n type=\"button\"\n onClick={onClick}\n disabled={disabled}\n className={`${controlClasses} border-0 flex-1 min-w-0 text-left`}\n role={role}\n aria-checked={ariaChecked}\n aria-current={ariaCurrent}\n aria-label={ariaLabel}\n aria-labelledby={ariaLabelledBy}\n >\n {children}\n </button>\n ) : (\n // The set goes here, not on the wrapper: `radio` and `checkbox` make their\n // descendants presentational, which would bury a control in the trailing slot.\n <div className=\"flex-1 min-w-0\" {...rowFallbackAria}>\n {children}\n </div>\n );\n\n return (\n <div\n className={[chromeClasses, 'flex items-center', className]\n .filter(Boolean)\n .join(' ')}\n style={rowStyle}\n >\n {control}\n <div className=\"shrink-0 flex items-center\">{trailing}</div>\n </div>\n );\n }\n\n if (href && !disabled) {\n return (\n <a\n href={href}\n onClick={onClick}\n className={`${classes} no-underline text-inherit`}\n style={rowStyle}\n role={role}\n aria-checked={ariaChecked}\n aria-current={ariaCurrent}\n aria-label={ariaLabel}\n aria-labelledby={ariaLabelledBy}\n >\n {children}\n </a>\n );\n }\n\n if (onClick) {\n return (\n <button\n type=\"button\"\n onClick={onClick}\n disabled={disabled}\n className={classes}\n style={rowStyle}\n role={role}\n aria-checked={ariaChecked}\n aria-current={ariaCurrent}\n aria-label={ariaLabel}\n aria-labelledby={ariaLabelledBy}\n >\n {children}\n </button>\n );\n }\n\n return (\n <div className={classes} style={rowStyle} {...rowFallbackAria}>\n {children}\n </div>\n );\n};\n\ninterface SurfaceComponent extends React.FC<SurfaceProps> {\n Row: React.FC<SurfaceRowProps>;\n}\n\nconst Surface = (({\n children,\n padding = false,\n overflow = 'hidden',\n role,\n 'aria-label': ariaLabel,\n 'aria-labelledby': ariaLabelledBy,\n className = '',\n style,\n}: SurfaceProps) => {\n // Literal class names (not interpolated) so Tailwind's JIT emits them.\n const overflowClass =\n overflow === 'clip'\n ? 'overflow-clip'\n : overflow === 'visible'\n ? 'overflow-visible'\n : 'overflow-hidden';\n return (\n <div\n className={`w-full rounded-[var(--malachite-radius-xxl,14px)] ${overflowClass} bg-[var(--malachite-card-background)] border border-solid border-[var(--malachite-card-border)] ${padding ? 'py-3 px-4' : ''} ${className}`}\n style={{ boxShadow: TRIPLE_SHADOW, ...FOREGROUND_RESET, ...style }}\n role={role}\n aria-label={ariaLabel}\n aria-labelledby={ariaLabelledBy}\n >\n {children}\n </div>\n );\n}) as SurfaceComponent;\n\nSurface.Row = SurfaceRow;\n\nexport default Surface;\nexport { Surface, SurfaceRow };\n"],"names":["TONE_CLASSES","TONE_FOREGROUNDS","TRIPLE_SHADOW","SurfaceRow","children","divider","disabled","onClick","href","selected","selection","trailing","tone","role","ariaLabel","ariaLabelledBy","className","style","interactive","hover","tinted","chromeClasses","controlClasses","FOCUS_RING_INSET","classes","selectedStyle","SELECTED_ROW_BACKGROUND","SELECTED_ROW_FOREGROUND","SELECTED_ROW_ACCENT","SELECTED_TEXT_CONTEXT","toneForeground","rowStyle","ariaChecked","ariaCurrent","rowFallbackAria","isRenderableNode","control","jsx","jsxs","Surface","padding","overflow","FOREGROUND_RESET"],"mappings":"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"}
|
package/dist/index.d.ts
CHANGED
|
@@ -15,10 +15,12 @@ declare interface AccordionBaseProps {
|
|
|
15
15
|
className?: string;
|
|
16
16
|
children?: default_2.ReactNode;
|
|
17
17
|
/**
|
|
18
|
-
* Visual style. `default` is the flat list row (
|
|
19
|
-
*
|
|
20
|
-
*
|
|
21
|
-
*
|
|
18
|
+
* Visual style. `default` is the flat list row (row dividers, hover background).
|
|
19
|
+
* `sunken` draws the section as a sunken card (dimmed rounded container, no
|
|
20
|
+
* dividers/hover) — used for stacked detail sections in side panels and detail
|
|
21
|
+
* pages (see `CollapsibleSection`). Both lead with the arrow: it is the affordance
|
|
22
|
+
* that says the section opens, and a reader scanning a stack of them finds it in
|
|
23
|
+
* one column rather than at whatever width each header happens to end.
|
|
22
24
|
*/
|
|
23
25
|
variant?: 'default' | 'sunken';
|
|
24
26
|
/**
|
|
@@ -4001,8 +4003,12 @@ export declare interface SurfaceRowProps {
|
|
|
4001
4003
|
selection?: 'highlight' | 'announce';
|
|
4002
4004
|
/** Content pinned right, outside the row button, so it may hold its own control. */
|
|
4003
4005
|
trailing?: default_2.ReactNode;
|
|
4004
|
-
/**
|
|
4005
|
-
|
|
4006
|
+
/**
|
|
4007
|
+
* Tints the whole row by what it reports. `muted` is a row whose subject is inactive —
|
|
4008
|
+
* not disabled: it stays interactive. `success` and `error` are for a row that carries
|
|
4009
|
+
* an outcome, where the result belongs to the whole line rather than to one cell in it.
|
|
4010
|
+
*/
|
|
4011
|
+
tone?: 'default' | 'muted' | 'success' | 'error';
|
|
4006
4012
|
/** ARIA role for a clickable row, e.g. `radio`/`checkbox` (wrap the list in a `radiogroup`). */
|
|
4007
4013
|
role?: default_2.AriaRole;
|
|
4008
4014
|
'aria-label'?: string;
|
package/package.json
CHANGED