@k2b/ui 0.19.0 → 0.21.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.
- package/dist/browser/actions/Button.js +20 -31
- package/dist/browser/actions/Button.js.map +1 -1
- package/dist/browser/actions/ContextMenu.js +7 -3
- package/dist/browser/actions/ContextMenu.js.map +1 -1
- package/dist/browser/actions/CopyButton.js +10 -8
- package/dist/browser/actions/CopyButton.js.map +1 -1
- package/dist/browser/actions/GestureMenu.js +471 -0
- package/dist/browser/actions/GestureMenu.js.map +1 -0
- package/dist/browser/actions/index.js +1 -0
- package/dist/browser/actions/index.js.map +1 -1
- package/dist/browser/chat/ChatComposer.js +3 -41
- package/dist/browser/chat/ChatComposer.js.map +1 -1
- package/dist/browser/chat/ChatPrimitives.js +9 -3
- package/dist/browser/chat/ChatPrimitives.js.map +1 -1
- package/dist/browser/chat/ChatTimeline.js +19 -1
- package/dist/browser/chat/ChatTimeline.js.map +1 -1
- package/dist/browser/content/Calendar.js +1951 -565
- package/dist/browser/content/Calendar.js.map +1 -1
- package/dist/browser/content/MessageRow.js +2 -2
- package/dist/browser/content/MessageRow.js.map +1 -1
- package/dist/browser/content/calendar-messages.js +83 -0
- package/dist/browser/content/calendar-messages.js.map +1 -0
- package/dist/browser/content/calendar-month-layout.js +108 -0
- package/dist/browser/content/calendar-month-layout.js.map +1 -0
- package/dist/browser/content/index.js.map +1 -1
- package/dist/browser/inputs/DatePicker.js +46 -10
- package/dist/browser/inputs/DatePicker.js.map +1 -1
- package/dist/browser/inputs/TextInput.js +13 -1
- package/dist/browser/inputs/TextInput.js.map +1 -1
- package/dist/browser/inputs/date-picker.js +24 -0
- package/dist/browser/inputs/date-picker.js.map +1 -1
- package/dist/browser/inputs/markdown/actions.js +0 -42
- package/dist/browser/inputs/markdown/actions.js.map +1 -1
- package/dist/browser/intl/messages.js +6 -6
- package/dist/browser/intl/messages.js.map +1 -1
- package/dist/ssr/actions/Button.js +21 -32
- package/dist/ssr/actions/Button.js.map +1 -1
- package/dist/ssr/actions/ContextMenu.js +7 -3
- package/dist/ssr/actions/ContextMenu.js.map +1 -1
- package/dist/ssr/actions/CopyButton.js +9 -7
- package/dist/ssr/actions/CopyButton.js.map +1 -1
- package/dist/ssr/actions/GestureMenu.js +415 -0
- package/dist/ssr/actions/GestureMenu.js.map +1 -0
- package/dist/ssr/actions/index.js +1 -0
- package/dist/ssr/actions/index.js.map +1 -1
- package/dist/ssr/chat/ChatComposer.js +3 -41
- package/dist/ssr/chat/ChatComposer.js.map +1 -1
- package/dist/ssr/chat/ChatPrimitives.js +9 -3
- package/dist/ssr/chat/ChatPrimitives.js.map +1 -1
- package/dist/ssr/chat/ChatTimeline.js +19 -1
- package/dist/ssr/chat/ChatTimeline.js.map +1 -1
- package/dist/ssr/content/Calendar.js +1223 -105
- package/dist/ssr/content/Calendar.js.map +1 -1
- package/dist/ssr/content/MessageRow.js +2 -2
- package/dist/ssr/content/MessageRow.js.map +1 -1
- package/dist/ssr/content/calendar-messages.js +83 -0
- package/dist/ssr/content/calendar-messages.js.map +1 -0
- package/dist/ssr/content/calendar-month-layout.js +108 -0
- package/dist/ssr/content/calendar-month-layout.js.map +1 -0
- package/dist/ssr/content/index.js.map +1 -1
- package/dist/ssr/inputs/DatePicker.js +34 -4
- package/dist/ssr/inputs/DatePicker.js.map +1 -1
- package/dist/ssr/inputs/TextInput.js +13 -1
- package/dist/ssr/inputs/TextInput.js.map +1 -1
- package/dist/ssr/inputs/date-picker.js +24 -0
- package/dist/ssr/inputs/date-picker.js.map +1 -1
- package/dist/ssr/inputs/markdown/actions.js +0 -42
- package/dist/ssr/inputs/markdown/actions.js.map +1 -1
- package/dist/ssr/intl/messages.js +6 -6
- package/dist/ssr/intl/messages.js.map +1 -1
- package/dist/styles.css +1 -1
- package/dist/types/actions/GestureMenu.d.ts +37 -0
- package/dist/types/actions/index.d.ts +2 -0
- package/dist/types/chat/ChatComposer.d.ts +4 -1
- package/dist/types/content/Calendar.d.ts +32 -0
- package/dist/types/content/calendar-messages.d.ts +41 -0
- package/dist/types/content/calendar-month-layout.d.ts +52 -0
- package/dist/types/content/index.d.ts +1 -1
- package/dist/types/inputs/date-picker.d.ts +7 -0
- package/dist/types/inputs/markdown/actions.d.ts +0 -2
- package/dist/types/intl/messages.d.ts +3 -3
- package/package.json +2 -2
|
@@ -1,24 +1,33 @@
|
|
|
1
|
+
import { memo as _$memo } from "solid-js/web";
|
|
1
2
|
import { ssrElement as _$ssrElement } from "solid-js/web";
|
|
2
3
|
import { mergeProps as _$mergeProps } from "solid-js/web";
|
|
3
|
-
import { memo as _$memo } from "solid-js/web";
|
|
4
|
-
import { escape as _$escape } from "solid-js/web";
|
|
5
4
|
import { createComponent as _$createComponent } from "solid-js/web";
|
|
6
5
|
import { ssr as _$ssr } from "solid-js/web";
|
|
7
|
-
|
|
8
|
-
|
|
6
|
+
import { escape as _$escape } from "solid-js/web";
|
|
7
|
+
var _tmpl$ = ["<span class=\"k2b-button__label\">", "</span>"],
|
|
8
|
+
_tmpl$2 = "<span class=\"k2b-button__busy\" aria-hidden=\"true\"><i class=\"ti ti-loader-2 k2b-spin\"></i></span>";
|
|
9
9
|
import { Link } from "@k2b/ssr/nav";
|
|
10
10
|
import { Show, splitProps } from "solid-js";
|
|
11
11
|
import { isServer } from "solid-js/web";
|
|
12
12
|
import { Tooltip, useLabelTooltip } from "../feedback/Tooltip";
|
|
13
13
|
const buttonClass = className => ["k2b-button", className].filter(Boolean).join(" ");
|
|
14
14
|
export function Button(props) {
|
|
15
|
-
const [local, rest] = splitProps(props, ["children", "class", "disabled", "loading", "loadingLabel", "ref", "size", "tooltip", "tooltipDelay", "tooltipPlacement", "type", "variant", "wrap", "align"]);
|
|
15
|
+
const [local, rest] = splitProps(props, ["aria-label", "aria-labelledby", "children", "class", "disabled", "loading", "loadingLabel", "ref", "size", "tooltip", "tooltipDelay", "tooltipPlacement", "type", "variant", "wrap", "align"]);
|
|
16
16
|
let target;
|
|
17
17
|
const setRef = element => {
|
|
18
18
|
target = element;
|
|
19
19
|
if (typeof local.ref === "function") local.ref(element);
|
|
20
20
|
};
|
|
21
|
+
// While busy, a loading label names the button, over any name the caller
|
|
22
|
+
// gave it, because the idle content it would describe is not visible.
|
|
23
|
+
const busyName = () => local.loading && local.loadingLabel || undefined;
|
|
21
24
|
const control = _$ssrElement("button", _$mergeProps(rest, {
|
|
25
|
+
get ["aria-label"]() {
|
|
26
|
+
return busyName() ?? local["aria-label"];
|
|
27
|
+
},
|
|
28
|
+
get ["aria-labelledby"]() {
|
|
29
|
+
return busyName() ? undefined : local["aria-labelledby"];
|
|
30
|
+
},
|
|
22
31
|
get type() {
|
|
23
32
|
return local.type ?? "button";
|
|
24
33
|
},
|
|
@@ -43,24 +52,14 @@ export function Button(props) {
|
|
|
43
52
|
get ["aria-busy"]() {
|
|
44
53
|
return local.loading ? "true" : undefined;
|
|
45
54
|
}
|
|
46
|
-
}), [_$escape(_$createComponent(Show, {
|
|
55
|
+
}), [_$ssr(_tmpl$, _$escape(local.children)), _$escape(_$createComponent(Show, {
|
|
47
56
|
get when() {
|
|
48
57
|
return local.loading;
|
|
49
58
|
},
|
|
50
59
|
get children() {
|
|
51
|
-
return _$ssr(_tmpl$);
|
|
60
|
+
return _$ssr(_tmpl$2);
|
|
52
61
|
}
|
|
53
|
-
})),
|
|
54
|
-
get when() {
|
|
55
|
-
return _$memo(() => !!local.loading)() && local.loadingLabel;
|
|
56
|
-
},
|
|
57
|
-
get fallback() {
|
|
58
|
-
return local.children;
|
|
59
|
-
},
|
|
60
|
-
get children() {
|
|
61
|
-
return local.loadingLabel;
|
|
62
|
-
}
|
|
63
|
-
})))], false);
|
|
62
|
+
}))], false);
|
|
64
63
|
return [control, _$createComponent(Show, {
|
|
65
64
|
get when() {
|
|
66
65
|
return _$memo(() => local.tooltip !== false)() && local.tooltip !== undefined;
|
|
@@ -92,7 +91,7 @@ export function ButtonLink(props) {
|
|
|
92
91
|
if (typeof local.ref === "function") local.ref(element);
|
|
93
92
|
};
|
|
94
93
|
const className = buttonClass(local.class);
|
|
95
|
-
const content = _$ssr(_tmpl
|
|
94
|
+
const content = _$ssr(_tmpl$, _$escape(local.children));
|
|
96
95
|
const tooltip = () => _$createComponent(Show, {
|
|
97
96
|
get when() {
|
|
98
97
|
return _$memo(() => local.tooltip !== false)() && local.tooltip !== undefined;
|
|
@@ -170,12 +169,9 @@ export function ButtonLink(props) {
|
|
|
170
169
|
*/
|
|
171
170
|
export const iconTitle = (title, tooltip) => tooltip === false || tooltip === undefined ? title : isServer && typeof tooltip === "string" ? tooltip : undefined;
|
|
172
171
|
export function IconButton(props) {
|
|
173
|
-
const [local, rest] = splitProps(props, ["children", "class", "label", "
|
|
172
|
+
const [local, rest] = splitProps(props, ["children", "class", "label", "title", "tooltip", "variant"]);
|
|
174
173
|
const tooltip = useLabelTooltip(() => local.tooltip, () => local.title ?? local.label);
|
|
175
174
|
return _$createComponent(Button, _$mergeProps(rest, {
|
|
176
|
-
get loading() {
|
|
177
|
-
return local.loading;
|
|
178
|
-
},
|
|
179
175
|
get variant() {
|
|
180
176
|
return local.variant ?? "ghost";
|
|
181
177
|
},
|
|
@@ -183,7 +179,7 @@ export function IconButton(props) {
|
|
|
183
179
|
return `k2b-icon-button ${local.class ?? ""}`;
|
|
184
180
|
},
|
|
185
181
|
get ["aria-label"]() {
|
|
186
|
-
return
|
|
182
|
+
return local.label;
|
|
187
183
|
},
|
|
188
184
|
get title() {
|
|
189
185
|
return iconTitle(local.title, tooltip());
|
|
@@ -192,14 +188,7 @@ export function IconButton(props) {
|
|
|
192
188
|
return tooltip();
|
|
193
189
|
},
|
|
194
190
|
get children() {
|
|
195
|
-
return
|
|
196
|
-
get when() {
|
|
197
|
-
return !local.loading;
|
|
198
|
-
},
|
|
199
|
-
get children() {
|
|
200
|
-
return local.children;
|
|
201
|
-
}
|
|
202
|
-
});
|
|
191
|
+
return local.children;
|
|
203
192
|
}
|
|
204
193
|
}));
|
|
205
194
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"names":[],"sources":["actions/Button.tsx"],"sourcesContent":["import { Link, type LinkNavigateEvent, type NavigationScrollMode } from \"@k2b/ssr/nav\";\nimport { type JSX, Show, splitProps } from \"solid-js\";\nimport { isServer } from \"solid-js/web\";\nimport { Tooltip, type TooltipPlacement, useLabelTooltip } from \"../feedback/Tooltip\";\n\nexport type ButtonVariant = \"primary\" | \"secondary\" | \"ghost\" | \"text\" | \"subtle\" | \"input\" | \"warning\" | \"danger\" | \"success\" | \"ai\";\nexport type ButtonSize = \"xs\" | \"sm\" | \"md\" | \"lg\";\nexport type ButtonAlign = \"center\" | \"start\";\n\nexport type ButtonProps = JSX.ButtonHTMLAttributes<HTMLButtonElement> & {\n /** Allow a deliberately long or rich label to wrap. Defaults to false. */\n wrap?: boolean;\n /** Start-align the content and let the label fill the button, for full-width rows. Defaults to `\"center\"`. */\n align?: ButtonAlign;\n loading?: boolean;\n loadingLabel?: string;\n size?: ButtonSize;\n variant?: ButtonVariant;\n tooltip?: JSX.Element | false;\n tooltipDelay?: number;\n tooltipPlacement?: TooltipPlacement;\n};\n\nexport type ButtonLinkProps = Omit<JSX.AnchorHTMLAttributes<HTMLAnchorElement>, \"onClick\"> & {\n /** Allow a deliberately long or rich label to wrap. Defaults to false. */\n wrap?: boolean;\n /** Start-align the content and let the label fill the button, for full-width rows. Defaults to `\"center\"`. */\n align?: ButtonAlign;\n navigation?: \"document\" | \"enhanced\";\n onClick?: JSX.EventHandlerUnion<HTMLAnchorElement, MouseEvent>;\n onNavigate?: (event: LinkNavigateEvent) => void | Promise<void>;\n replace?: boolean;\n scroll?: NavigationScrollMode;\n size?: ButtonSize;\n variant?: ButtonVariant;\n tooltip?: JSX.Element | false;\n tooltipDelay?: number;\n tooltipPlacement?: TooltipPlacement;\n};\n\nconst buttonClass = (className?: string): string => [\"k2b-button\", className].filter(Boolean).join(\" \");\n\nexport function Button(props: ButtonProps): JSX.Element {\n const [local, rest] = splitProps(props, [\n \"children\",\n \"class\",\n \"disabled\",\n \"loading\",\n \"loadingLabel\",\n \"ref\",\n \"size\",\n \"tooltip\",\n \"tooltipDelay\",\n \"tooltipPlacement\",\n \"type\",\n \"variant\",\n \"wrap\",\n \"align\",\n ]);\n let target: HTMLButtonElement | undefined;\n const setRef = (element: HTMLButtonElement) => {\n target = element;\n if (typeof local.ref === \"function\") local.ref(element);\n };\n\n const control = (\n <button\n {...rest}\n ref={setRef}\n type={local.type ?? \"button\"}\n class={buttonClass(local.class)}\n data-size={local.size ?? \"md\"}\n data-variant={local.variant ?? \"primary\"}\n data-wrap={local.wrap || undefined}\n data-align={local.align === \"start\" ? \"start\" : undefined}\n disabled={local.disabled || local.loading}\n aria-busy={local.loading ? \"true\" : undefined}\n >\n <Show when={local.loading}>\n <i class=\"ti ti-loader-2 k2b-spin\" aria-hidden=\"true\" />\n </Show>\n <span class=\"k2b-button__label\">\n <Show when={local.loading && local.loadingLabel} fallback={local.children}>\n {local.loadingLabel}\n </Show>\n </span>\n </button>\n );\n\n return (\n <>\n {control}\n <Show when={local.tooltip !== false && local.tooltip !== undefined}>\n <Tooltip\n content={local.tooltip as JSX.Element}\n target={() => target}\n delay={local.tooltipDelay}\n disabled={local.disabled || local.loading}\n placement={local.tooltipPlacement}\n />\n </Show>\n </>\n );\n}\n\nexport function ButtonLink(props: ButtonLinkProps): JSX.Element {\n const [local, rest] = splitProps(props, [\n \"children\",\n \"class\",\n \"href\",\n \"navigation\",\n \"onClick\",\n \"onNavigate\",\n \"replace\",\n \"ref\",\n \"scroll\",\n \"size\",\n \"tooltip\",\n \"tooltipDelay\",\n \"tooltipPlacement\",\n \"variant\",\n \"wrap\",\n \"align\",\n ]);\n let target: HTMLAnchorElement | undefined;\n const setRef = (element: HTMLAnchorElement) => {\n target = element;\n if (typeof local.ref === \"function\") local.ref(element);\n };\n const className = buttonClass(local.class);\n const content = <span class=\"k2b-button__label\">{local.children}</span>;\n\n const tooltip = () => (\n <Show when={local.tooltip !== false && local.tooltip !== undefined}>\n <Tooltip content={local.tooltip as JSX.Element} target={() => target} delay={local.tooltipDelay} placement={local.tooltipPlacement} />\n </Show>\n );\n\n if (local.navigation === \"enhanced\" && local.href && local.onNavigate) {\n return (\n <>\n <Link\n {...rest}\n ref={setRef}\n href={local.href}\n class={className}\n data-size={local.size ?? \"md\"}\n data-variant={local.variant ?? \"primary\"}\n data-wrap={local.wrap || undefined}\n data-align={local.align === \"start\" ? \"start\" : undefined}\n onClick={local.onClick}\n onNavigate={local.onNavigate}\n replace={local.replace}\n scroll={local.scroll}\n >\n {content}\n </Link>\n {tooltip()}\n </>\n );\n }\n\n return (\n <>\n <a\n {...rest}\n ref={setRef}\n href={local.href}\n class={className}\n data-size={local.size ?? \"md\"}\n data-variant={local.variant ?? \"primary\"}\n data-wrap={local.wrap || undefined}\n data-align={local.align === \"start\" ? \"start\" : undefined}\n onClick={local.onClick}\n >\n {content}\n </a>\n {tooltip()}\n </>\n );\n}\n\nexport type IconButtonProps = Omit<ButtonProps, \"children\"> & {\n children: JSX.Element;\n label: string;\n};\n\nexport type IconButtonLinkProps = Omit<ButtonLinkProps, \"children\"> & {\n children: JSX.Element;\n label: string;\n};\n\n/**\n * The native title is only the server-rendered, no-JavaScript hint; the\n * hydrated tooltip removes it. Without a tooltip, an explicit title stays.\n */\nexport const iconTitle = (title: string | undefined, tooltip: JSX.Element | false | undefined): string | undefined =>\n tooltip === false || tooltip === undefined ? title : isServer && typeof tooltip === \"string\" ? tooltip : undefined;\n\nexport function IconButton(props: IconButtonProps): JSX.Element {\n const [local, rest] = splitProps(props, [\"children\", \"class\", \"label\", \"loading\", \"loadingLabel\", \"title\", \"tooltip\", \"variant\"]);\n const tooltip = useLabelTooltip(\n () => local.tooltip,\n () => local.title ?? local.label,\n );\n\n return (\n <Button\n {...rest}\n loading={local.loading}\n variant={local.variant ?? \"ghost\"}\n class={`k2b-icon-button ${local.class ?? \"\"}`}\n aria-label={local.loading ? (local.loadingLabel ?? local.label) : local.label}\n title={iconTitle(local.title, tooltip())}\n tooltip={tooltip()}\n >\n <Show when={!local.loading}>{local.children}</Show>\n </Button>\n );\n}\n\nexport function IconButtonLink(props: IconButtonLinkProps): JSX.Element {\n const [local, rest] = splitProps(props, [\"children\", \"class\", \"label\", \"title\", \"tooltip\", \"variant\"]);\n const tooltip = useLabelTooltip(\n () => local.tooltip,\n () => local.title ?? local.label,\n );\n\n return (\n <ButtonLink\n {...rest}\n variant={local.variant ?? \"ghost\"}\n class={`k2b-icon-button ${local.class ?? \"\"}`}\n aria-label={local.label}\n title={iconTitle(local.title, tooltip())}\n tooltip={tooltip()}\n >\n {local.children}\n </ButtonLink>\n );\n}\n"],"mappings":"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","ignoreList":[]}
|
|
1
|
+
{"version":3,"names":[],"sources":["actions/Button.tsx"],"sourcesContent":["import { Link, type LinkNavigateEvent, type NavigationScrollMode } from \"@k2b/ssr/nav\";\nimport { type JSX, Show, splitProps } from \"solid-js\";\nimport { isServer } from \"solid-js/web\";\nimport { Tooltip, type TooltipPlacement, useLabelTooltip } from \"../feedback/Tooltip\";\n\nexport type ButtonVariant = \"primary\" | \"secondary\" | \"ghost\" | \"text\" | \"subtle\" | \"input\" | \"warning\" | \"danger\" | \"success\" | \"ai\";\nexport type ButtonSize = \"xs\" | \"sm\" | \"md\" | \"lg\";\nexport type ButtonAlign = \"center\" | \"start\";\n\nexport type ButtonProps = JSX.ButtonHTMLAttributes<HTMLButtonElement> & {\n /** Allow a deliberately long or rich label to wrap. Defaults to false. */\n wrap?: boolean;\n /** Start-align the content and let the label fill the button, for full-width rows. Defaults to `\"center\"`. */\n align?: ButtonAlign;\n loading?: boolean;\n loadingLabel?: string;\n size?: ButtonSize;\n variant?: ButtonVariant;\n tooltip?: JSX.Element | false;\n tooltipDelay?: number;\n tooltipPlacement?: TooltipPlacement;\n};\n\nexport type ButtonLinkProps = Omit<JSX.AnchorHTMLAttributes<HTMLAnchorElement>, \"onClick\"> & {\n /** Allow a deliberately long or rich label to wrap. Defaults to false. */\n wrap?: boolean;\n /** Start-align the content and let the label fill the button, for full-width rows. Defaults to `\"center\"`. */\n align?: ButtonAlign;\n navigation?: \"document\" | \"enhanced\";\n onClick?: JSX.EventHandlerUnion<HTMLAnchorElement, MouseEvent>;\n onNavigate?: (event: LinkNavigateEvent) => void | Promise<void>;\n replace?: boolean;\n scroll?: NavigationScrollMode;\n size?: ButtonSize;\n variant?: ButtonVariant;\n tooltip?: JSX.Element | false;\n tooltipDelay?: number;\n tooltipPlacement?: TooltipPlacement;\n};\n\nconst buttonClass = (className?: string): string => [\"k2b-button\", className].filter(Boolean).join(\" \");\n\nexport function Button(props: ButtonProps): JSX.Element {\n const [local, rest] = splitProps(props, [\n \"aria-label\",\n \"aria-labelledby\",\n \"children\",\n \"class\",\n \"disabled\",\n \"loading\",\n \"loadingLabel\",\n \"ref\",\n \"size\",\n \"tooltip\",\n \"tooltipDelay\",\n \"tooltipPlacement\",\n \"type\",\n \"variant\",\n \"wrap\",\n \"align\",\n ]);\n let target: HTMLButtonElement | undefined;\n const setRef = (element: HTMLButtonElement) => {\n target = element;\n if (typeof local.ref === \"function\") local.ref(element);\n };\n // While busy, a loading label names the button, over any name the caller\n // gave it, because the idle content it would describe is not visible.\n const busyName = () => (local.loading && local.loadingLabel) || undefined;\n\n const control = (\n <button\n {...rest}\n aria-label={busyName() ?? local[\"aria-label\"]}\n aria-labelledby={busyName() ? undefined : local[\"aria-labelledby\"]}\n ref={setRef}\n type={local.type ?? \"button\"}\n class={buttonClass(local.class)}\n data-size={local.size ?? \"md\"}\n data-variant={local.variant ?? \"primary\"}\n data-wrap={local.wrap || undefined}\n data-align={local.align === \"start\" ? \"start\" : undefined}\n disabled={local.disabled || local.loading}\n aria-busy={local.loading ? \"true\" : undefined}\n >\n {/* The idle content keeps its place, invisibly, so the button keeps its\n size; the spinner is drawn over it. */}\n <span class=\"k2b-button__label\">{local.children}</span>\n <Show when={local.loading}>\n <span class=\"k2b-button__busy\" aria-hidden=\"true\">\n <i class=\"ti ti-loader-2 k2b-spin\" />\n </span>\n </Show>\n </button>\n );\n\n return (\n <>\n {control}\n <Show when={local.tooltip !== false && local.tooltip !== undefined}>\n <Tooltip\n content={local.tooltip as JSX.Element}\n target={() => target}\n delay={local.tooltipDelay}\n disabled={local.disabled || local.loading}\n placement={local.tooltipPlacement}\n />\n </Show>\n </>\n );\n}\n\nexport function ButtonLink(props: ButtonLinkProps): JSX.Element {\n const [local, rest] = splitProps(props, [\n \"children\",\n \"class\",\n \"href\",\n \"navigation\",\n \"onClick\",\n \"onNavigate\",\n \"replace\",\n \"ref\",\n \"scroll\",\n \"size\",\n \"tooltip\",\n \"tooltipDelay\",\n \"tooltipPlacement\",\n \"variant\",\n \"wrap\",\n \"align\",\n ]);\n let target: HTMLAnchorElement | undefined;\n const setRef = (element: HTMLAnchorElement) => {\n target = element;\n if (typeof local.ref === \"function\") local.ref(element);\n };\n const className = buttonClass(local.class);\n const content = <span class=\"k2b-button__label\">{local.children}</span>;\n\n const tooltip = () => (\n <Show when={local.tooltip !== false && local.tooltip !== undefined}>\n <Tooltip content={local.tooltip as JSX.Element} target={() => target} delay={local.tooltipDelay} placement={local.tooltipPlacement} />\n </Show>\n );\n\n if (local.navigation === \"enhanced\" && local.href && local.onNavigate) {\n return (\n <>\n <Link\n {...rest}\n ref={setRef}\n href={local.href}\n class={className}\n data-size={local.size ?? \"md\"}\n data-variant={local.variant ?? \"primary\"}\n data-wrap={local.wrap || undefined}\n data-align={local.align === \"start\" ? \"start\" : undefined}\n onClick={local.onClick}\n onNavigate={local.onNavigate}\n replace={local.replace}\n scroll={local.scroll}\n >\n {content}\n </Link>\n {tooltip()}\n </>\n );\n }\n\n return (\n <>\n <a\n {...rest}\n ref={setRef}\n href={local.href}\n class={className}\n data-size={local.size ?? \"md\"}\n data-variant={local.variant ?? \"primary\"}\n data-wrap={local.wrap || undefined}\n data-align={local.align === \"start\" ? \"start\" : undefined}\n onClick={local.onClick}\n >\n {content}\n </a>\n {tooltip()}\n </>\n );\n}\n\nexport type IconButtonProps = Omit<ButtonProps, \"children\"> & {\n children: JSX.Element;\n label: string;\n};\n\nexport type IconButtonLinkProps = Omit<ButtonLinkProps, \"children\"> & {\n children: JSX.Element;\n label: string;\n};\n\n/**\n * The native title is only the server-rendered, no-JavaScript hint; the\n * hydrated tooltip removes it. Without a tooltip, an explicit title stays.\n */\nexport const iconTitle = (title: string | undefined, tooltip: JSX.Element | false | undefined): string | undefined =>\n tooltip === false || tooltip === undefined ? title : isServer && typeof tooltip === \"string\" ? tooltip : undefined;\n\nexport function IconButton(props: IconButtonProps): JSX.Element {\n const [local, rest] = splitProps(props, [\"children\", \"class\", \"label\", \"title\", \"tooltip\", \"variant\"]);\n const tooltip = useLabelTooltip(\n () => local.tooltip,\n () => local.title ?? local.label,\n );\n\n return (\n <Button\n {...rest}\n variant={local.variant ?? \"ghost\"}\n class={`k2b-icon-button ${local.class ?? \"\"}`}\n aria-label={local.label}\n title={iconTitle(local.title, tooltip())}\n tooltip={tooltip()}\n >\n {local.children}\n </Button>\n );\n}\n\nexport function IconButtonLink(props: IconButtonLinkProps): JSX.Element {\n const [local, rest] = splitProps(props, [\"children\", \"class\", \"label\", \"title\", \"tooltip\", \"variant\"]);\n const tooltip = useLabelTooltip(\n () => local.tooltip,\n () => local.title ?? local.label,\n );\n\n return (\n <ButtonLink\n {...rest}\n variant={local.variant ?? \"ghost\"}\n class={`k2b-icon-button ${local.class ?? \"\"}`}\n aria-label={local.label}\n title={iconTitle(local.title, tooltip())}\n tooltip={tooltip()}\n >\n {local.children}\n </ButtonLink>\n );\n}\n"],"mappings":"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","ignoreList":[]}
|
|
@@ -23,6 +23,8 @@ export function ContextMenu(props) {
|
|
|
23
23
|
let menu;
|
|
24
24
|
let listenersAttached = false;
|
|
25
25
|
let hostRing = true;
|
|
26
|
+
/** The element inside the host that had focus as the menu opened, such as a row or a day of a composite widget. */
|
|
27
|
+
let opener;
|
|
26
28
|
let anchor = {
|
|
27
29
|
x: 0,
|
|
28
30
|
y: 0
|
|
@@ -94,8 +96,10 @@ export function ContextMenu(props) {
|
|
|
94
96
|
const open = (x, y) => {
|
|
95
97
|
if (props.disabled) return;
|
|
96
98
|
closeActiveContextMenu?.();
|
|
97
|
-
// Focus may sit on a descendant of the host, as in composite widgets.
|
|
98
|
-
|
|
99
|
+
// Focus may sit on a descendant of the host, as in composite widgets; Escape returns it there.
|
|
100
|
+
const focused = document.activeElement;
|
|
101
|
+
opener = focused instanceof HTMLElement && focused !== host && host?.contains(focused) ? focused : undefined;
|
|
102
|
+
hostRing = ringOnReturn(focused);
|
|
99
103
|
anchor = {
|
|
100
104
|
x,
|
|
101
105
|
y
|
|
@@ -139,7 +143,7 @@ export function ContextMenu(props) {
|
|
|
139
143
|
} else if (event.key === "Escape") {
|
|
140
144
|
event.preventDefault();
|
|
141
145
|
close();
|
|
142
|
-
returnFocus(host, hostRing);
|
|
146
|
+
returnFocus(opener?.isConnected ? opener : host, hostRing);
|
|
143
147
|
} else if (event.key === "Tab") {
|
|
144
148
|
close();
|
|
145
149
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"names":[],"sources":["actions/ContextMenu.tsx"],"sourcesContent":["import { createMemo, createSignal, createUniqueId, type JSX, onCleanup, onMount, Show } from \"solid-js\";\nimport { Portal } from \"solid-js/web\";\nimport { returnFocus, ringOnReturn } from \"../internal/focus-return\";\nimport { useUiMessages } from \"../intl/messages\";\nimport { type DropdownItem, DropdownItems, type DropdownPosition, dropdownPosition, focusRowSegment } from \"./Dropdown\";\n\nexport type ContextMenuProps = {\n items: readonly DropdownItem[];\n children: JSX.Element;\n class?: string | ((isOpen: boolean) => string);\n /** Tab order of the context-menu host. Descendant-focused composite widgets use -1. */\n tabIndex?: number;\n disabled?: boolean;\n onClose?: () => void;\n onOpen?: () => void;\n id?: string;\n label?: string;\n};\n\nlet closeActiveContextMenu: (() => void) | undefined;\n\nconst focusableItems = (menu: HTMLElement | undefined): HTMLElement[] =>\n menu\n ? Array.from(\n menu.querySelectorAll<HTMLElement>(\n [\n \"[role='menuitem']:not([aria-disabled='true'])\",\n \"[role='menuitemcheckbox']:not([aria-disabled='true'])\",\n \"[role='menuitemradio']:not([aria-disabled='true'])\",\n ].join(\", \"),\n ),\n )\n : [];\n\n/** Right-click menu with keyboard invocation, one-open-at-a-time behavior, and viewport clamping. */\nexport function ContextMenu(props: ContextMenuProps): JSX.Element {\n const messages = useUiMessages();\n const generatedId = `k2b-context-${createUniqueId()}`;\n const menuId = `${generatedId}-menu`;\n const [position, setPosition] = createSignal<{ x: number; y: number }>();\n let host: HTMLDivElement | undefined;\n let menu: HTMLDivElement | undefined;\n let listenersAttached = false;\n let hostRing = true;\n let anchor = { x: 0, y: 0 };\n let viewport = { width: 0, height: 0 };\n let resizeObserver: ResizeObserver | undefined;\n\n const isOpen = () => position() !== undefined;\n const hostClass = createMemo(() => (typeof props.class === \"function\" ? props.class(isOpen()) : props.class));\n const items = createMemo<readonly DropdownItem[]>(() => props.items);\n\n const dismiss = (event: PointerEvent) => {\n if (menu?.contains(event.target as Node)) return;\n close();\n };\n const closeOnScroll = (event: Event) => {\n const target = event.target;\n if (target instanceof Node && menu?.contains(target)) return;\n close();\n };\n // A resize event may leave the viewport's size unchanged; headless Chromium\n // was seen to fire one on a fresh page. The clamped menu is still in place.\n const closeOnResize = () => {\n if (window.innerWidth === viewport.width && window.innerHeight === viewport.height) return;\n close();\n };\n const attachOpenListeners = () => {\n if (listenersAttached) return;\n listenersAttached = true;\n document.addEventListener(\"pointerdown\", dismiss);\n document.addEventListener(\"keydown\", keyDown);\n window.addEventListener(\"resize\", closeOnResize);\n window.addEventListener(\"scroll\", closeOnScroll, true);\n };\n const detachOpenListeners = () => {\n if (!listenersAttached) return;\n listenersAttached = false;\n document.removeEventListener(\"pointerdown\", dismiss);\n document.removeEventListener(\"keydown\", keyDown);\n window.removeEventListener(\"resize\", closeOnResize);\n window.removeEventListener(\"scroll\", closeOnScroll, true);\n resizeObserver?.disconnect();\n };\n /** Clamps the rendered menu into the viewport beside the point that opened it. */\n const place = () => {\n if (!isOpen() || !menu?.isConnected) return;\n const point = dropdownPosition(\n new DOMRect(anchor.x, anchor.y, 0, 0),\n menu.getBoundingClientRect(),\n \"bottom-right\" satisfies DropdownPosition,\n { width: window.innerWidth, height: window.innerHeight },\n 0,\n );\n setPosition({ x: point.left, y: point.top });\n };\n const close = () => {\n detachOpenListeners();\n if (!isOpen()) return;\n setPosition();\n if (closeActiveContextMenu === close) closeActiveContextMenu = undefined;\n props.onClose?.();\n };\n const focusItem = (index: number) => {\n const items = focusableItems(menu);\n if (items.length === 0) return;\n items[(index + items.length) % items.length]?.focus();\n };\n const open = (x: number, y: number) => {\n if (props.disabled) return;\n closeActiveContextMenu?.();\n // Focus may sit on a descendant of the host, as in composite widgets.\n hostRing = ringOnReturn(document.activeElement);\n anchor = { x, y };\n viewport = { width: window.innerWidth, height: window.innerHeight };\n setPosition({ x, y });\n closeActiveContextMenu = close;\n attachOpenListeners();\n props.onOpen?.();\n // The menu sizes to its entries, so it is measured once rendered and\n // clamped into the viewport before the next paint, and again whenever its\n // entries change its size while it is open.\n queueMicrotask(() => {\n if (!isOpen() || !menu?.isConnected) return;\n place();\n if (typeof ResizeObserver !== \"undefined\") {\n resizeObserver ??= new ResizeObserver(place);\n resizeObserver.observe(menu);\n }\n focusItem(0);\n });\n };\n const keyDown = (event: KeyboardEvent) => {\n if (!isOpen()) return;\n const items = focusableItems(menu);\n const current = items.indexOf(document.activeElement as HTMLElement);\n if (event.key === \"ArrowDown\" || event.key === \"ArrowUp\") {\n event.preventDefault();\n focusItem(current + (event.key === \"ArrowDown\" ? 1 : -1));\n } else if (focusRowSegment(items, event.key)) {\n event.preventDefault();\n } else if (event.key === \"Home\" || event.key === \"End\") {\n event.preventDefault();\n focusItem(event.key === \"Home\" ? 0 : -1);\n } else if (event.key === \"Escape\") {\n event.preventDefault();\n close();\n returnFocus(host, hostRing);\n } else if (event.key === \"Tab\") {\n close();\n }\n };\n\n onMount(() => {\n onCleanup(() => {\n detachOpenListeners();\n if (closeActiveContextMenu === close) closeActiveContextMenu = undefined;\n });\n });\n\n return (\n <>\n {/* biome-ignore lint/a11y/useAriaPropsSupportedByRole: the named group owns the context-menu relationship without pretending arbitrary children form one button. */}\n <div\n ref={host}\n id={props.id ?? generatedId}\n class={hostClass()}\n role=\"group\"\n tabIndex={props.disabled ? undefined : (props.tabIndex ?? 0)}\n aria-label={props.label ?? messages().contextMenu}\n aria-haspopup=\"menu\"\n aria-expanded={isOpen()}\n aria-controls={isOpen() ? menuId : undefined}\n aria-disabled={props.disabled ? \"true\" : undefined}\n onContextMenu={(event) => {\n if (props.disabled) return;\n event.preventDefault();\n event.stopPropagation();\n open(event.clientX, event.clientY);\n }}\n onKeyDown={(event) => {\n if (props.disabled || (event.key !== \"ContextMenu\" && !(event.shiftKey && event.key === \"F10\"))) return;\n event.preventDefault();\n const rect = host?.getBoundingClientRect();\n if (rect) open(rect.left + 12, rect.top + 12);\n }}\n >\n {props.children}\n </div>\n <Show when={position()}>\n {(point) => (\n <Portal>\n <div\n ref={menu}\n id={menuId}\n class=\"k2b-ui k2b-context-menu\"\n role=\"menu\"\n aria-label={props.label ?? messages().contextMenu}\n style={{ left: `${point().x}px`, top: `${point().y}px` }}\n onPointerDown={(event) => event.stopPropagation()}\n >\n <DropdownItems items={items()} close={close} />\n </div>\n </Portal>\n )}\n </Show>\n </>\n );\n}\n\nexport default ContextMenu;\n"],"mappings":"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","ignoreList":[]}
|
|
1
|
+
{"version":3,"names":[],"sources":["actions/ContextMenu.tsx"],"sourcesContent":["import { createMemo, createSignal, createUniqueId, type JSX, onCleanup, onMount, Show } from \"solid-js\";\nimport { Portal } from \"solid-js/web\";\nimport { returnFocus, ringOnReturn } from \"../internal/focus-return\";\nimport { useUiMessages } from \"../intl/messages\";\nimport { type DropdownItem, DropdownItems, type DropdownPosition, dropdownPosition, focusRowSegment } from \"./Dropdown\";\n\nexport type ContextMenuProps = {\n items: readonly DropdownItem[];\n children: JSX.Element;\n class?: string | ((isOpen: boolean) => string);\n /** Tab order of the context-menu host. Descendant-focused composite widgets use -1. */\n tabIndex?: number;\n disabled?: boolean;\n onClose?: () => void;\n onOpen?: () => void;\n id?: string;\n label?: string;\n};\n\nlet closeActiveContextMenu: (() => void) | undefined;\n\nconst focusableItems = (menu: HTMLElement | undefined): HTMLElement[] =>\n menu\n ? Array.from(\n menu.querySelectorAll<HTMLElement>(\n [\n \"[role='menuitem']:not([aria-disabled='true'])\",\n \"[role='menuitemcheckbox']:not([aria-disabled='true'])\",\n \"[role='menuitemradio']:not([aria-disabled='true'])\",\n ].join(\", \"),\n ),\n )\n : [];\n\n/** Right-click menu with keyboard invocation, one-open-at-a-time behavior, and viewport clamping. */\nexport function ContextMenu(props: ContextMenuProps): JSX.Element {\n const messages = useUiMessages();\n const generatedId = `k2b-context-${createUniqueId()}`;\n const menuId = `${generatedId}-menu`;\n const [position, setPosition] = createSignal<{ x: number; y: number }>();\n let host: HTMLDivElement | undefined;\n let menu: HTMLDivElement | undefined;\n let listenersAttached = false;\n let hostRing = true;\n /** The element inside the host that had focus as the menu opened, such as a row or a day of a composite widget. */\n let opener: HTMLElement | undefined;\n let anchor = { x: 0, y: 0 };\n let viewport = { width: 0, height: 0 };\n let resizeObserver: ResizeObserver | undefined;\n\n const isOpen = () => position() !== undefined;\n const hostClass = createMemo(() => (typeof props.class === \"function\" ? props.class(isOpen()) : props.class));\n const items = createMemo<readonly DropdownItem[]>(() => props.items);\n\n const dismiss = (event: PointerEvent) => {\n if (menu?.contains(event.target as Node)) return;\n close();\n };\n const closeOnScroll = (event: Event) => {\n const target = event.target;\n if (target instanceof Node && menu?.contains(target)) return;\n close();\n };\n // A resize event may leave the viewport's size unchanged; headless Chromium\n // was seen to fire one on a fresh page. The clamped menu is still in place.\n const closeOnResize = () => {\n if (window.innerWidth === viewport.width && window.innerHeight === viewport.height) return;\n close();\n };\n const attachOpenListeners = () => {\n if (listenersAttached) return;\n listenersAttached = true;\n document.addEventListener(\"pointerdown\", dismiss);\n document.addEventListener(\"keydown\", keyDown);\n window.addEventListener(\"resize\", closeOnResize);\n window.addEventListener(\"scroll\", closeOnScroll, true);\n };\n const detachOpenListeners = () => {\n if (!listenersAttached) return;\n listenersAttached = false;\n document.removeEventListener(\"pointerdown\", dismiss);\n document.removeEventListener(\"keydown\", keyDown);\n window.removeEventListener(\"resize\", closeOnResize);\n window.removeEventListener(\"scroll\", closeOnScroll, true);\n resizeObserver?.disconnect();\n };\n /** Clamps the rendered menu into the viewport beside the point that opened it. */\n const place = () => {\n if (!isOpen() || !menu?.isConnected) return;\n const point = dropdownPosition(\n new DOMRect(anchor.x, anchor.y, 0, 0),\n menu.getBoundingClientRect(),\n \"bottom-right\" satisfies DropdownPosition,\n { width: window.innerWidth, height: window.innerHeight },\n 0,\n );\n setPosition({ x: point.left, y: point.top });\n };\n const close = () => {\n detachOpenListeners();\n if (!isOpen()) return;\n setPosition();\n if (closeActiveContextMenu === close) closeActiveContextMenu = undefined;\n props.onClose?.();\n };\n const focusItem = (index: number) => {\n const items = focusableItems(menu);\n if (items.length === 0) return;\n items[(index + items.length) % items.length]?.focus();\n };\n const open = (x: number, y: number) => {\n if (props.disabled) return;\n closeActiveContextMenu?.();\n // Focus may sit on a descendant of the host, as in composite widgets; Escape returns it there.\n const focused = document.activeElement;\n opener = focused instanceof HTMLElement && focused !== host && host?.contains(focused) ? focused : undefined;\n hostRing = ringOnReturn(focused);\n anchor = { x, y };\n viewport = { width: window.innerWidth, height: window.innerHeight };\n setPosition({ x, y });\n closeActiveContextMenu = close;\n attachOpenListeners();\n props.onOpen?.();\n // The menu sizes to its entries, so it is measured once rendered and\n // clamped into the viewport before the next paint, and again whenever its\n // entries change its size while it is open.\n queueMicrotask(() => {\n if (!isOpen() || !menu?.isConnected) return;\n place();\n if (typeof ResizeObserver !== \"undefined\") {\n resizeObserver ??= new ResizeObserver(place);\n resizeObserver.observe(menu);\n }\n focusItem(0);\n });\n };\n const keyDown = (event: KeyboardEvent) => {\n if (!isOpen()) return;\n const items = focusableItems(menu);\n const current = items.indexOf(document.activeElement as HTMLElement);\n if (event.key === \"ArrowDown\" || event.key === \"ArrowUp\") {\n event.preventDefault();\n focusItem(current + (event.key === \"ArrowDown\" ? 1 : -1));\n } else if (focusRowSegment(items, event.key)) {\n event.preventDefault();\n } else if (event.key === \"Home\" || event.key === \"End\") {\n event.preventDefault();\n focusItem(event.key === \"Home\" ? 0 : -1);\n } else if (event.key === \"Escape\") {\n event.preventDefault();\n close();\n returnFocus(opener?.isConnected ? opener : host, hostRing);\n } else if (event.key === \"Tab\") {\n close();\n }\n };\n\n onMount(() => {\n onCleanup(() => {\n detachOpenListeners();\n if (closeActiveContextMenu === close) closeActiveContextMenu = undefined;\n });\n });\n\n return (\n <>\n {/* biome-ignore lint/a11y/useAriaPropsSupportedByRole: the named group owns the context-menu relationship without pretending arbitrary children form one button. */}\n <div\n ref={host}\n id={props.id ?? generatedId}\n class={hostClass()}\n role=\"group\"\n tabIndex={props.disabled ? undefined : (props.tabIndex ?? 0)}\n aria-label={props.label ?? messages().contextMenu}\n aria-haspopup=\"menu\"\n aria-expanded={isOpen()}\n aria-controls={isOpen() ? menuId : undefined}\n aria-disabled={props.disabled ? \"true\" : undefined}\n onContextMenu={(event) => {\n if (props.disabled) return;\n event.preventDefault();\n event.stopPropagation();\n open(event.clientX, event.clientY);\n }}\n onKeyDown={(event) => {\n if (props.disabled || (event.key !== \"ContextMenu\" && !(event.shiftKey && event.key === \"F10\"))) return;\n event.preventDefault();\n const rect = host?.getBoundingClientRect();\n if (rect) open(rect.left + 12, rect.top + 12);\n }}\n >\n {props.children}\n </div>\n <Show when={position()}>\n {(point) => (\n <Portal>\n <div\n ref={menu}\n id={menuId}\n class=\"k2b-ui k2b-context-menu\"\n role=\"menu\"\n aria-label={props.label ?? messages().contextMenu}\n style={{ left: `${point().x}px`, top: `${point().y}px` }}\n onPointerDown={(event) => event.stopPropagation()}\n >\n <DropdownItems items={items()} close={close} />\n </div>\n </Portal>\n )}\n </Show>\n </>\n );\n}\n\nexport default ContextMenu;\n"],"mappings":"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","ignoreList":[]}
|
|
@@ -45,11 +45,16 @@ export function CopyButton(props) {
|
|
|
45
45
|
const visibleLabel = () => copied() ? local.copiedLabel ?? messages().copied : local.label ?? messages().copy;
|
|
46
46
|
const icon = () => copied() ? "ti ti-check" : "ti ti-copy";
|
|
47
47
|
const iconOnly = () => local.iconOnly ?? local.label === undefined;
|
|
48
|
-
const buttonLabel = () => local.loading && local.loadingLabel ? local.loadingLabel : visibleLabel();
|
|
49
48
|
const button = tooltip => _$createComponent(Button, _$mergeProps(rest, {
|
|
50
49
|
tooltip: tooltip,
|
|
51
50
|
get disabled() {
|
|
52
|
-
return local.disabled
|
|
51
|
+
return local.disabled;
|
|
52
|
+
},
|
|
53
|
+
get loading() {
|
|
54
|
+
return local.loading;
|
|
55
|
+
},
|
|
56
|
+
get loadingLabel() {
|
|
57
|
+
return local.loadingLabel;
|
|
53
58
|
},
|
|
54
59
|
get type() {
|
|
55
60
|
return local.type ?? "button";
|
|
@@ -63,15 +68,12 @@ export function CopyButton(props) {
|
|
|
63
68
|
get variant() {
|
|
64
69
|
return local.variant ?? "ghost";
|
|
65
70
|
},
|
|
66
|
-
get ["aria-busy"]() {
|
|
67
|
-
return local.loading ? "true" : undefined;
|
|
68
|
-
},
|
|
69
71
|
get ["aria-label"]() {
|
|
70
|
-
return _$memo(() => !!iconOnly())() ?
|
|
72
|
+
return _$memo(() => !!iconOnly())() ? visibleLabel() : undefined;
|
|
71
73
|
},
|
|
72
74
|
onClick: copy,
|
|
73
75
|
get children() {
|
|
74
|
-
return [_$ssr(_tmpl$, _$ssrAttribute("class",
|
|
76
|
+
return [_$ssr(_tmpl$, _$ssrAttribute("class", _$escape(icon(), true), false)), !iconOnly() && _$ssr(_tmpl$2, _$escape(visibleLabel()))];
|
|
75
77
|
}
|
|
76
78
|
}));
|
|
77
79
|
return iconOnly() ? [button(visibleLabel()), _$ssr(_tmpl$3, copied() ? _$escape(visibleLabel()) : "")] : button(undefined);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"names":[],"sources":["actions/CopyButton.tsx"],"sourcesContent":["import { copyToClipboard } from \"@k2b/stdlib/browser\";\nimport { createSignal, type JSX, onCleanup, splitProps } from \"solid-js\";\nimport { useUiMessages } from \"../intl/messages\";\nimport { Button, type ButtonProps } from \"./Button\";\n\nexport type CopyButtonValue = { text: string; value?: string } | { text?: string; value: string };\n\nexport type CopyButtonProps = CopyButtonValue &\n Omit<ButtonProps, \"children\" | \"onClick\"> & {\n label?: string;\n copiedLabel?: string;\n iconOnly?: boolean;\n onCopied?: () => void;\n onCopyError?: (error: unknown) => void;\n resetAfter?: number;\n };\n\nexport async function copyText(\n text: string,\n onCopyError?: (error: unknown) => void,\n copy: (text: string) => Promise<void> = copyToClipboard,\n): Promise<void> {\n try {\n await copy(text);\n } catch (error) {\n onCopyError?.(error);\n throw error;\n }\n}\n\nexport function CopyButton(props: CopyButtonProps): JSX.Element {\n const messages = useUiMessages();\n const [local, rest] = splitProps(props, [\n \"class\",\n \"copiedLabel\",\n \"disabled\",\n \"iconOnly\",\n \"label\",\n \"loading\",\n \"loadingLabel\",\n \"onCopied\",\n \"onCopyError\",\n \"resetAfter\",\n \"size\",\n \"text\",\n \"type\",\n \"value\",\n \"variant\",\n ]);\n const [copied, setCopied] = createSignal(false);\n let resetTimer: ReturnType<typeof setTimeout> | undefined;\n\n onCleanup(() => {\n if (resetTimer) clearTimeout(resetTimer);\n });\n\n const copy = async () => {\n // `copyText` reports through `onCopyError` and then rethrows so direct\n // callers can await it. The click handler is not awaited by anyone, so it\n // has to absorb the rejection here — otherwise a denied clipboard\n // permission surfaces as an unhandled rejection despite `onCopyError`.\n try {\n await copyText(local.text ?? local.value ?? \"\", local.onCopyError);\n } catch {\n return;\n }\n setCopied(true);\n local.onCopied?.();\n if (resetTimer) clearTimeout(resetTimer);\n resetTimer = setTimeout(() => setCopied(false), local.resetAfter ?? 2000);\n };\n\n const visibleLabel = () => (copied() ? (local.copiedLabel ?? messages().copied) : (local.label ?? messages().copy));\n const icon = () => (copied() ? \"ti ti-check\" : \"ti ti-copy\");\n const iconOnly = () => local.iconOnly ?? local.label === undefined;\n const
|
|
1
|
+
{"version":3,"names":[],"sources":["actions/CopyButton.tsx"],"sourcesContent":["import { copyToClipboard } from \"@k2b/stdlib/browser\";\nimport { createSignal, type JSX, onCleanup, splitProps } from \"solid-js\";\nimport { useUiMessages } from \"../intl/messages\";\nimport { Button, type ButtonProps } from \"./Button\";\n\nexport type CopyButtonValue = { text: string; value?: string } | { text?: string; value: string };\n\nexport type CopyButtonProps = CopyButtonValue &\n Omit<ButtonProps, \"children\" | \"onClick\"> & {\n label?: string;\n copiedLabel?: string;\n iconOnly?: boolean;\n onCopied?: () => void;\n onCopyError?: (error: unknown) => void;\n resetAfter?: number;\n };\n\nexport async function copyText(\n text: string,\n onCopyError?: (error: unknown) => void,\n copy: (text: string) => Promise<void> = copyToClipboard,\n): Promise<void> {\n try {\n await copy(text);\n } catch (error) {\n onCopyError?.(error);\n throw error;\n }\n}\n\nexport function CopyButton(props: CopyButtonProps): JSX.Element {\n const messages = useUiMessages();\n const [local, rest] = splitProps(props, [\n \"class\",\n \"copiedLabel\",\n \"disabled\",\n \"iconOnly\",\n \"label\",\n \"loading\",\n \"loadingLabel\",\n \"onCopied\",\n \"onCopyError\",\n \"resetAfter\",\n \"size\",\n \"text\",\n \"type\",\n \"value\",\n \"variant\",\n ]);\n const [copied, setCopied] = createSignal(false);\n let resetTimer: ReturnType<typeof setTimeout> | undefined;\n\n onCleanup(() => {\n if (resetTimer) clearTimeout(resetTimer);\n });\n\n const copy = async () => {\n // `copyText` reports through `onCopyError` and then rethrows so direct\n // callers can await it. The click handler is not awaited by anyone, so it\n // has to absorb the rejection here — otherwise a denied clipboard\n // permission surfaces as an unhandled rejection despite `onCopyError`.\n try {\n await copyText(local.text ?? local.value ?? \"\", local.onCopyError);\n } catch {\n return;\n }\n setCopied(true);\n local.onCopied?.();\n if (resetTimer) clearTimeout(resetTimer);\n resetTimer = setTimeout(() => setCopied(false), local.resetAfter ?? 2000);\n };\n\n const visibleLabel = () => (copied() ? (local.copiedLabel ?? messages().copied) : (local.label ?? messages().copy));\n const icon = () => (copied() ? \"ti ti-check\" : \"ti ti-copy\");\n const iconOnly = () => local.iconOnly ?? local.label === undefined;\n const button = (tooltip?: string) => (\n <Button\n {...rest}\n tooltip={tooltip}\n disabled={local.disabled}\n loading={local.loading}\n loadingLabel={local.loadingLabel}\n type={local.type ?? \"button\"}\n class={`k2b-copy-button ${local.class ?? \"\"}`}\n size={local.size ?? \"sm\"}\n variant={local.variant ?? \"ghost\"}\n aria-label={iconOnly() ? visibleLabel() : undefined}\n onClick={copy}\n >\n <i class={icon()} aria-hidden=\"true\" />\n {!iconOnly() && <span>{visibleLabel()}</span>}\n </Button>\n );\n\n return iconOnly() ? (\n <>\n {button(visibleLabel())}\n <span class=\"k2b-sr-only\" aria-live=\"polite\">\n {copied() ? visibleLabel() : \"\"}\n </span>\n </>\n ) : (\n button(undefined)\n );\n}\n\nexport default CopyButton;\n"],"mappings":"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","ignoreList":[]}
|