@estiva-app/ui 0.16.1 → 0.18.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/README.md +3 -1
- package/dist/Button.d.ts.map +1 -1
- package/dist/Checkbox.d.ts +14 -1
- package/dist/Checkbox.d.ts.map +1 -1
- package/dist/CommandPalette.d.ts.map +1 -1
- package/dist/FilePicker.d.ts +22 -0
- package/dist/FilePicker.d.ts.map +1 -0
- package/dist/Form.d.ts +49 -0
- package/dist/Form.d.ts.map +1 -0
- package/dist/IconButton.d.ts.map +1 -1
- package/dist/SearchInput.d.ts.map +1 -1
- package/dist/TextInput.d.ts.map +1 -1
- package/dist/Textarea.d.ts.map +1 -1
- package/dist/eslint/index.d.ts +1 -1
- package/dist/eslint/index.d.ts.map +1 -1
- package/dist/eslint/index.js +88 -8
- package/dist/eslint/index.js.map +4 -4
- package/dist/eslint/no-raw-element.d.ts +88 -0
- package/dist/eslint/no-raw-element.d.ts.map +1 -0
- package/dist/formBusy.d.ts +15 -0
- package/dist/formBusy.d.ts.map +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +479 -350
- package/dist/index.js.map +4 -4
- package/package.json +1 -1
- package/src/Button.tsx +4 -1
- package/src/Checkbox.mdx +19 -1
- package/src/Checkbox.stories.tsx +12 -0
- package/src/Checkbox.test.tsx +54 -0
- package/src/Checkbox.tsx +40 -3
- package/src/CommandPalette.mdx +6 -2
- package/src/CommandPalette.stories.tsx +6 -3
- package/src/CommandPalette.test.tsx +15 -0
- package/src/CommandPalette.tsx +34 -46
- package/src/FilePicker.mdx +49 -0
- package/src/FilePicker.stories.tsx +61 -0
- package/src/FilePicker.test.tsx +79 -0
- package/src/FilePicker.tsx +40 -0
- package/src/Form.mdx +78 -0
- package/src/Form.stories.tsx +68 -0
- package/src/Form.test.tsx +385 -0
- package/src/Form.tsx +173 -0
- package/src/IconButton.tsx +4 -1
- package/src/SearchInput.mdx +2 -2
- package/src/SearchInput.tsx +3 -1
- package/src/TextInput.mdx +2 -2
- package/src/TextInput.test.tsx +7 -0
- package/src/TextInput.tsx +4 -1
- package/src/Textarea.tsx +4 -1
- package/src/eslint/index.test.ts +24 -15
- package/src/eslint/index.ts +6 -5
- package/src/eslint/{no-raw-button.test.ts → no-raw-element.test.ts} +53 -8
- package/src/eslint/no-raw-element.ts +180 -0
- package/src/formBusy.ts +19 -0
- package/src/index.ts +2 -0
- package/stories/Choosing.mdx +3 -0
- package/dist/eslint/no-raw-button.d.ts +0 -11
- package/dist/eslint/no-raw-button.d.ts.map +0 -1
- package/src/eslint/no-raw-button.ts +0 -39
package/dist/index.js
CHANGED
|
@@ -244,6 +244,13 @@ function Card({
|
|
|
244
244
|
// src/IconButton.tsx
|
|
245
245
|
import { Button as BaseButton } from "@base-ui/react/button";
|
|
246
246
|
|
|
247
|
+
// src/formBusy.ts
|
|
248
|
+
import { createContext, useContext } from "react";
|
|
249
|
+
var FormBusyContext = createContext(false);
|
|
250
|
+
function useFormBusy() {
|
|
251
|
+
return useContext(FormBusyContext);
|
|
252
|
+
}
|
|
253
|
+
|
|
247
254
|
// src/Tooltip.tsx
|
|
248
255
|
import { Tooltip as BaseTooltip } from "@base-ui/react/tooltip";
|
|
249
256
|
|
|
@@ -337,11 +344,12 @@ function IconButton({
|
|
|
337
344
|
type = "button",
|
|
338
345
|
...props
|
|
339
346
|
}) {
|
|
347
|
+
const formBusy = useFormBusy();
|
|
340
348
|
const button = /* @__PURE__ */ jsx8(
|
|
341
349
|
BaseButton,
|
|
342
350
|
{
|
|
343
351
|
type,
|
|
344
|
-
disabled: disabled || !!disabledReason,
|
|
352
|
+
disabled: disabled || !!disabledReason || formBusy,
|
|
345
353
|
focusableWhenDisabled: !!disabledReason,
|
|
346
354
|
className: (state) => cn(
|
|
347
355
|
// `shrink-0` stops a flex parent squashing the button; `self-center`
|
|
@@ -682,11 +690,12 @@ function Button({
|
|
|
682
690
|
...props
|
|
683
691
|
}) {
|
|
684
692
|
const hasLeadingIcon = !!leadingIcon;
|
|
693
|
+
const formBusy = useFormBusy();
|
|
685
694
|
const button = /* @__PURE__ */ jsxs8(
|
|
686
695
|
BaseButton3,
|
|
687
696
|
{
|
|
688
697
|
type,
|
|
689
|
-
disabled: disabled || !!disabledReason,
|
|
698
|
+
disabled: disabled || !!disabledReason || formBusy,
|
|
690
699
|
focusableWhenDisabled: !!disabledReason,
|
|
691
700
|
className: (state) => cn(
|
|
692
701
|
// No `self-center` here, deliberately — `IconButton` needs it and this
|
|
@@ -732,8 +741,9 @@ function Button({
|
|
|
732
741
|
|
|
733
742
|
// src/Checkbox.tsx
|
|
734
743
|
import { Checkbox as BaseCheckbox } from "@base-ui/react/checkbox";
|
|
744
|
+
import { Field as BaseField } from "@base-ui/react/field";
|
|
735
745
|
import { IconCheck } from "@tabler/icons-react";
|
|
736
|
-
import { jsx as jsx14 } from "react/jsx-runtime";
|
|
746
|
+
import { jsx as jsx14, jsxs as jsxs9 } from "react/jsx-runtime";
|
|
737
747
|
function squareClasses(checked, disabled, interactive, className) {
|
|
738
748
|
return cn(
|
|
739
749
|
"inline-flex items-center justify-center size-4 shrink-0 rounded-sm border transition-colors",
|
|
@@ -744,11 +754,19 @@ function squareClasses(checked, disabled, interactive, className) {
|
|
|
744
754
|
);
|
|
745
755
|
}
|
|
746
756
|
var tickClasses = (checked) => cn("flex", !checked && "invisible");
|
|
747
|
-
|
|
757
|
+
var WORDS_CLASSES = "text-body-2 text-text-primary";
|
|
758
|
+
function Checkbox({ checked, onChange, disabled: ownDisabled = false, label, className, ...aria }) {
|
|
759
|
+
const formBusy = useFormBusy();
|
|
760
|
+
const disabled = ownDisabled || formBusy;
|
|
748
761
|
if (!onChange) {
|
|
749
|
-
|
|
762
|
+
const picture = /* @__PURE__ */ jsx14("span", { "aria-hidden": "true", className: squareClasses(checked, disabled, false, className), children: /* @__PURE__ */ jsx14("span", { className: tickClasses(checked), children: /* @__PURE__ */ jsx14(IconCheck, { size: 12, stroke: 3 }) }) });
|
|
763
|
+
if (label === void 0) return picture;
|
|
764
|
+
return /* @__PURE__ */ jsxs9("span", { className: "flex items-center gap-2", children: [
|
|
765
|
+
picture,
|
|
766
|
+
/* @__PURE__ */ jsx14("span", { className: WORDS_CLASSES, children: label })
|
|
767
|
+
] });
|
|
750
768
|
}
|
|
751
|
-
|
|
769
|
+
const box = /* @__PURE__ */ jsx14(
|
|
752
770
|
BaseCheckbox.Root,
|
|
753
771
|
{
|
|
754
772
|
checked,
|
|
@@ -761,10 +779,15 @@ function Checkbox({ checked, onChange, disabled = false, className, ...aria }) {
|
|
|
761
779
|
children: /* @__PURE__ */ jsx14(BaseCheckbox.Indicator, { keepMounted: true, className: (state) => tickClasses(state.checked), children: /* @__PURE__ */ jsx14(IconCheck, { size: 12, stroke: 3 }) })
|
|
762
780
|
}
|
|
763
781
|
);
|
|
782
|
+
if (label === void 0) return box;
|
|
783
|
+
return /* @__PURE__ */ jsx14(BaseField.Root, { disabled, children: /* @__PURE__ */ jsxs9(BaseField.Label, { className: cn("flex items-center gap-2", !disabled && "cursor-pointer"), children: [
|
|
784
|
+
box,
|
|
785
|
+
/* @__PURE__ */ jsx14("span", { className: WORDS_CLASSES, children: label })
|
|
786
|
+
] }) });
|
|
764
787
|
}
|
|
765
788
|
|
|
766
789
|
// src/Chip.tsx
|
|
767
|
-
import { jsx as jsx15, jsxs as
|
|
790
|
+
import { jsx as jsx15, jsxs as jsxs10 } from "react/jsx-runtime";
|
|
768
791
|
var typeStyles = {
|
|
769
792
|
neutral: "bg-bg-inset text-text-primary",
|
|
770
793
|
brand: "bg-accent-muted text-accent-primary signal:border signal:border-accent-outline",
|
|
@@ -774,7 +797,7 @@ var typeStyles = {
|
|
|
774
797
|
error: "bg-error-muted text-error-default signal:border signal:border-error-outline"
|
|
775
798
|
};
|
|
776
799
|
function Chip({ type = "neutral", label, leadingIcon, trailingIcon, className }) {
|
|
777
|
-
return /* @__PURE__ */
|
|
800
|
+
return /* @__PURE__ */ jsxs10("div", { className: cn("inline-flex min-w-0 items-center justify-center gap-1.5 rounded-full max-h-[20px] px-2 py-1", typeStyles[type], className), children: [
|
|
778
801
|
leadingIcon && /* @__PURE__ */ jsx15("span", { className: "flex size-3 shrink-0 items-center justify-center", children: leadingIcon }),
|
|
779
802
|
label && // A chip given a `max-w-*` cuts a long label with an ellipsis instead
|
|
780
803
|
// of growing past it — Ship's project chip on an issue row
|
|
@@ -791,12 +814,12 @@ function Chip({ type = "neutral", label, leadingIcon, trailingIcon, className })
|
|
|
791
814
|
|
|
792
815
|
// src/CommandPalette.tsx
|
|
793
816
|
import {
|
|
794
|
-
createContext as
|
|
795
|
-
useContext as
|
|
817
|
+
createContext as createContext3,
|
|
818
|
+
useContext as useContext3,
|
|
796
819
|
useEffect,
|
|
797
|
-
useLayoutEffect as
|
|
820
|
+
useLayoutEffect as useLayoutEffect3,
|
|
798
821
|
useMemo as useMemo2,
|
|
799
|
-
useRef as
|
|
822
|
+
useRef as useRef3,
|
|
800
823
|
useState as useState3
|
|
801
824
|
} from "react";
|
|
802
825
|
import { Dialog } from "@base-ui/react/dialog";
|
|
@@ -810,7 +833,7 @@ import { Combobox } from "@base-ui/react/combobox";
|
|
|
810
833
|
import { IconX as IconX3 } from "@tabler/icons-react";
|
|
811
834
|
|
|
812
835
|
// src/Menu.tsx
|
|
813
|
-
import { createContext, useContext } from "react";
|
|
836
|
+
import { createContext as createContext2, useContext as useContext2 } from "react";
|
|
814
837
|
import { IconChevronRight } from "@tabler/icons-react";
|
|
815
838
|
import { Menu as BaseMenu } from "@base-ui/react/menu";
|
|
816
839
|
|
|
@@ -836,8 +859,8 @@ function SectionLabel({ children, className }) {
|
|
|
836
859
|
}
|
|
837
860
|
|
|
838
861
|
// src/Menu.tsx
|
|
839
|
-
import { Fragment as Fragment2, jsx as jsx17, jsxs as
|
|
840
|
-
var MenuContext =
|
|
862
|
+
import { Fragment as Fragment2, jsx as jsx17, jsxs as jsxs11 } from "react/jsx-runtime";
|
|
863
|
+
var MenuContext = createContext2(null);
|
|
841
864
|
function MenuPanel({ children, className, ...props }) {
|
|
842
865
|
return /* @__PURE__ */ jsx17(
|
|
843
866
|
"div",
|
|
@@ -869,7 +892,7 @@ var VIEWPORT_PAD2 = 8;
|
|
|
869
892
|
var HOVER_OPEN_DELAY = 0;
|
|
870
893
|
var HOVER_CLOSE_DELAY = 150;
|
|
871
894
|
function Menu({ trigger, align = "left", openOnHover = false, open, onOpenChange, actionsRef, children, className, contentClassName }) {
|
|
872
|
-
return /* @__PURE__ */
|
|
895
|
+
return /* @__PURE__ */ jsxs11(
|
|
873
896
|
BaseMenu.Root,
|
|
874
897
|
{
|
|
875
898
|
open,
|
|
@@ -911,13 +934,13 @@ function Menu({ trigger, align = "left", openOnHover = false, open, onOpenChange
|
|
|
911
934
|
);
|
|
912
935
|
}
|
|
913
936
|
function MenuSub({ label, leading, selected, children, className }) {
|
|
914
|
-
const menu =
|
|
937
|
+
const menu = useContext2(MenuContext);
|
|
915
938
|
if (!isProduction() && menu?.openOnHover) {
|
|
916
939
|
console.error(
|
|
917
940
|
'[@estiva-app/ui] Menu: `openOnHover` and `MenuSub` cannot be used together \u2014 leaving the submenu panel strands the menu open. Open this menu on a press instead. See Menu.mdx, "Hover-opened menus".'
|
|
918
941
|
);
|
|
919
942
|
}
|
|
920
|
-
return /* @__PURE__ */
|
|
943
|
+
return /* @__PURE__ */ jsxs11(BaseMenu.SubmenuRoot, { children: [
|
|
921
944
|
/* @__PURE__ */ jsx17(
|
|
922
945
|
BaseMenu.SubmenuTrigger,
|
|
923
946
|
{
|
|
@@ -974,9 +997,9 @@ function menuItemClassName({ size, selected, className }) {
|
|
|
974
997
|
}
|
|
975
998
|
function MenuItemBody({ label, children, size = "default", description, leading, trailing, hint, shortcut, submenu, destructive, selected }) {
|
|
976
999
|
const edge = trailing ?? (shortcut ? /* @__PURE__ */ jsx17(Kbd, { children: shortcut }) : submenu ? /* @__PURE__ */ jsx17(IconChevronRight, { size: 16, stroke: 1.5, className: "shrink-0 text-text-muted" }) : null);
|
|
977
|
-
return /* @__PURE__ */
|
|
1000
|
+
return /* @__PURE__ */ jsxs11(Fragment2, { children: [
|
|
978
1001
|
leading && /* @__PURE__ */ jsx17("span", { className: "flex shrink-0 items-center", children: leading }),
|
|
979
|
-
children ?? /* @__PURE__ */
|
|
1002
|
+
children ?? /* @__PURE__ */ jsxs11("span", { className: cn("flex min-w-0 flex-1 flex-col", size === "tall" && "gap-[2px]"), children: [
|
|
980
1003
|
/* @__PURE__ */ jsx17("span", { className: cn("truncate text-body-2", destructive ? "text-error-default" : "text-text-primary"), children: label }),
|
|
981
1004
|
description && /* @__PURE__ */ jsx17("span", { className: "truncate text-caption text-text-secondary", children: description })
|
|
982
1005
|
] }),
|
|
@@ -984,7 +1007,7 @@ function MenuItemBody({ label, children, size = "default", description, leading,
|
|
|
984
1007
|
// the wider of the two and never changes, so revealing the hint moves
|
|
985
1008
|
// nothing. No transition here either — the hint switches on the same
|
|
986
1009
|
// :hover / `selected` as the fill, in the same frame.
|
|
987
|
-
/* @__PURE__ */
|
|
1010
|
+
/* @__PURE__ */ jsxs11("span", { className: "grid shrink-0 items-center justify-items-end [&>*]:col-start-1 [&>*]:row-start-1", children: [
|
|
988
1011
|
edge && /* @__PURE__ */ jsx17("span", { className: cn("flex items-center", hint && "group-hover:opacity-0 group-data-[highlighted]:opacity-0", hint && selected && "opacity-0"), children: edge }),
|
|
989
1012
|
hint && /* @__PURE__ */ jsx17("span", { className: cn("flex items-center opacity-0 group-hover:opacity-100 group-data-[highlighted]:opacity-100", selected && "opacity-100"), children: hint })
|
|
990
1013
|
] })
|
|
@@ -1008,7 +1031,7 @@ function MenuItem({ label, children, size = "default", description, leading, tra
|
|
|
1008
1031
|
}
|
|
1009
1032
|
);
|
|
1010
1033
|
const rowClassName = menuItemClassName({ size, selected, className });
|
|
1011
|
-
if (!
|
|
1034
|
+
if (!useContext2(MenuContext)) {
|
|
1012
1035
|
return /* @__PURE__ */ jsx17("button", { type: "button", className: rowClassName, ...props, children: body });
|
|
1013
1036
|
}
|
|
1014
1037
|
return /* @__PURE__ */ jsx17(
|
|
@@ -1023,21 +1046,21 @@ function MenuItem({ label, children, size = "default", description, leading, tra
|
|
|
1023
1046
|
);
|
|
1024
1047
|
}
|
|
1025
1048
|
function EnterHint({ target }) {
|
|
1026
|
-
return /* @__PURE__ */
|
|
1049
|
+
return /* @__PURE__ */ jsxs11("span", { className: "flex shrink-0 items-center gap-1.5 text-text-muted", children: [
|
|
1027
1050
|
/* @__PURE__ */ jsx17(Kbd, { children: "\u21A9 Enter" }),
|
|
1028
1051
|
target && /* @__PURE__ */ jsx17("span", { className: "text-menu signal:font-mono signal:text-small signal:tracking-wider", children: target })
|
|
1029
1052
|
] });
|
|
1030
1053
|
}
|
|
1031
1054
|
function MenuSection({ label, children, className }) {
|
|
1032
|
-
const inMenu =
|
|
1055
|
+
const inMenu = useContext2(MenuContext) !== null;
|
|
1033
1056
|
const heading = /* @__PURE__ */ jsx17("div", { className: cn("flex h-8 items-center px-2", className), children: /* @__PURE__ */ jsx17(SectionLabel, { className: "text-text-secondary", children: label }) });
|
|
1034
1057
|
if (!inMenu) {
|
|
1035
|
-
return /* @__PURE__ */
|
|
1058
|
+
return /* @__PURE__ */ jsxs11("div", { className: "flex flex-col", children: [
|
|
1036
1059
|
heading,
|
|
1037
1060
|
children
|
|
1038
1061
|
] });
|
|
1039
1062
|
}
|
|
1040
|
-
return /* @__PURE__ */
|
|
1063
|
+
return /* @__PURE__ */ jsxs11(BaseMenu.Group, { className: "flex flex-col", children: [
|
|
1041
1064
|
/* @__PURE__ */ jsx17(BaseMenu.GroupLabel, { render: heading }),
|
|
1042
1065
|
children
|
|
1043
1066
|
] });
|
|
@@ -1047,7 +1070,7 @@ function MenuRow({ children, className }) {
|
|
|
1047
1070
|
}
|
|
1048
1071
|
|
|
1049
1072
|
// src/ChipInput.tsx
|
|
1050
|
-
import { jsx as jsx18, jsxs as
|
|
1073
|
+
import { jsx as jsx18, jsxs as jsxs12 } from "react/jsx-runtime";
|
|
1051
1074
|
var CHIP_BOX = (
|
|
1052
1075
|
// Curved, not a pill (Katerina, 2026-09-01): the Avatar keeps its own
|
|
1053
1076
|
// rounded-sm corners — never a forced circle — and the chip's corner
|
|
@@ -1058,7 +1081,7 @@ var CHIP_LABEL = "text-caption font-medium text-text-primary";
|
|
|
1058
1081
|
var CHIP_REMOVE = "size-4 flex items-center justify-center rounded-full hover:bg-bg-hover text-text-secondary";
|
|
1059
1082
|
var chipPadding = (leading, removable) => cn(leading ? "pl-[2px]" : "pl-2", removable ? "pr-1" : "pr-2");
|
|
1060
1083
|
function InputChip({ label, leading, onRemove, removeLabel, truncate, className }) {
|
|
1061
|
-
return /* @__PURE__ */
|
|
1084
|
+
return /* @__PURE__ */ jsxs12("div", { className: cn(CHIP_BOX, chipPadding(!!leading, !!onRemove), className), children: [
|
|
1062
1085
|
leading && /* @__PURE__ */ jsx18("span", { className: "flex shrink-0 items-center", children: leading }),
|
|
1063
1086
|
/* @__PURE__ */ jsx18("span", { className: cn(CHIP_LABEL, truncate && "min-w-0 truncate"), children: label }),
|
|
1064
1087
|
onRemove && /* @__PURE__ */ jsx18(
|
|
@@ -1124,7 +1147,7 @@ function ChipInput({
|
|
|
1124
1147
|
setQuery("");
|
|
1125
1148
|
}
|
|
1126
1149
|
}
|
|
1127
|
-
return /* @__PURE__ */
|
|
1150
|
+
return /* @__PURE__ */ jsxs12(
|
|
1128
1151
|
Combobox.Root,
|
|
1129
1152
|
{
|
|
1130
1153
|
multiple: true,
|
|
@@ -1141,8 +1164,8 @@ function ChipInput({
|
|
|
1141
1164
|
filter,
|
|
1142
1165
|
itemToStringLabel: (option) => option.label,
|
|
1143
1166
|
children: [
|
|
1144
|
-
/* @__PURE__ */
|
|
1145
|
-
value.map((option) => /* @__PURE__ */
|
|
1167
|
+
/* @__PURE__ */ jsxs12(Combobox.Chips, { ref: setBox, className: "bg-bg-inset border border-border-default hover:border-border-strong focus-within:border-border-focus focus-within:hover:border-border-focus rounded-lg px-3 py-1.5 flex flex-wrap items-center gap-1.5 transition-colors min-h-[38px] cursor-text signal:transition-shadow signal:focus-within:shadow-focus-ring", children: [
|
|
1168
|
+
value.map((option) => /* @__PURE__ */ jsxs12(Combobox.Chip, { className: cn(CHIP_BOX, chipPadding(!!chipLeading, true)), children: [
|
|
1146
1169
|
chipLeading && /* @__PURE__ */ jsx18("span", { className: "flex shrink-0 items-center", children: chipLeading(option) }),
|
|
1147
1170
|
/* @__PURE__ */ jsx18("span", { className: CHIP_LABEL, children: option.label }),
|
|
1148
1171
|
/* @__PURE__ */ jsx18(Combobox.ChipRemove, { className: CHIP_REMOVE, "aria-label": `Remove ${option.label}`, children: /* @__PURE__ */ jsx18(IconX3, { size: 10, stroke: 1.5 }) })
|
|
@@ -1193,12 +1216,12 @@ var VIEWPORT_PAD3 = 8;
|
|
|
1193
1216
|
|
|
1194
1217
|
// src/EmptyState.tsx
|
|
1195
1218
|
import { IconMessage2 } from "@tabler/icons-react";
|
|
1196
|
-
import { jsx as jsx19, jsxs as
|
|
1219
|
+
import { jsx as jsx19, jsxs as jsxs13 } from "react/jsx-runtime";
|
|
1197
1220
|
function EmptyState({ icon, message, scope = "page", className }) {
|
|
1198
1221
|
if (scope === "section") {
|
|
1199
1222
|
return /* @__PURE__ */ jsx19("p", { className: cn("text-caption text-text-muted", className), children: message });
|
|
1200
1223
|
}
|
|
1201
|
-
return /* @__PURE__ */
|
|
1224
|
+
return /* @__PURE__ */ jsxs13("div", { className: cn("flex flex-1 flex-col items-center justify-center gap-2", className), children: [
|
|
1202
1225
|
/* @__PURE__ */ jsx19("span", { className: "text-text-secondary", children: icon ?? /* @__PURE__ */ jsx19(IconMessage2, { size: 16, stroke: 1.5 }) }),
|
|
1203
1226
|
/* @__PURE__ */ jsx19("p", { className: "text-body-2 text-text-secondary text-center", children: message })
|
|
1204
1227
|
] });
|
|
@@ -1206,8 +1229,8 @@ function EmptyState({ icon, message, scope = "page", className }) {
|
|
|
1206
1229
|
|
|
1207
1230
|
// src/Field.tsx
|
|
1208
1231
|
import { cloneElement, isValidElement } from "react";
|
|
1209
|
-
import { Field as
|
|
1210
|
-
import { jsx as jsx20, jsxs as
|
|
1232
|
+
import { Field as BaseField2 } from "@base-ui/react/field";
|
|
1233
|
+
import { jsx as jsx20, jsxs as jsxs14 } from "react/jsx-runtime";
|
|
1211
1234
|
var LINE_STYLES = {
|
|
1212
1235
|
helper: "text-caption text-text-muted",
|
|
1213
1236
|
warning: "text-caption text-warning-default",
|
|
@@ -1217,14 +1240,14 @@ function Field({ label, required = false, helper, error, children }) {
|
|
|
1217
1240
|
const control = required && isValidElement(children) ? cloneElement(children, {
|
|
1218
1241
|
"aria-required": children.props["aria-required"] ?? true
|
|
1219
1242
|
}) : children;
|
|
1220
|
-
return /* @__PURE__ */
|
|
1221
|
-
/* @__PURE__ */
|
|
1243
|
+
return /* @__PURE__ */ jsxs14(BaseField2.Root, { invalid: !!error, className: "flex flex-col gap-2", children: [
|
|
1244
|
+
/* @__PURE__ */ jsxs14(BaseField2.Label, { className: cn("text-input-label text-text-primary", required && "flex items-center"), children: [
|
|
1222
1245
|
label,
|
|
1223
1246
|
required && /* @__PURE__ */ jsx20("span", { "aria-hidden": "true", className: "text-error-default ml-0.5", children: "*" })
|
|
1224
1247
|
] }),
|
|
1225
|
-
/* @__PURE__ */
|
|
1248
|
+
/* @__PURE__ */ jsxs14("div", { className: "flex flex-col gap-1.5", children: [
|
|
1226
1249
|
control,
|
|
1227
|
-
error != null ? /* @__PURE__ */ jsx20(
|
|
1250
|
+
error != null ? /* @__PURE__ */ jsx20(BaseField2.Error, { match: true, className: LINE_STYLES.error, children: error }) : helper != null ? /* @__PURE__ */ jsx20(BaseField2.Description, { className: LINE_STYLES.helper, children: helper }) : null
|
|
1228
1251
|
] })
|
|
1229
1252
|
] });
|
|
1230
1253
|
}
|
|
@@ -1232,39 +1255,129 @@ function FieldLine({ tone = "helper", children, className }) {
|
|
|
1232
1255
|
return /* @__PURE__ */ jsx20("p", { role: tone === "error" ? "alert" : "status", className: cn(LINE_STYLES[tone], className), children });
|
|
1233
1256
|
}
|
|
1234
1257
|
|
|
1258
|
+
// src/Form.tsx
|
|
1259
|
+
import { useLayoutEffect as useLayoutEffect2, useRef as useRef2 } from "react";
|
|
1260
|
+
import { Fieldset } from "@base-ui/react/fieldset";
|
|
1261
|
+
import { Form as BaseForm } from "@base-ui/react/form";
|
|
1262
|
+
import { jsx as jsx21 } from "react/jsx-runtime";
|
|
1263
|
+
var CONTROL = 'input:not([type="hidden"]), textarea, select, button, [role="checkbox"], [role="combobox"], [tabindex]:not([tabindex="-1"])';
|
|
1264
|
+
var ONE_LINE = /* @__PURE__ */ new Set(["", "text", "search", "email", "url", "tel", "password", "number"]);
|
|
1265
|
+
function usable(element) {
|
|
1266
|
+
return element instanceof HTMLElement && element.isConnected && element.matches(CONTROL) && !element.matches(":disabled") && element.getAttribute("aria-disabled") !== "true";
|
|
1267
|
+
}
|
|
1268
|
+
function firstInvalid(form) {
|
|
1269
|
+
for (const marked of form.querySelectorAll("[data-invalid]")) {
|
|
1270
|
+
if (usable(marked)) return marked;
|
|
1271
|
+
const inside = Array.from(marked.querySelectorAll(CONTROL)).find(usable);
|
|
1272
|
+
if (inside) return inside;
|
|
1273
|
+
}
|
|
1274
|
+
return void 0;
|
|
1275
|
+
}
|
|
1276
|
+
function Form({ onSubmit, busy: ownBusy = false, enterSends = true, className, children, ...props }) {
|
|
1277
|
+
const outerBusy = useFormBusy();
|
|
1278
|
+
const busy = ownBusy || outerBusy;
|
|
1279
|
+
const form = useRef2(null);
|
|
1280
|
+
const busyNow = useRef2(busy);
|
|
1281
|
+
busyNow.current = busy;
|
|
1282
|
+
const sender = useRef2(null);
|
|
1283
|
+
const holding = useRef2(false);
|
|
1284
|
+
const lastInside = useRef2(null);
|
|
1285
|
+
useLayoutEffect2(() => {
|
|
1286
|
+
const element = form.current;
|
|
1287
|
+
if (!element) return;
|
|
1288
|
+
if (busy) {
|
|
1289
|
+
const active = document.activeElement;
|
|
1290
|
+
const from = active && active !== element && element.contains(active) ? active : !active || active === document.body ? lastInside.current : null;
|
|
1291
|
+
if (from?.isConnected) {
|
|
1292
|
+
sender.current = from;
|
|
1293
|
+
holding.current = true;
|
|
1294
|
+
element.focus();
|
|
1295
|
+
}
|
|
1296
|
+
return;
|
|
1297
|
+
}
|
|
1298
|
+
if (!holding.current) return;
|
|
1299
|
+
holding.current = false;
|
|
1300
|
+
if (document.activeElement !== element && document.activeElement !== document.body) return;
|
|
1301
|
+
const target = firstInvalid(element) ?? (usable(sender.current) ? sender.current : Array.from(element.querySelectorAll(CONTROL)).find(usable));
|
|
1302
|
+
sender.current = null;
|
|
1303
|
+
target?.focus();
|
|
1304
|
+
}, [busy]);
|
|
1305
|
+
const onKeyDown = (event) => {
|
|
1306
|
+
props.onKeyDown?.(event);
|
|
1307
|
+
if (event.key !== "Enter" || event.defaultPrevented || event.nativeEvent.isComposing) return;
|
|
1308
|
+
const target = event.target;
|
|
1309
|
+
const inInput = target instanceof HTMLInputElement;
|
|
1310
|
+
if (inInput) event.preventDefault();
|
|
1311
|
+
if (event.altKey || event.shiftKey) return;
|
|
1312
|
+
if (event.ctrlKey || event.metaKey) {
|
|
1313
|
+
event.preventDefault();
|
|
1314
|
+
form.current?.requestSubmit();
|
|
1315
|
+
return;
|
|
1316
|
+
}
|
|
1317
|
+
if (!inInput) return;
|
|
1318
|
+
const oneLine = ONE_LINE.has((target.getAttribute("type") ?? "").toLowerCase()) && target.getAttribute("role") !== "combobox";
|
|
1319
|
+
if (enterSends && oneLine) form.current?.requestSubmit();
|
|
1320
|
+
};
|
|
1321
|
+
return /* @__PURE__ */ jsx21(
|
|
1322
|
+
BaseForm,
|
|
1323
|
+
{
|
|
1324
|
+
ref: form,
|
|
1325
|
+
...props,
|
|
1326
|
+
tabIndex: busy ? -1 : props.tabIndex,
|
|
1327
|
+
className: cn("outline-none", className),
|
|
1328
|
+
onFocus: (event) => {
|
|
1329
|
+
if (event.target !== form.current) lastInside.current = event.target;
|
|
1330
|
+
props.onFocus?.(event);
|
|
1331
|
+
},
|
|
1332
|
+
onBlur: (event) => {
|
|
1333
|
+
const next = event.relatedTarget;
|
|
1334
|
+
if (next && !form.current?.contains(next)) lastInside.current = null;
|
|
1335
|
+
props.onBlur?.(event);
|
|
1336
|
+
},
|
|
1337
|
+
onKeyDown,
|
|
1338
|
+
onSubmit: (event) => {
|
|
1339
|
+
event.preventDefault();
|
|
1340
|
+
if (busyNow.current) return;
|
|
1341
|
+
void onSubmit();
|
|
1342
|
+
},
|
|
1343
|
+
children: /* @__PURE__ */ jsx21(Fieldset.Root, { disabled: busy, className: "contents", children: /* @__PURE__ */ jsx21(FormBusyContext.Provider, { value: busy, children }) })
|
|
1344
|
+
}
|
|
1345
|
+
);
|
|
1346
|
+
}
|
|
1347
|
+
|
|
1235
1348
|
// src/Skeleton.tsx
|
|
1236
|
-
import { jsx as
|
|
1349
|
+
import { jsx as jsx22, jsxs as jsxs15 } from "react/jsx-runtime";
|
|
1237
1350
|
function SkeletonBar({ className, style }) {
|
|
1238
|
-
return /* @__PURE__ */
|
|
1351
|
+
return /* @__PURE__ */ jsx22("div", { className: cn("bg-bg-inset rounded-sm animate-pulse", className), style });
|
|
1239
1352
|
}
|
|
1240
1353
|
var ROW_BAR_WIDTHS = [150, 100, 170, 120, 90, 140, 110, 160];
|
|
1241
1354
|
function SkeletonRow({ barWidth = 130 }) {
|
|
1242
|
-
return /* @__PURE__ */
|
|
1243
|
-
/* @__PURE__ */
|
|
1244
|
-
/* @__PURE__ */
|
|
1355
|
+
return /* @__PURE__ */ jsxs15("div", { className: "flex items-center gap-2 px-2 h-[32px] rounded-lg", children: [
|
|
1356
|
+
/* @__PURE__ */ jsx22(SkeletonBar, { className: "w-4 h-4 shrink-0" }),
|
|
1357
|
+
/* @__PURE__ */ jsx22(SkeletonBar, { className: "h-3.5", style: { width: barWidth } })
|
|
1245
1358
|
] });
|
|
1246
1359
|
}
|
|
1247
1360
|
function SkeletonList({ rows = 8, className }) {
|
|
1248
|
-
return /* @__PURE__ */
|
|
1361
|
+
return /* @__PURE__ */ jsx22("div", { className: cn("flex flex-col gap-0.5 animate-skeleton-in", className), "aria-hidden": true, children: Array.from({ length: rows }, (_, i) => /* @__PURE__ */ jsx22(SkeletonRow, { barWidth: ROW_BAR_WIDTHS[i % ROW_BAR_WIDTHS.length] }, i)) });
|
|
1249
1362
|
}
|
|
1250
1363
|
|
|
1251
1364
|
// src/CommandPalette.tsx
|
|
1252
|
-
import { Fragment as Fragment3, jsx as
|
|
1253
|
-
var PaletteContext =
|
|
1365
|
+
import { Fragment as Fragment3, jsx as jsx23, jsxs as jsxs16 } from "react/jsx-runtime";
|
|
1366
|
+
var PaletteContext = createContext3(null);
|
|
1254
1367
|
function usePalette(part) {
|
|
1255
|
-
const palette =
|
|
1368
|
+
const palette = useContext3(PaletteContext);
|
|
1256
1369
|
if (!palette) throw new Error(`[@estiva-app/ui] ${part} must be inside a CommandPalette.`);
|
|
1257
1370
|
return palette;
|
|
1258
1371
|
}
|
|
1259
|
-
var
|
|
1372
|
+
var CONTROL2 = 'input:not([type="hidden"]):not([tabindex="-1"]), textarea, button:not([tabindex="-1"]), [role="combobox"]';
|
|
1260
1373
|
function firstControl(popup) {
|
|
1261
1374
|
if (!popup) return null;
|
|
1262
1375
|
const field = popup.querySelector("[data-command-palette-field]");
|
|
1263
1376
|
if (field) return field;
|
|
1264
|
-
return popup.querySelector("[data-command-palette-fields]")?.querySelector(
|
|
1377
|
+
return popup.querySelector("[data-command-palette-fields]")?.querySelector(CONTROL2) ?? null;
|
|
1265
1378
|
}
|
|
1266
1379
|
function CommandPalette({ open, onOpenChange, label, where, modKey = "Ctrl", children }) {
|
|
1267
|
-
const popupRef =
|
|
1380
|
+
const popupRef = useRef3(null);
|
|
1268
1381
|
useEffect(() => {
|
|
1269
1382
|
if (!open) return;
|
|
1270
1383
|
const frame = requestAnimationFrame(() => {
|
|
@@ -1275,9 +1388,9 @@ function CommandPalette({ open, onOpenChange, label, where, modKey = "Ctrl", chi
|
|
|
1275
1388
|
return () => cancelAnimationFrame(frame);
|
|
1276
1389
|
});
|
|
1277
1390
|
const context = useMemo2(() => ({ where, modKey, popupRef }), [where, modKey]);
|
|
1278
|
-
return /* @__PURE__ */
|
|
1279
|
-
/* @__PURE__ */
|
|
1280
|
-
/* @__PURE__ */
|
|
1391
|
+
return /* @__PURE__ */ jsx23(Dialog.Root, { open, onOpenChange: (next) => onOpenChange(next), children: /* @__PURE__ */ jsxs16(Dialog.Portal, { children: [
|
|
1392
|
+
/* @__PURE__ */ jsx23(Dialog.Backdrop, { className: "fixed inset-0 z-40 bg-scrim" }),
|
|
1393
|
+
/* @__PURE__ */ jsx23(Dialog.Viewport, { className: "fixed inset-0 z-50 flex items-start justify-center pt-[16vh]", children: /* @__PURE__ */ jsx23(
|
|
1281
1394
|
Dialog.Popup,
|
|
1282
1395
|
{
|
|
1283
1396
|
ref: popupRef,
|
|
@@ -1287,13 +1400,13 @@ function CommandPalette({ open, onOpenChange, label, where, modKey = "Ctrl", chi
|
|
|
1287
1400
|
if (e.target === popupRef.current) firstControl(popupRef.current)?.focus();
|
|
1288
1401
|
},
|
|
1289
1402
|
className: "flex w-[658px] max-w-[calc(100vw-32px)] flex-col overflow-hidden rounded-lg border border-border-default bg-bg-elevated shadow-lg outline-none",
|
|
1290
|
-
children: /* @__PURE__ */
|
|
1403
|
+
children: /* @__PURE__ */ jsx23(PaletteContext.Provider, { value: context, children })
|
|
1291
1404
|
}
|
|
1292
1405
|
) })
|
|
1293
1406
|
] }) });
|
|
1294
1407
|
}
|
|
1295
1408
|
function LevelChip({ chip, onBack }) {
|
|
1296
|
-
return /* @__PURE__ */
|
|
1409
|
+
return /* @__PURE__ */ jsx23(InputChip, { label: chip.label, leading: chip.leading, onRemove: onBack, removeLabel: `Leave ${chip.label}`, truncate: true, className: "max-w-[272px] shrink-0" });
|
|
1297
1410
|
}
|
|
1298
1411
|
function useBack(chip, popupRef) {
|
|
1299
1412
|
return () => {
|
|
@@ -1304,10 +1417,10 @@ function useBack(chip, popupRef) {
|
|
|
1304
1417
|
}
|
|
1305
1418
|
function Footer({ keys }) {
|
|
1306
1419
|
const { where } = usePalette("The footer");
|
|
1307
|
-
return /* @__PURE__ */
|
|
1308
|
-
/* @__PURE__ */
|
|
1309
|
-
keys.map(([key, word]) => /* @__PURE__ */
|
|
1310
|
-
/* @__PURE__ */
|
|
1420
|
+
return /* @__PURE__ */ jsxs16("div", { className: "flex h-9 shrink-0 items-center gap-4 border-t border-border-subtle px-5 text-caption text-text-secondary signal:font-mono signal:text-small signal:text-text-muted", children: [
|
|
1421
|
+
/* @__PURE__ */ jsx23("span", { className: "min-w-0 flex-1 truncate", children: where }),
|
|
1422
|
+
keys.map(([key, word]) => /* @__PURE__ */ jsxs16("span", { className: "flex shrink-0 items-center gap-1.5", children: [
|
|
1423
|
+
/* @__PURE__ */ jsx23(Kbd, { children: key }),
|
|
1311
1424
|
" ",
|
|
1312
1425
|
word
|
|
1313
1426
|
] }, key))
|
|
@@ -1321,11 +1434,11 @@ function CommandPaletteSearch({ query, onQueryChange, placeholder, groups, chip,
|
|
|
1321
1434
|
const rows = items.flatMap((g) => g.items);
|
|
1322
1435
|
const [litId, setLitId] = useState3();
|
|
1323
1436
|
const lit = litId === void 0 ? void 0 : rows.find((r) => r.id === litId);
|
|
1324
|
-
const inputRef =
|
|
1325
|
-
const reported =
|
|
1326
|
-
const settled =
|
|
1327
|
-
const moved =
|
|
1328
|
-
|
|
1437
|
+
const inputRef = useRef3(null);
|
|
1438
|
+
const reported = useRef3({});
|
|
1439
|
+
const settled = useRef3(null);
|
|
1440
|
+
const moved = useRef3(false);
|
|
1441
|
+
useLayoutEffect3(() => {
|
|
1329
1442
|
const before = settled.current;
|
|
1330
1443
|
const now = reported.current;
|
|
1331
1444
|
const sameLevel = before?.query === query && before?.level === chip?.label;
|
|
@@ -1384,7 +1497,7 @@ function CommandPaletteSearch({ query, onQueryChange, placeholder, groups, chip,
|
|
|
1384
1497
|
...chip && query === "" ? [["Backspace", "back"]] : [],
|
|
1385
1498
|
["Esc", "close"]
|
|
1386
1499
|
];
|
|
1387
|
-
return /* @__PURE__ */
|
|
1500
|
+
return /* @__PURE__ */ jsxs16(
|
|
1388
1501
|
Autocomplete.Root,
|
|
1389
1502
|
{
|
|
1390
1503
|
items,
|
|
@@ -1407,10 +1520,10 @@ function CommandPaletteSearch({ query, onQueryChange, placeholder, groups, chip,
|
|
|
1407
1520
|
setLitId(id);
|
|
1408
1521
|
},
|
|
1409
1522
|
children: [
|
|
1410
|
-
/* @__PURE__ */
|
|
1411
|
-
/* @__PURE__ */
|
|
1412
|
-
chip && /* @__PURE__ */
|
|
1413
|
-
/* @__PURE__ */
|
|
1523
|
+
/* @__PURE__ */ jsxs16("div", { className: "flex h-12 shrink-0 items-center gap-3 border-b border-border-subtle px-5", children: [
|
|
1524
|
+
/* @__PURE__ */ jsx23(IconSearch, { size: 16, stroke: 1.5, className: "shrink-0 text-text-secondary" }),
|
|
1525
|
+
chip && /* @__PURE__ */ jsx23(LevelChip, { chip, onBack: back }),
|
|
1526
|
+
/* @__PURE__ */ jsx23(
|
|
1414
1527
|
Autocomplete.Input,
|
|
1415
1528
|
{
|
|
1416
1529
|
ref: inputRef,
|
|
@@ -1422,43 +1535,43 @@ function CommandPaletteSearch({ query, onQueryChange, placeholder, groups, chip,
|
|
|
1422
1535
|
}
|
|
1423
1536
|
)
|
|
1424
1537
|
] }),
|
|
1425
|
-
/* @__PURE__ */
|
|
1426
|
-
children != null && /* @__PURE__ */
|
|
1427
|
-
/* @__PURE__ */
|
|
1428
|
-
/* @__PURE__ */
|
|
1429
|
-
/* @__PURE__ */
|
|
1538
|
+
/* @__PURE__ */ jsxs16(ScrollArea, { viewportClassName: "max-h-[420px]", contentClassName: "flex flex-col p-2", children: [
|
|
1539
|
+
children != null && /* @__PURE__ */ jsx23("div", { className: "flex flex-col gap-2 px-3 pb-2 pt-3", children }),
|
|
1540
|
+
/* @__PURE__ */ jsx23(Autocomplete.List, { className: "flex flex-col", children: (group) => /* @__PURE__ */ jsxs16(Autocomplete.Group, { items: group.items, className: "flex flex-col", children: [
|
|
1541
|
+
/* @__PURE__ */ jsx23(Autocomplete.GroupLabel, { className: "flex h-7 shrink-0 items-center px-3", children: /* @__PURE__ */ jsx23(SectionLabel, { className: "text-text-secondary", children: group.value }) }),
|
|
1542
|
+
/* @__PURE__ */ jsx23(Autocomplete.Collection, { children: (row) => (
|
|
1430
1543
|
/* The menu row, as the list's own option — the way Select
|
|
1431
1544
|
puts it on `Select.Item`. One row, two parts. */
|
|
1432
|
-
/* @__PURE__ */
|
|
1545
|
+
/* @__PURE__ */ jsx23(Autocomplete.Item, { value: row, onClick: () => row.onSelect(), className: menuItemClassName({ size: "tall" }), children: /* @__PURE__ */ jsx23(
|
|
1433
1546
|
MenuItemBody,
|
|
1434
1547
|
{
|
|
1435
1548
|
size: "tall",
|
|
1436
1549
|
label: row.label,
|
|
1437
1550
|
description: row.description,
|
|
1438
|
-
leading: /* @__PURE__ */
|
|
1551
|
+
leading: /* @__PURE__ */ jsx23(RowLeading, { row }),
|
|
1439
1552
|
submenu: !!row.onGoIn,
|
|
1440
|
-
hint: row.onGoIn ? /* @__PURE__ */
|
|
1553
|
+
hint: row.onGoIn ? /* @__PURE__ */ jsx23(Kbd, { children: "Tab" }) : /* @__PURE__ */ jsx23(EnterHint, {})
|
|
1441
1554
|
}
|
|
1442
1555
|
) }, row.id)
|
|
1443
1556
|
) })
|
|
1444
1557
|
] }, group.value) }),
|
|
1445
|
-
/* @__PURE__ */
|
|
1446
|
-
/* @__PURE__ */
|
|
1447
|
-
/* @__PURE__ */
|
|
1558
|
+
/* @__PURE__ */ jsx23(Autocomplete.Status, { className: "flex items-center gap-2 px-3 [&:not(:empty)]:h-8", children: pending && /* @__PURE__ */ jsxs16(Fragment3, { children: [
|
|
1559
|
+
/* @__PURE__ */ jsx23(IconLoader22, { size: 12, stroke: 1.5, className: "shrink-0 animate-spin text-text-muted" }),
|
|
1560
|
+
/* @__PURE__ */ jsx23("span", { className: "text-caption text-text-muted", children: pending })
|
|
1448
1561
|
] }) }),
|
|
1449
|
-
notes.map((note) => /* @__PURE__ */
|
|
1450
|
-
/* @__PURE__ */
|
|
1562
|
+
notes.map((note) => /* @__PURE__ */ jsx23("div", { className: "flex min-h-8 items-center px-3", children: /* @__PURE__ */ jsx23(FieldLine, { children: note }) }, note)),
|
|
1563
|
+
/* @__PURE__ */ jsx23(Autocomplete.Empty, { className: "flex items-center px-3 [&:not(:empty)]:min-h-10", children: empty && /* @__PURE__ */ jsx23(EmptyState, { scope: "section", message: empty }) })
|
|
1451
1564
|
] }),
|
|
1452
|
-
/* @__PURE__ */
|
|
1565
|
+
/* @__PURE__ */ jsx23(Footer, { keys })
|
|
1453
1566
|
]
|
|
1454
1567
|
}
|
|
1455
1568
|
);
|
|
1456
1569
|
}
|
|
1457
1570
|
function RowLeading({ row }) {
|
|
1458
1571
|
if (row.icon != null) {
|
|
1459
|
-
return /* @__PURE__ */
|
|
1572
|
+
return /* @__PURE__ */ jsx23("span", { className: "flex size-8 items-center justify-center rounded-sm bg-bg-inset text-text-secondary", children: row.icon });
|
|
1460
1573
|
}
|
|
1461
|
-
return /* @__PURE__ */
|
|
1574
|
+
return /* @__PURE__ */ jsx23("span", { className: "flex size-8 items-center justify-center", children: row.leading });
|
|
1462
1575
|
}
|
|
1463
1576
|
var TEXT_TYPES = /* @__PURE__ */ new Set(["", "text", "search", "email", "url", "tel", "password", "number"]);
|
|
1464
1577
|
function isTextField(el) {
|
|
@@ -1468,31 +1581,18 @@ function isTextField(el) {
|
|
|
1468
1581
|
function CommandPaletteForm({ chip, icon, submitLabel, onSubmit, submitWaits, working, error, children }) {
|
|
1469
1582
|
const { modKey, popupRef } = usePalette("CommandPaletteForm");
|
|
1470
1583
|
const back = useBack(chip, popupRef);
|
|
1471
|
-
const frameRef =
|
|
1584
|
+
const frameRef = useRef3(null);
|
|
1472
1585
|
const busy = !!working;
|
|
1473
|
-
const busyRef =
|
|
1586
|
+
const busyRef = useRef3(busy);
|
|
1474
1587
|
busyRef.current = busy;
|
|
1475
|
-
const
|
|
1476
|
-
const firstInvalid = () => frameRef.current?.querySelector("[data-invalid]")?.querySelector(CONTROL) ?? null;
|
|
1588
|
+
const firstInvalid2 = () => frameRef.current?.querySelector("[data-invalid]")?.querySelector(CONTROL2) ?? null;
|
|
1477
1589
|
const submit = () => {
|
|
1478
1590
|
if (busyRef.current || submitWaits) return;
|
|
1479
|
-
const active = document.activeElement;
|
|
1480
|
-
returnTo.current = active instanceof HTMLElement && frameRef.current?.contains(active) ? active : null;
|
|
1481
1591
|
onSubmit();
|
|
1482
1592
|
requestAnimationFrame(() => {
|
|
1483
|
-
if (!busyRef.current)
|
|
1593
|
+
if (!busyRef.current) firstInvalid2()?.focus();
|
|
1484
1594
|
});
|
|
1485
1595
|
};
|
|
1486
|
-
useLayoutEffect2(() => {
|
|
1487
|
-
if (busy) {
|
|
1488
|
-
frameRef.current?.focus();
|
|
1489
|
-
return;
|
|
1490
|
-
}
|
|
1491
|
-
if (document.activeElement !== frameRef.current) return;
|
|
1492
|
-
const was = returnTo.current;
|
|
1493
|
-
const target = firstInvalid() ?? (was?.isConnected && !was.disabled ? was : null) ?? firstControl(popupRef.current);
|
|
1494
|
-
target?.focus();
|
|
1495
|
-
}, [busy]);
|
|
1496
1596
|
const backWorksFrom = (el) => {
|
|
1497
1597
|
const frame = frameRef.current;
|
|
1498
1598
|
if (!frame || busy || !el || !frame.contains(el)) return false;
|
|
@@ -1501,13 +1601,8 @@ function CommandPaletteForm({ chip, icon, submitLabel, onSubmit, submitWaits, wo
|
|
|
1501
1601
|
};
|
|
1502
1602
|
const [backNamed, setBackNamed] = useState3(false);
|
|
1503
1603
|
const measure = () => setBackNamed(backWorksFrom(document.activeElement));
|
|
1504
|
-
|
|
1604
|
+
useLayoutEffect3(measure);
|
|
1505
1605
|
const onKeyDown = (e) => {
|
|
1506
|
-
if (e.key === "Enter" && (e.ctrlKey || e.metaKey)) {
|
|
1507
|
-
e.preventDefault();
|
|
1508
|
-
submit();
|
|
1509
|
-
return;
|
|
1510
|
-
}
|
|
1511
1606
|
if (e.key === "Backspace" && !e.ctrlKey && !e.metaKey && !e.altKey && backWorksFrom(e.target)) {
|
|
1512
1607
|
e.preventDefault();
|
|
1513
1608
|
back();
|
|
@@ -1518,54 +1613,56 @@ function CommandPaletteForm({ chip, icon, submitLabel, onSubmit, submitWaits, wo
|
|
|
1518
1613
|
...backNamed ? [["Backspace", "back"]] : [],
|
|
1519
1614
|
["Esc", "close"]
|
|
1520
1615
|
];
|
|
1521
|
-
return /* @__PURE__ */
|
|
1522
|
-
/* @__PURE__ */
|
|
1523
|
-
icon != null && /* @__PURE__ */
|
|
1524
|
-
/* @__PURE__ */
|
|
1616
|
+
return /* @__PURE__ */ jsxs16("div", { ref: frameRef, tabIndex: -1, onKeyDown, onFocus: measure, onBlur: measure, onInput: measure, className: "flex min-h-0 flex-col outline-none", children: [
|
|
1617
|
+
/* @__PURE__ */ jsxs16("div", { className: "flex h-12 shrink-0 items-center gap-3 border-b border-border-subtle px-5", children: [
|
|
1618
|
+
icon != null && /* @__PURE__ */ jsx23("span", { className: "flex shrink-0 items-center text-text-secondary", children: icon }),
|
|
1619
|
+
/* @__PURE__ */ jsx23(LevelChip, { chip, onBack: back })
|
|
1525
1620
|
] }),
|
|
1526
|
-
/* @__PURE__ */
|
|
1527
|
-
|
|
1528
|
-
/* @__PURE__ */
|
|
1529
|
-
|
|
1530
|
-
|
|
1531
|
-
|
|
1532
|
-
|
|
1533
|
-
|
|
1534
|
-
|
|
1535
|
-
|
|
1536
|
-
|
|
1537
|
-
|
|
1538
|
-
|
|
1539
|
-
|
|
1621
|
+
/* @__PURE__ */ jsxs16(Form, { "data-command-palette-fields": "", busy, enterSends: false, onSubmit: submit, className: "flex min-h-0 flex-col", children: [
|
|
1622
|
+
/* @__PURE__ */ jsx23(ScrollArea, { viewportClassName: "max-h-[420px]", contentClassName: "flex flex-col px-5 py-4", children: /* @__PURE__ */ jsx23("div", { className: "flex flex-col gap-4", children }) }),
|
|
1623
|
+
/* @__PURE__ */ jsxs16("div", { className: "flex shrink-0 items-center gap-3 px-5 pb-4", children: [
|
|
1624
|
+
/* @__PURE__ */ jsx23("div", { className: "min-w-0 flex-1", children: working ? /* @__PURE__ */ jsx23(FieldLine, { children: working.line }) : error ? /* @__PURE__ */ jsx23(FieldLine, { tone: "error", children: error }) : null }),
|
|
1625
|
+
/* @__PURE__ */ jsx23(
|
|
1626
|
+
Button,
|
|
1627
|
+
{
|
|
1628
|
+
type: "submit",
|
|
1629
|
+
variant: "primary",
|
|
1630
|
+
disabled: busy,
|
|
1631
|
+
disabledReason: busy ? void 0 : submitWaits,
|
|
1632
|
+
leadingIcon: busy ? /* @__PURE__ */ jsx23(IconLoader22, { size: 16, stroke: 1.5, className: "animate-spin" }) : void 0,
|
|
1633
|
+
children: working ? working.button : submitLabel
|
|
1634
|
+
}
|
|
1635
|
+
)
|
|
1636
|
+
] })
|
|
1540
1637
|
] }),
|
|
1541
|
-
/* @__PURE__ */
|
|
1638
|
+
/* @__PURE__ */ jsx23(Footer, { keys })
|
|
1542
1639
|
] });
|
|
1543
1640
|
}
|
|
1544
1641
|
var BAR_WIDTHS = ["w-4/5", "w-3/5", "w-2/3"];
|
|
1545
1642
|
function CommandPaletteWorking({ children, bars = 2 }) {
|
|
1546
|
-
return /* @__PURE__ */
|
|
1547
|
-
/* @__PURE__ */
|
|
1548
|
-
/* @__PURE__ */
|
|
1643
|
+
return /* @__PURE__ */ jsxs16("div", { role: "status", className: "flex flex-col gap-2", children: [
|
|
1644
|
+
/* @__PURE__ */ jsxs16("span", { className: "flex items-center gap-2 text-caption text-text-secondary", children: [
|
|
1645
|
+
/* @__PURE__ */ jsx23(IconLoader22, { size: 14, stroke: 1.5, className: "shrink-0 animate-spin" }),
|
|
1549
1646
|
children
|
|
1550
1647
|
] }),
|
|
1551
|
-
BAR_WIDTHS.slice(0, bars).map((width) => /* @__PURE__ */
|
|
1648
|
+
BAR_WIDTHS.slice(0, bars).map((width) => /* @__PURE__ */ jsx23(SkeletonBar, { className: cn("h-3", width) }, width))
|
|
1552
1649
|
] });
|
|
1553
1650
|
}
|
|
1554
1651
|
function CommandPaletteAnswer({ children, note }) {
|
|
1555
1652
|
const paragraphs = children.split(/\n\s*\n/).filter((p) => p.trim() !== "");
|
|
1556
|
-
return /* @__PURE__ */
|
|
1557
|
-
paragraphs.map((paragraph, i) => /* @__PURE__ */
|
|
1558
|
-
(part, j) => /^\[\d+\]$/.test(part) ? /* @__PURE__ */
|
|
1653
|
+
return /* @__PURE__ */ jsxs16("div", { className: "flex flex-col gap-2", children: [
|
|
1654
|
+
paragraphs.map((paragraph, i) => /* @__PURE__ */ jsx23("p", { className: "whitespace-pre-wrap text-body-2 text-text-primary", children: paragraph.split(/\s*(\[\d+\])/).map(
|
|
1655
|
+
(part, j) => /^\[\d+\]$/.test(part) ? /* @__PURE__ */ jsx23("sup", { className: "ml-0.5 font-mono text-small text-accent-primary", children: part.slice(1, -1) }, j) : part
|
|
1559
1656
|
) }, i)),
|
|
1560
|
-
note && /* @__PURE__ */
|
|
1657
|
+
note && /* @__PURE__ */ jsx23(FieldLine, { children: note })
|
|
1561
1658
|
] });
|
|
1562
1659
|
}
|
|
1563
1660
|
function CommandPaletteQuote({ children }) {
|
|
1564
|
-
return /* @__PURE__ */
|
|
1661
|
+
return /* @__PURE__ */ jsx23("p", { className: "whitespace-pre-wrap border-l-2 border-border-default pl-3 text-body-2 text-text-primary", children });
|
|
1565
1662
|
}
|
|
1566
1663
|
|
|
1567
1664
|
// src/InlineChip.tsx
|
|
1568
|
-
import { Fragment as Fragment4, jsx as
|
|
1665
|
+
import { Fragment as Fragment4, jsx as jsx24, jsxs as jsxs17 } from "react/jsx-runtime";
|
|
1569
1666
|
var INLINE_CHIP_CLASSES = "inline-flex h-[1.4em] items-center gap-1 rounded-sm px-1 mx-0.5 align-top text-body-2 font-normal select-none";
|
|
1570
1667
|
var INLINE_CHIP_TONE_CLASSES = {
|
|
1571
1668
|
/** A thing or a place — anything that is not a person. */
|
|
@@ -1587,41 +1684,41 @@ function inlineChipClassName(tone, className) {
|
|
|
1587
1684
|
return cn(INLINE_CHIP_CLASSES, INLINE_CHIP_TONE_CLASSES[tone], className);
|
|
1588
1685
|
}
|
|
1589
1686
|
function InlineChip({ tone = "neutral", icon, href, className, children, ...props }) {
|
|
1590
|
-
const body = /* @__PURE__ */
|
|
1591
|
-
icon && /* @__PURE__ */
|
|
1687
|
+
const body = /* @__PURE__ */ jsxs17(Fragment4, { children: [
|
|
1688
|
+
icon && /* @__PURE__ */ jsx24("span", { className: "flex size-4 shrink-0 items-center justify-center text-text-secondary", children: icon }),
|
|
1592
1689
|
children
|
|
1593
1690
|
] });
|
|
1594
1691
|
const classes = inlineChipClassName(tone, className);
|
|
1595
1692
|
if (href === void 0) {
|
|
1596
|
-
return /* @__PURE__ */
|
|
1693
|
+
return /* @__PURE__ */ jsx24("span", { className: classes, ...props, children: body });
|
|
1597
1694
|
}
|
|
1598
|
-
return /* @__PURE__ */
|
|
1695
|
+
return /* @__PURE__ */ jsx24("a", { href, className: classes, ...props, children: body });
|
|
1599
1696
|
}
|
|
1600
1697
|
|
|
1601
1698
|
// src/IdentityMenu.tsx
|
|
1602
|
-
import { useCallback, useRef as
|
|
1699
|
+
import { useCallback, useRef as useRef4 } from "react";
|
|
1603
1700
|
|
|
1604
1701
|
// src/Divider.tsx
|
|
1605
1702
|
import { Separator } from "@base-ui/react/separator";
|
|
1606
|
-
import { jsx as
|
|
1703
|
+
import { jsx as jsx25, jsxs as jsxs18 } from "react/jsx-runtime";
|
|
1607
1704
|
function Divider({ orientation = "horizontal", label, tone = "default", className }) {
|
|
1608
1705
|
if (label && orientation === "horizontal") {
|
|
1609
1706
|
const line = tone === "warning" ? "bg-warning-muted" : "bg-border-subtle";
|
|
1610
|
-
return /* @__PURE__ */
|
|
1707
|
+
return /* @__PURE__ */ jsxs18(
|
|
1611
1708
|
Separator,
|
|
1612
1709
|
{
|
|
1613
1710
|
orientation: "horizontal",
|
|
1614
1711
|
"aria-label": label,
|
|
1615
1712
|
className: cn("flex shrink-0 items-center gap-2 mx-3", className),
|
|
1616
1713
|
children: [
|
|
1617
|
-
/* @__PURE__ */
|
|
1618
|
-
/* @__PURE__ */
|
|
1619
|
-
/* @__PURE__ */
|
|
1714
|
+
/* @__PURE__ */ jsx25("span", { "aria-hidden": "true", className: cn("h-px flex-1", line) }),
|
|
1715
|
+
/* @__PURE__ */ jsx25("span", { className: cn("shrink-0 text-caption", tone === "warning" ? "text-warning-default" : "text-text-muted"), children: label }),
|
|
1716
|
+
/* @__PURE__ */ jsx25("span", { "aria-hidden": "true", className: cn("h-px flex-1", line) })
|
|
1620
1717
|
]
|
|
1621
1718
|
}
|
|
1622
1719
|
);
|
|
1623
1720
|
}
|
|
1624
|
-
return /* @__PURE__ */
|
|
1721
|
+
return /* @__PURE__ */ jsx25(
|
|
1625
1722
|
Separator,
|
|
1626
1723
|
{
|
|
1627
1724
|
orientation,
|
|
@@ -1631,15 +1728,15 @@ function Divider({ orientation = "horizontal", label, tone = "default", classNam
|
|
|
1631
1728
|
}
|
|
1632
1729
|
|
|
1633
1730
|
// src/Person.tsx
|
|
1634
|
-
import { jsx as
|
|
1731
|
+
import { jsx as jsx26, jsxs as jsxs19 } from "react/jsx-runtime";
|
|
1635
1732
|
function Person({ name, picture, fallback = "\u2014", size = 20, className }) {
|
|
1636
1733
|
return (
|
|
1637
1734
|
// gap-2: the face-to-name distance is one rule (8px) wherever a person
|
|
1638
1735
|
// appears — Katerina, 2026-09-01, set on Person so PersonTrigger and every
|
|
1639
1736
|
// row agree by construction.
|
|
1640
|
-
/* @__PURE__ */
|
|
1641
|
-
/* @__PURE__ */
|
|
1642
|
-
/* @__PURE__ */
|
|
1737
|
+
/* @__PURE__ */ jsxs19("span", { className: cn("flex min-w-0 items-center gap-2", className), children: [
|
|
1738
|
+
/* @__PURE__ */ jsx26(Avatar, { name, src: picture, size }),
|
|
1739
|
+
/* @__PURE__ */ jsx26("span", { className: cn("truncate", !name && "text-text-muted"), children: name ?? fallback })
|
|
1643
1740
|
] })
|
|
1644
1741
|
);
|
|
1645
1742
|
}
|
|
@@ -1647,10 +1744,10 @@ function Person({ name, picture, fallback = "\u2014", size = 20, className }) {
|
|
|
1647
1744
|
// src/PersonTrigger.tsx
|
|
1648
1745
|
import { Button as BaseButton5 } from "@base-ui/react/button";
|
|
1649
1746
|
import { IconChevronDown } from "@tabler/icons-react";
|
|
1650
|
-
import { jsx as
|
|
1747
|
+
import { jsx as jsx27, jsxs as jsxs20 } from "react/jsx-runtime";
|
|
1651
1748
|
function PersonTrigger({ name, picture, fallback, size, open = false, compact = false, className, ...props }) {
|
|
1652
1749
|
if (compact) {
|
|
1653
|
-
return /* @__PURE__ */
|
|
1750
|
+
return /* @__PURE__ */ jsx27(
|
|
1654
1751
|
BaseButton5,
|
|
1655
1752
|
{
|
|
1656
1753
|
type: "button",
|
|
@@ -1659,11 +1756,11 @@ function PersonTrigger({ name, picture, fallback, size, open = false, compact =
|
|
|
1659
1756
|
"aria-label": props["aria-label"] ?? name ?? fallback,
|
|
1660
1757
|
className: cn("cursor-pointer rounded-full", className),
|
|
1661
1758
|
...props,
|
|
1662
|
-
children: /* @__PURE__ */
|
|
1759
|
+
children: /* @__PURE__ */ jsx27(Avatar, { name, src: picture, size: size ?? 36 })
|
|
1663
1760
|
}
|
|
1664
1761
|
);
|
|
1665
1762
|
}
|
|
1666
|
-
return /* @__PURE__ */
|
|
1763
|
+
return /* @__PURE__ */ jsxs20(
|
|
1667
1764
|
BaseButton5,
|
|
1668
1765
|
{
|
|
1669
1766
|
type: "button",
|
|
@@ -1681,18 +1778,18 @@ function PersonTrigger({ name, picture, fallback, size, open = false, compact =
|
|
|
1681
1778
|
),
|
|
1682
1779
|
...props,
|
|
1683
1780
|
children: [
|
|
1684
|
-
/* @__PURE__ */
|
|
1685
|
-
/* @__PURE__ */
|
|
1781
|
+
/* @__PURE__ */ jsx27(Person, { name, picture, fallback, size: size ?? 22 }),
|
|
1782
|
+
/* @__PURE__ */ jsx27(IconChevronDown, { size: 14, stroke: 1.5, className: "shrink-0 text-text-muted" })
|
|
1686
1783
|
]
|
|
1687
1784
|
}
|
|
1688
1785
|
);
|
|
1689
1786
|
}
|
|
1690
1787
|
|
|
1691
1788
|
// src/IdentityMenu.tsx
|
|
1692
|
-
import { Fragment as Fragment5, jsx as
|
|
1789
|
+
import { Fragment as Fragment5, jsx as jsx28, jsxs as jsxs21 } from "react/jsx-runtime";
|
|
1693
1790
|
function IdentityPanel({ className, onClose = () => {
|
|
1694
1791
|
}, ...rest }) {
|
|
1695
|
-
return /* @__PURE__ */
|
|
1792
|
+
return /* @__PURE__ */ jsx28(MenuPanel, { className: cn("w-72", className), children: /* @__PURE__ */ jsx28(IdentityRows, { ...rest, onClose }) });
|
|
1696
1793
|
}
|
|
1697
1794
|
function IdentityRows({ me, signedIn, relayUrl, idBase, onCopyKey, onSignOut, onClose, children }) {
|
|
1698
1795
|
const act = (action) => () => {
|
|
@@ -1700,53 +1797,53 @@ function IdentityRows({ me, signedIn, relayUrl, idBase, onCopyKey, onSignOut, on
|
|
|
1700
1797
|
action();
|
|
1701
1798
|
};
|
|
1702
1799
|
const appRows = typeof children === "function" ? children(onClose) : children;
|
|
1703
|
-
return /* @__PURE__ */
|
|
1704
|
-
/* @__PURE__ */
|
|
1705
|
-
/* @__PURE__ */
|
|
1706
|
-
/* @__PURE__ */
|
|
1707
|
-
me.email && /* @__PURE__ */
|
|
1800
|
+
return /* @__PURE__ */ jsxs21(Fragment5, { children: [
|
|
1801
|
+
/* @__PURE__ */ jsxs21(MenuSection, { label: signedIn ? "Signed in as" : "Acting as", children: [
|
|
1802
|
+
/* @__PURE__ */ jsx28(MenuRow, { children: /* @__PURE__ */ jsx28(Person, { name: me.name, picture: me.picture, fallback: "Anonymous", size: 28, className: "text-body-2-strong" }) }),
|
|
1803
|
+
/* @__PURE__ */ jsx28(Note, { children: signedIn ? "Your Estiva ID. The same person you are in every Estiva app." : "A key held by this browser only. Nobody else knows who you are." }),
|
|
1804
|
+
me.email && /* @__PURE__ */ jsxs21(Note, { children: [
|
|
1708
1805
|
me.email,
|
|
1709
1806
|
" \xB7 known to Estiva, never published to the relay"
|
|
1710
1807
|
] })
|
|
1711
1808
|
] }),
|
|
1712
|
-
relayUrl && /* @__PURE__ */
|
|
1713
|
-
/* @__PURE__ */
|
|
1714
|
-
/* @__PURE__ */
|
|
1715
|
-
/* @__PURE__ */
|
|
1716
|
-
/* @__PURE__ */
|
|
1809
|
+
relayUrl && /* @__PURE__ */ jsxs21(Fragment5, { children: [
|
|
1810
|
+
/* @__PURE__ */ jsx28(Divider, { className: "mx-0 my-2" }),
|
|
1811
|
+
/* @__PURE__ */ jsxs21(MenuSection, { label: "Workspace", children: [
|
|
1812
|
+
/* @__PURE__ */ jsx28(MenuRow, { children: /* @__PURE__ */ jsx28("span", { className: "break-all font-mono text-caption text-text-primary", children: relayUrl }) }),
|
|
1813
|
+
/* @__PURE__ */ jsx28(Note, { children: "Everyone on this relay shares this workspace, in every app." })
|
|
1717
1814
|
] })
|
|
1718
1815
|
] }),
|
|
1719
|
-
appRows && /* @__PURE__ */
|
|
1720
|
-
/* @__PURE__ */
|
|
1816
|
+
appRows && /* @__PURE__ */ jsxs21(Fragment5, { children: [
|
|
1817
|
+
/* @__PURE__ */ jsx28(Divider, { className: "mx-0 my-2" }),
|
|
1721
1818
|
appRows
|
|
1722
1819
|
] }),
|
|
1723
|
-
signedIn && idBase || onCopyKey || idBase && onSignOut ? /* @__PURE__ */
|
|
1724
|
-
signedIn && idBase && /* @__PURE__ */
|
|
1820
|
+
signedIn && idBase || onCopyKey || idBase && onSignOut ? /* @__PURE__ */ jsx28(Divider, { className: "mx-0 my-2" }) : null,
|
|
1821
|
+
signedIn && idBase && /* @__PURE__ */ jsx28(
|
|
1725
1822
|
MenuItem,
|
|
1726
1823
|
{
|
|
1727
1824
|
label: "Edit your profile in Estiva ID",
|
|
1728
1825
|
onClick: act(() => window.open(idBase, "_blank", "noopener,noreferrer"))
|
|
1729
1826
|
}
|
|
1730
1827
|
),
|
|
1731
|
-
onCopyKey && /* @__PURE__ */
|
|
1732
|
-
idBase && onSignOut && /* @__PURE__ */
|
|
1828
|
+
onCopyKey && /* @__PURE__ */ jsx28(MenuItem, { label: "Copy public key", onClick: act(onCopyKey) }),
|
|
1829
|
+
idBase && onSignOut && /* @__PURE__ */ jsx28(MenuItem, { label: "Sign out", onClick: act(onSignOut) })
|
|
1733
1830
|
] });
|
|
1734
1831
|
}
|
|
1735
1832
|
function IdentityMenu({ me, signedIn, relayUrl, idBase, onCopyKey, onSignOut, compact = false, className, children }) {
|
|
1736
|
-
const actions =
|
|
1833
|
+
const actions = useRef4(null);
|
|
1737
1834
|
const close = useCallback(() => actions.current?.close(), []);
|
|
1738
1835
|
return (
|
|
1739
1836
|
/* The wrapper carries the caller's `className` and nothing else. It used
|
|
1740
1837
|
to be `relative`, because the panel was positioned against it; the panel
|
|
1741
1838
|
hangs from the trigger and portals now, so a positioning context here
|
|
1742
1839
|
would only be a lie about what this box does. */
|
|
1743
|
-
/* @__PURE__ */
|
|
1840
|
+
/* @__PURE__ */ jsx28("div", { className, children: /* @__PURE__ */ jsx28(
|
|
1744
1841
|
Menu,
|
|
1745
1842
|
{
|
|
1746
1843
|
align: "right",
|
|
1747
1844
|
actionsRef: actions,
|
|
1748
1845
|
className: "w-72",
|
|
1749
|
-
trigger: /* @__PURE__ */
|
|
1846
|
+
trigger: /* @__PURE__ */ jsx28(
|
|
1750
1847
|
PersonTrigger,
|
|
1751
1848
|
{
|
|
1752
1849
|
name: me.name,
|
|
@@ -1757,7 +1854,7 @@ function IdentityMenu({ me, signedIn, relayUrl, idBase, onCopyKey, onSignOut, co
|
|
|
1757
1854
|
"aria-label": compact ? "Account menu" : void 0
|
|
1758
1855
|
}
|
|
1759
1856
|
),
|
|
1760
|
-
children: /* @__PURE__ */
|
|
1857
|
+
children: /* @__PURE__ */ jsx28(
|
|
1761
1858
|
IdentityRows,
|
|
1762
1859
|
{
|
|
1763
1860
|
me,
|
|
@@ -1775,18 +1872,40 @@ function IdentityMenu({ me, signedIn, relayUrl, idBase, onCopyKey, onSignOut, co
|
|
|
1775
1872
|
);
|
|
1776
1873
|
}
|
|
1777
1874
|
function Note({ children }) {
|
|
1778
|
-
return /* @__PURE__ */
|
|
1875
|
+
return /* @__PURE__ */ jsx28("span", { className: "px-2 pb-1.5 text-caption text-text-secondary", children });
|
|
1779
1876
|
}
|
|
1780
1877
|
|
|
1878
|
+
// src/FilePicker.tsx
|
|
1879
|
+
import { forwardRef } from "react";
|
|
1880
|
+
import { jsx as jsx29 } from "react/jsx-runtime";
|
|
1881
|
+
var FilePicker = forwardRef(function FilePicker2({ onPick, ...props }, ref) {
|
|
1882
|
+
return /* @__PURE__ */ jsx29(
|
|
1883
|
+
"input",
|
|
1884
|
+
{
|
|
1885
|
+
ref,
|
|
1886
|
+
type: "file",
|
|
1887
|
+
"aria-hidden": "true",
|
|
1888
|
+
tabIndex: -1,
|
|
1889
|
+
className: "hidden",
|
|
1890
|
+
...props,
|
|
1891
|
+
onChange: (event) => {
|
|
1892
|
+
const files = Array.from(event.target.files ?? []);
|
|
1893
|
+
event.target.value = "";
|
|
1894
|
+
if (files.length > 0) onPick(files);
|
|
1895
|
+
}
|
|
1896
|
+
}
|
|
1897
|
+
);
|
|
1898
|
+
});
|
|
1899
|
+
|
|
1781
1900
|
// src/Select.tsx
|
|
1782
1901
|
import { IconCheck as IconCheck2, IconChevronDown as IconChevronDown2 } from "@tabler/icons-react";
|
|
1783
1902
|
import { Select as BaseSelect } from "@base-ui/react/select";
|
|
1784
|
-
import { jsx as
|
|
1903
|
+
import { jsx as jsx30, jsxs as jsxs22 } from "react/jsx-runtime";
|
|
1785
1904
|
var GAP4 = 4;
|
|
1786
1905
|
var VIEWPORT_PAD4 = 8;
|
|
1787
1906
|
function Select({ value, onChange, options, size = "default", ariaLabel, placeholder = "Select\u2026", disabled, className, ...aria }) {
|
|
1788
1907
|
const selected = options.find((o) => o.value === value);
|
|
1789
|
-
return /* @__PURE__ */
|
|
1908
|
+
return /* @__PURE__ */ jsxs22(
|
|
1790
1909
|
BaseSelect.Root,
|
|
1791
1910
|
{
|
|
1792
1911
|
value,
|
|
@@ -1794,7 +1913,7 @@ function Select({ value, onChange, options, size = "default", ariaLabel, placeho
|
|
|
1794
1913
|
disabled,
|
|
1795
1914
|
modal: false,
|
|
1796
1915
|
children: [
|
|
1797
|
-
/* @__PURE__ */
|
|
1916
|
+
/* @__PURE__ */ jsxs22(
|
|
1798
1917
|
BaseSelect.Trigger,
|
|
1799
1918
|
{
|
|
1800
1919
|
"aria-label": ariaLabel,
|
|
@@ -1821,20 +1940,20 @@ function Select({ value, onChange, options, size = "default", ariaLabel, placeho
|
|
|
1821
1940
|
className
|
|
1822
1941
|
),
|
|
1823
1942
|
children: [
|
|
1824
|
-
/* @__PURE__ */
|
|
1825
|
-
selected?.leading && /* @__PURE__ */
|
|
1826
|
-
/* @__PURE__ */
|
|
1943
|
+
/* @__PURE__ */ jsxs22("span", { className: cn("flex min-w-0 items-center gap-2", !selected && "text-text-muted"), children: [
|
|
1944
|
+
selected?.leading && /* @__PURE__ */ jsx30("span", { className: "flex shrink-0 items-center", children: selected.leading }),
|
|
1945
|
+
/* @__PURE__ */ jsx30(BaseSelect.Value, { className: "truncate", children: () => selected?.label ?? placeholder })
|
|
1827
1946
|
] }),
|
|
1828
|
-
/* @__PURE__ */
|
|
1947
|
+
/* @__PURE__ */ jsx30(
|
|
1829
1948
|
BaseSelect.Icon,
|
|
1830
1949
|
{
|
|
1831
|
-
render: /* @__PURE__ */
|
|
1950
|
+
render: /* @__PURE__ */ jsx30(IconChevronDown2, { size: size === "small" ? 14 : 16, stroke: 1.5, className: "shrink-0 text-text-secondary" })
|
|
1832
1951
|
}
|
|
1833
1952
|
)
|
|
1834
1953
|
]
|
|
1835
1954
|
}
|
|
1836
1955
|
),
|
|
1837
|
-
/* @__PURE__ */
|
|
1956
|
+
/* @__PURE__ */ jsx30(BaseSelect.Portal, { children: /* @__PURE__ */ jsx30(
|
|
1838
1957
|
BaseSelect.Positioner,
|
|
1839
1958
|
{
|
|
1840
1959
|
side: "bottom",
|
|
@@ -1843,25 +1962,25 @@ function Select({ value, onChange, options, size = "default", ariaLabel, placeho
|
|
|
1843
1962
|
collisionPadding: VIEWPORT_PAD4,
|
|
1844
1963
|
alignItemWithTrigger: false,
|
|
1845
1964
|
className: "z-50 data-[anchor-hidden]:hidden",
|
|
1846
|
-
children: /* @__PURE__ */
|
|
1965
|
+
children: /* @__PURE__ */ jsx30(
|
|
1847
1966
|
BaseSelect.Popup,
|
|
1848
1967
|
{
|
|
1849
|
-
render: /* @__PURE__ */
|
|
1968
|
+
render: /* @__PURE__ */ jsx30(MenuPanel, {}),
|
|
1850
1969
|
className: "min-w-[var(--anchor-width)] p-0",
|
|
1851
|
-
children: /* @__PURE__ */
|
|
1970
|
+
children: /* @__PURE__ */ jsx30(ScrollArea, { viewportClassName: "max-h-[min(288px,var(--available-height))]", contentClassName: "flex flex-col p-2", children: options.map((option) => /* @__PURE__ */ jsxs22(
|
|
1852
1971
|
BaseSelect.Item,
|
|
1853
1972
|
{
|
|
1854
1973
|
value: option.value,
|
|
1855
1974
|
className: cn(menuItemClassName({ size: "default" }), "justify-between data-[selected]:font-medium"),
|
|
1856
1975
|
children: [
|
|
1857
|
-
/* @__PURE__ */
|
|
1858
|
-
option.leading && /* @__PURE__ */
|
|
1859
|
-
/* @__PURE__ */
|
|
1976
|
+
/* @__PURE__ */ jsxs22("span", { className: "flex min-w-0 items-center gap-2", children: [
|
|
1977
|
+
option.leading && /* @__PURE__ */ jsx30("span", { className: "flex shrink-0 items-center", children: option.leading }),
|
|
1978
|
+
/* @__PURE__ */ jsx30(BaseSelect.ItemText, { className: "truncate text-body-2 text-text-primary", children: option.label })
|
|
1860
1979
|
] }),
|
|
1861
|
-
/* @__PURE__ */
|
|
1980
|
+
/* @__PURE__ */ jsx30(
|
|
1862
1981
|
BaseSelect.ItemIndicator,
|
|
1863
1982
|
{
|
|
1864
|
-
render: /* @__PURE__ */
|
|
1983
|
+
render: /* @__PURE__ */ jsx30(IconCheck2, { size: 16, stroke: 1.5, className: "shrink-0 text-text-secondary" })
|
|
1865
1984
|
}
|
|
1866
1985
|
)
|
|
1867
1986
|
]
|
|
@@ -1878,17 +1997,20 @@ function Select({ value, onChange, options, size = "default", ariaLabel, placeho
|
|
|
1878
1997
|
}
|
|
1879
1998
|
|
|
1880
1999
|
// src/TextInput.tsx
|
|
1881
|
-
import { forwardRef } from "react";
|
|
2000
|
+
import { forwardRef as forwardRef2 } from "react";
|
|
1882
2001
|
import { Input } from "@base-ui/react/input";
|
|
1883
|
-
import { jsx as
|
|
1884
|
-
var TextInput =
|
|
1885
|
-
return /* @__PURE__ */
|
|
2002
|
+
import { jsx as jsx31 } from "react/jsx-runtime";
|
|
2003
|
+
var TextInput = forwardRef2(function TextInput2({ className, type = "text", size = "default", ...props }, ref) {
|
|
2004
|
+
return /* @__PURE__ */ jsx31(
|
|
1886
2005
|
Input,
|
|
1887
2006
|
{
|
|
1888
2007
|
ref,
|
|
1889
2008
|
type,
|
|
1890
2009
|
className: cn(
|
|
1891
|
-
|
|
2010
|
+
// The border strengthens on hover, as Select's and ChipInput's do (Katerina, 16 September:
|
|
2011
|
+
// "aren't there hover states in text input and text area?"). Focus comes after hover in
|
|
2012
|
+
// Tailwind's order, so a focused field keeps the focus border under the pointer.
|
|
2013
|
+
"bg-bg-inset border border-border-default hover:border-border-strong focus:border-border-focus rounded-lg",
|
|
1892
2014
|
// The small size is the small Select's trigger, class for class.
|
|
1893
2015
|
size === "default" && "px-3 py-2 text-input-value",
|
|
1894
2016
|
size === "small" && "h-6 px-2 text-caption",
|
|
@@ -1904,17 +2026,20 @@ var TextInput = forwardRef(function TextInput2({ className, type = "text", size
|
|
|
1904
2026
|
});
|
|
1905
2027
|
|
|
1906
2028
|
// src/Textarea.tsx
|
|
1907
|
-
import { forwardRef as
|
|
1908
|
-
import { Field as
|
|
1909
|
-
import { jsx as
|
|
1910
|
-
var Textarea =
|
|
1911
|
-
return /* @__PURE__ */
|
|
1912
|
-
|
|
2029
|
+
import { forwardRef as forwardRef3 } from "react";
|
|
2030
|
+
import { Field as BaseField3 } from "@base-ui/react/field";
|
|
2031
|
+
import { jsx as jsx32 } from "react/jsx-runtime";
|
|
2032
|
+
var Textarea = forwardRef3(function Textarea2({ className, ...props }, ref) {
|
|
2033
|
+
return /* @__PURE__ */ jsx32(
|
|
2034
|
+
BaseField3.Control,
|
|
1913
2035
|
{
|
|
1914
2036
|
ref,
|
|
1915
|
-
render: /* @__PURE__ */
|
|
2037
|
+
render: /* @__PURE__ */ jsx32("textarea", {}),
|
|
1916
2038
|
className: cn(
|
|
1917
|
-
|
|
2039
|
+
// The border strengthens on hover, as Select's and ChipInput's do (Katerina, 16 September:
|
|
2040
|
+
// "aren't there hover states in text input and text area?"). Focus comes after hover in
|
|
2041
|
+
// Tailwind's order, so a focused field keeps the focus border under the pointer.
|
|
2042
|
+
"bg-bg-inset border border-border-default hover:border-border-strong focus:border-border-focus rounded-lg px-3 py-2",
|
|
1918
2043
|
"text-input-value text-text-primary placeholder:text-text-muted",
|
|
1919
2044
|
"resize-none outline-none transition-colors",
|
|
1920
2045
|
"disabled:pointer-events-none disabled:bg-bg-disabled disabled:text-text-disabled",
|
|
@@ -1930,24 +2055,24 @@ var Textarea = forwardRef2(function Textarea2({ className, ...props }, ref) {
|
|
|
1930
2055
|
import { useState as useState4 } from "react";
|
|
1931
2056
|
|
|
1932
2057
|
// src/DialogShell.tsx
|
|
1933
|
-
import { useRef as
|
|
2058
|
+
import { useRef as useRef5 } from "react";
|
|
1934
2059
|
import { Dialog as Dialog2 } from "@base-ui/react/dialog";
|
|
1935
2060
|
import { AlertDialog } from "@base-ui/react/alert-dialog";
|
|
1936
2061
|
import { IconX as IconX4 } from "@tabler/icons-react";
|
|
1937
|
-
import { jsx as
|
|
2062
|
+
import { jsx as jsx33, jsxs as jsxs23 } from "react/jsx-runtime";
|
|
1938
2063
|
function DialogShell({ title, onClose, headerContent, footer, children, bodyClassName, bodyMaxHeight, width = 502, alert = false }) {
|
|
1939
2064
|
const Parts = alert ? AlertDialog : Dialog2;
|
|
1940
|
-
const popupRef =
|
|
1941
|
-
return /* @__PURE__ */
|
|
2065
|
+
const popupRef = useRef5(null);
|
|
2066
|
+
return /* @__PURE__ */ jsx33(
|
|
1942
2067
|
Parts.Root,
|
|
1943
2068
|
{
|
|
1944
2069
|
open: true,
|
|
1945
2070
|
onOpenChange: (open) => {
|
|
1946
2071
|
if (!open) onClose();
|
|
1947
2072
|
},
|
|
1948
|
-
children: /* @__PURE__ */
|
|
1949
|
-
/* @__PURE__ */
|
|
1950
|
-
/* @__PURE__ */
|
|
2073
|
+
children: /* @__PURE__ */ jsxs23(Parts.Portal, { children: [
|
|
2074
|
+
/* @__PURE__ */ jsx33(Parts.Backdrop, { className: "fixed inset-0 z-40 bg-scrim" }),
|
|
2075
|
+
/* @__PURE__ */ jsx33("div", { className: "fixed inset-0 z-50 flex items-center justify-center pointer-events-none", children: /* @__PURE__ */ jsxs23(
|
|
1951
2076
|
Parts.Popup,
|
|
1952
2077
|
{
|
|
1953
2078
|
ref: popupRef,
|
|
@@ -1956,16 +2081,16 @@ function DialogShell({ title, onClose, headerContent, footer, children, bodyClas
|
|
|
1956
2081
|
className: "bg-bg-elevated border border-border-subtle rounded-lg shadow-lg pointer-events-auto flex flex-col overflow-hidden outline-none",
|
|
1957
2082
|
style: { width },
|
|
1958
2083
|
children: [
|
|
1959
|
-
/* @__PURE__ */
|
|
1960
|
-
headerContent ?? /* @__PURE__ */
|
|
1961
|
-
/* @__PURE__ */
|
|
2084
|
+
/* @__PURE__ */ jsxs23("div", { className: "h-12 flex items-center justify-between pl-5 pr-4 border-b border-border-subtle shrink-0", children: [
|
|
2085
|
+
headerContent ?? /* @__PURE__ */ jsx33(Parts.Title, { className: "text-h4 text-text-primary", render: /* @__PURE__ */ jsx33("span", {}), children: title }),
|
|
2086
|
+
/* @__PURE__ */ jsx33(
|
|
1962
2087
|
Parts.Close,
|
|
1963
2088
|
{
|
|
1964
|
-
render: /* @__PURE__ */
|
|
2089
|
+
render: /* @__PURE__ */ jsx33(IconButton, { tooltip: "Close", "aria-label": "Close", children: /* @__PURE__ */ jsx33(IconX4, { size: 16, stroke: 1.5 }) })
|
|
1965
2090
|
}
|
|
1966
2091
|
)
|
|
1967
2092
|
] }),
|
|
1968
|
-
bodyMaxHeight ? /* @__PURE__ */
|
|
2093
|
+
bodyMaxHeight ? /* @__PURE__ */ jsx33(
|
|
1969
2094
|
ScrollArea,
|
|
1970
2095
|
{
|
|
1971
2096
|
className: cn(footer != null && "border-b border-border-subtle"),
|
|
@@ -1973,8 +2098,8 @@ function DialogShell({ title, onClose, headerContent, footer, children, bodyClas
|
|
|
1973
2098
|
contentClassName: cn("pl-5 pr-4 py-4", bodyClassName),
|
|
1974
2099
|
children
|
|
1975
2100
|
}
|
|
1976
|
-
) : /* @__PURE__ */
|
|
1977
|
-
footer != null && /* @__PURE__ */
|
|
2101
|
+
) : /* @__PURE__ */ jsx33("div", { className: cn("pl-5 pr-4 py-4", footer != null && "border-b border-border-subtle", bodyClassName), children }),
|
|
2102
|
+
footer != null && /* @__PURE__ */ jsx33("div", { className: "h-12 flex items-center justify-end gap-2 pl-5 pr-4 shrink-0", children: footer })
|
|
1978
2103
|
]
|
|
1979
2104
|
}
|
|
1980
2105
|
) })
|
|
@@ -1984,7 +2109,7 @@ function DialogShell({ title, onClose, headerContent, footer, children, bodyClas
|
|
|
1984
2109
|
}
|
|
1985
2110
|
|
|
1986
2111
|
// src/ConfirmDialog.tsx
|
|
1987
|
-
import { Fragment as Fragment6, jsx as
|
|
2112
|
+
import { Fragment as Fragment6, jsx as jsx34, jsxs as jsxs24 } from "react/jsx-runtime";
|
|
1988
2113
|
function ConfirmDialog({ title, children, confirmLabel, destructive = false, onConfirm, onClose }) {
|
|
1989
2114
|
const [busy, setBusy] = useState4(false);
|
|
1990
2115
|
const confirm = async () => {
|
|
@@ -1996,16 +2121,16 @@ function ConfirmDialog({ title, children, confirmLabel, destructive = false, onC
|
|
|
1996
2121
|
setBusy(false);
|
|
1997
2122
|
}
|
|
1998
2123
|
};
|
|
1999
|
-
return /* @__PURE__ */
|
|
2124
|
+
return /* @__PURE__ */ jsx34(
|
|
2000
2125
|
DialogShell,
|
|
2001
2126
|
{
|
|
2002
2127
|
alert: true,
|
|
2003
2128
|
title,
|
|
2004
2129
|
onClose,
|
|
2005
2130
|
bodyClassName: "flex flex-col gap-3 text-body-2 text-text-primary",
|
|
2006
|
-
footer: /* @__PURE__ */
|
|
2007
|
-
/* @__PURE__ */
|
|
2008
|
-
/* @__PURE__ */
|
|
2131
|
+
footer: /* @__PURE__ */ jsxs24(Fragment6, { children: [
|
|
2132
|
+
/* @__PURE__ */ jsx34(Button, { variant: "muted", onClick: onClose, disabled: busy, children: "Cancel" }),
|
|
2133
|
+
/* @__PURE__ */ jsx34(Button, { variant: destructive ? "destructive" : "primary", onClick: () => void confirm(), disabled: busy, children: confirmLabel })
|
|
2009
2134
|
] }),
|
|
2010
2135
|
children
|
|
2011
2136
|
}
|
|
@@ -2013,15 +2138,15 @@ function ConfirmDialog({ title, children, confirmLabel, destructive = false, onC
|
|
|
2013
2138
|
}
|
|
2014
2139
|
|
|
2015
2140
|
// src/EditableText.tsx
|
|
2016
|
-
import { useEffect as useEffect2, useRef as
|
|
2017
|
-
import { Field as
|
|
2141
|
+
import { useEffect as useEffect2, useRef as useRef6, useState as useState5 } from "react";
|
|
2142
|
+
import { Field as BaseField4 } from "@base-ui/react/field";
|
|
2018
2143
|
import { Input as Input2 } from "@base-ui/react/input";
|
|
2019
|
-
import { jsx as
|
|
2144
|
+
import { jsx as jsx35 } from "react/jsx-runtime";
|
|
2020
2145
|
function EditableText({ value, display, displayNode, placeholder, onCommit, multiline = false, className, label, readOnly = false }) {
|
|
2021
2146
|
const [editing, setEditing] = useState5(false);
|
|
2022
2147
|
const [draft, setDraft] = useState5(value);
|
|
2023
2148
|
const [busy, setBusy] = useState5(false);
|
|
2024
|
-
const fieldRef =
|
|
2149
|
+
const fieldRef = useRef6(null);
|
|
2025
2150
|
useEffect2(() => {
|
|
2026
2151
|
if (editing) {
|
|
2027
2152
|
fieldRef.current?.focus();
|
|
@@ -2074,7 +2199,7 @@ function EditableText({ value, display, displayNode, placeholder, onCommit, mult
|
|
|
2074
2199
|
written and never read. Read-only, this is a line of text: whatever
|
|
2075
2200
|
names the region around it (a `Field`, a `Property`) names this too.
|
|
2076
2201
|
*/
|
|
2077
|
-
/* @__PURE__ */
|
|
2202
|
+
/* @__PURE__ */ jsx35(
|
|
2078
2203
|
"div",
|
|
2079
2204
|
{
|
|
2080
2205
|
className: cn("w-full px-2 py-1", multiline && !displayNode && "whitespace-pre-wrap", display ?? value ? "text-text-primary" : "text-text-muted", className),
|
|
@@ -2084,11 +2209,11 @@ function EditableText({ value, display, displayNode, placeholder, onCommit, mult
|
|
|
2084
2209
|
);
|
|
2085
2210
|
}
|
|
2086
2211
|
if (editing) {
|
|
2087
|
-
return multiline ? /* @__PURE__ */
|
|
2088
|
-
|
|
2212
|
+
return multiline ? /* @__PURE__ */ jsx35(
|
|
2213
|
+
BaseField4.Control,
|
|
2089
2214
|
{
|
|
2090
2215
|
ref: fieldRef,
|
|
2091
|
-
render: /* @__PURE__ */
|
|
2216
|
+
render: /* @__PURE__ */ jsx35("textarea", { rows: 4 }),
|
|
2092
2217
|
"aria-label": label,
|
|
2093
2218
|
value: draft,
|
|
2094
2219
|
disabled: busy,
|
|
@@ -2097,7 +2222,7 @@ function EditableText({ value, display, displayNode, placeholder, onCommit, mult
|
|
|
2097
2222
|
onBlur: () => void commit(),
|
|
2098
2223
|
className: fieldClass
|
|
2099
2224
|
}
|
|
2100
|
-
) : /* @__PURE__ */
|
|
2225
|
+
) : /* @__PURE__ */ jsx35(
|
|
2101
2226
|
Input2,
|
|
2102
2227
|
{
|
|
2103
2228
|
ref: fieldRef,
|
|
@@ -2111,7 +2236,7 @@ function EditableText({ value, display, displayNode, placeholder, onCommit, mult
|
|
|
2111
2236
|
}
|
|
2112
2237
|
);
|
|
2113
2238
|
}
|
|
2114
|
-
return /* @__PURE__ */
|
|
2239
|
+
return /* @__PURE__ */ jsx35(
|
|
2115
2240
|
"button",
|
|
2116
2241
|
{
|
|
2117
2242
|
type: "button",
|
|
@@ -2130,7 +2255,7 @@ function EditableText({ value, display, displayNode, placeholder, onCommit, mult
|
|
|
2130
2255
|
|
|
2131
2256
|
// src/ProgressBar.tsx
|
|
2132
2257
|
import { Progress } from "@base-ui/react/progress";
|
|
2133
|
-
import { jsx as
|
|
2258
|
+
import { jsx as jsx36 } from "react/jsx-runtime";
|
|
2134
2259
|
var TRACK_CLASSES = {
|
|
2135
2260
|
default: "h-1.5 w-full overflow-hidden rounded-full bg-bg-inset",
|
|
2136
2261
|
// 4px, Katerina's ruling of 14 September; Peek's own bar is 3px until it takes this one.
|
|
@@ -2143,16 +2268,16 @@ var INDICATOR_CLASSES = {
|
|
|
2143
2268
|
function ProgressBar({ value, max, label, variant = "default", className }) {
|
|
2144
2269
|
return (
|
|
2145
2270
|
// `max={0}` needs no guard: Base UI reads the 0/0 share as an empty bar.
|
|
2146
|
-
/* @__PURE__ */
|
|
2271
|
+
/* @__PURE__ */ jsx36(Progress.Root, { value, max, "aria-label": label, className, children: /* @__PURE__ */ jsx36(Progress.Track, { className: TRACK_CLASSES[variant], children: /* @__PURE__ */ jsx36(Progress.Indicator, { className: INDICATOR_CLASSES[variant] }) }) })
|
|
2147
2272
|
);
|
|
2148
2273
|
}
|
|
2149
2274
|
|
|
2150
2275
|
// src/Breadcrumb.tsx
|
|
2151
|
-
import { Fragment as Fragment7, useCallback as useCallback2, useLayoutEffect as
|
|
2152
|
-
import { jsx as
|
|
2276
|
+
import { Fragment as Fragment7, useCallback as useCallback2, useLayoutEffect as useLayoutEffect4, useRef as useRef7, useState as useState6 } from "react";
|
|
2277
|
+
import { jsx as jsx37, jsxs as jsxs25 } from "react/jsx-runtime";
|
|
2153
2278
|
function Breadcrumb({ items, className }) {
|
|
2154
|
-
const navRef =
|
|
2155
|
-
const labelRefs =
|
|
2279
|
+
const navRef = useRef7(null);
|
|
2280
|
+
const labelRefs = useRef7([]);
|
|
2156
2281
|
const [truncated, setTruncated] = useState6(/* @__PURE__ */ new Set());
|
|
2157
2282
|
const measure = useCallback2(() => {
|
|
2158
2283
|
const next = /* @__PURE__ */ new Set();
|
|
@@ -2161,7 +2286,7 @@ function Breadcrumb({ items, className }) {
|
|
|
2161
2286
|
});
|
|
2162
2287
|
setTruncated((prev) => prev.size === next.size && [...next].every((i) => prev.has(i)) ? prev : next);
|
|
2163
2288
|
}, []);
|
|
2164
|
-
|
|
2289
|
+
useLayoutEffect4(() => {
|
|
2165
2290
|
measure();
|
|
2166
2291
|
const nav = navRef.current;
|
|
2167
2292
|
if (!nav || typeof ResizeObserver === "undefined") return;
|
|
@@ -2169,7 +2294,7 @@ function Breadcrumb({ items, className }) {
|
|
|
2169
2294
|
observer.observe(nav);
|
|
2170
2295
|
return () => observer.disconnect();
|
|
2171
2296
|
}, [measure, items]);
|
|
2172
|
-
return /* @__PURE__ */
|
|
2297
|
+
return /* @__PURE__ */ jsx37("nav", { ref: navRef, "aria-label": "Breadcrumb", className: cn("flex min-w-0 items-center gap-1.5 text-body-2", className), children: items.map((item, index) => {
|
|
2173
2298
|
const last = index === items.length - 1;
|
|
2174
2299
|
const tone = item.muted || last && item.mono ? "text-text-muted" : last ? "text-text-primary" : "text-text-secondary";
|
|
2175
2300
|
const text = cn(
|
|
@@ -2187,25 +2312,25 @@ function Breadcrumb({ items, className }) {
|
|
|
2187
2312
|
// `plain` adds no look of its own: a crumb keeps the trail's size and
|
|
2188
2313
|
// tone, and brightens on hover. The package's own Link, so a trail
|
|
2189
2314
|
// follows whatever a link learns to do (UIG-5).
|
|
2190
|
-
/* @__PURE__ */
|
|
2191
|
-
) : /* @__PURE__ */
|
|
2192
|
-
return /* @__PURE__ */
|
|
2193
|
-
index > 0 && /* @__PURE__ */
|
|
2194
|
-
item.icon && /* @__PURE__ */
|
|
2315
|
+
/* @__PURE__ */ jsx37(Link, { ref: setLabelRef, variant: "plain", href: item.href, onClick: item.onClick, className: cn(text, "hover:text-text-primary"), children: item.label })
|
|
2316
|
+
) : /* @__PURE__ */ jsx37("span", { ref: setLabelRef, className: text, "aria-current": last ? "page" : void 0, children: item.label });
|
|
2317
|
+
return /* @__PURE__ */ jsxs25(Fragment7, { children: [
|
|
2318
|
+
index > 0 && /* @__PURE__ */ jsx37("span", { "aria-hidden": "true", className: "shrink-0 text-text-muted", children: "/" }),
|
|
2319
|
+
item.icon && /* @__PURE__ */ jsx37("span", { "aria-hidden": "true", className: cn("flex shrink-0", tone), children: item.icon }),
|
|
2195
2320
|
truncated.has(index) ? (
|
|
2196
2321
|
// `min-w-0 shrink` undoes the wrapper's own shrink-0 — the crumb
|
|
2197
2322
|
// must keep truncating inside it, or wrapping it would widen the
|
|
2198
2323
|
// trail and the tooltip would never be needed again.
|
|
2199
|
-
/* @__PURE__ */
|
|
2324
|
+
/* @__PURE__ */ jsx37(WithTooltip, { label: item.label, wrapperClassName: "min-w-0 shrink", children: crumb })
|
|
2200
2325
|
) : crumb
|
|
2201
2326
|
] }, `${item.label}-${index}`);
|
|
2202
2327
|
}) });
|
|
2203
2328
|
}
|
|
2204
2329
|
|
|
2205
2330
|
// src/NavItem.tsx
|
|
2206
|
-
import { jsx as
|
|
2331
|
+
import { jsx as jsx38, jsxs as jsxs26 } from "react/jsx-runtime";
|
|
2207
2332
|
function NavItem({ label, href, count, countLabel, active = false, icon, className, ...props }) {
|
|
2208
|
-
return /* @__PURE__ */
|
|
2333
|
+
return /* @__PURE__ */ jsxs26(
|
|
2209
2334
|
"a",
|
|
2210
2335
|
{
|
|
2211
2336
|
href,
|
|
@@ -2220,9 +2345,9 @@ function NavItem({ label, href, count, countLabel, active = false, icon, classNa
|
|
|
2220
2345
|
),
|
|
2221
2346
|
...props,
|
|
2222
2347
|
children: [
|
|
2223
|
-
icon && /* @__PURE__ */
|
|
2224
|
-
/* @__PURE__ */
|
|
2225
|
-
count ? /* @__PURE__ */
|
|
2348
|
+
icon && /* @__PURE__ */ jsx38("span", { className: "flex shrink-0 items-center", children: icon }),
|
|
2349
|
+
/* @__PURE__ */ jsx38("span", { className: "flex-1 truncate", children: label }),
|
|
2350
|
+
count ? /* @__PURE__ */ jsx38(WithTooltip, { label: countLabel ?? `${count} open`, children: /* @__PURE__ */ jsx38("span", { className: "min-w-4 shrink-0 text-center font-mono text-caption tabular-nums text-text-muted", children: count }) }) : null
|
|
2226
2351
|
]
|
|
2227
2352
|
}
|
|
2228
2353
|
);
|
|
@@ -2231,7 +2356,7 @@ function NavItem({ label, href, count, countLabel, active = false, icon, classNa
|
|
|
2231
2356
|
// src/Popover.tsx
|
|
2232
2357
|
import { useMemo as useMemo3 } from "react";
|
|
2233
2358
|
import { Popover as BasePopover } from "@base-ui/react/popover";
|
|
2234
|
-
import { jsx as
|
|
2359
|
+
import { jsx as jsx39, jsxs as jsxs27 } from "react/jsx-runtime";
|
|
2235
2360
|
var GAP5 = 4;
|
|
2236
2361
|
var VIEWPORT_PAD5 = 8;
|
|
2237
2362
|
function Popover({ trigger, anchor, align = "left", side = "bottom", open, onOpenChange, finalFocus, actionsRef, ariaLabel, children, className, contentClassName, maxHeight }) {
|
|
@@ -2240,7 +2365,7 @@ function Popover({ trigger, anchor, align = "left", side = "bottom", open, onOpe
|
|
|
2240
2365
|
if (anchor instanceof Element) return anchor;
|
|
2241
2366
|
return { getBoundingClientRect: () => anchor };
|
|
2242
2367
|
}, [anchor]);
|
|
2243
|
-
return /* @__PURE__ */
|
|
2368
|
+
return /* @__PURE__ */ jsxs27(
|
|
2244
2369
|
BasePopover.Root,
|
|
2245
2370
|
{
|
|
2246
2371
|
open,
|
|
@@ -2248,8 +2373,8 @@ function Popover({ trigger, anchor, align = "left", side = "bottom", open, onOpe
|
|
|
2248
2373
|
actionsRef,
|
|
2249
2374
|
modal: false,
|
|
2250
2375
|
children: [
|
|
2251
|
-
trigger && /* @__PURE__ */
|
|
2252
|
-
/* @__PURE__ */
|
|
2376
|
+
trigger && /* @__PURE__ */ jsx39(BasePopover.Trigger, { render: trigger, disabled: triggerDisabled(trigger) }),
|
|
2377
|
+
/* @__PURE__ */ jsx39(BasePopover.Portal, { children: /* @__PURE__ */ jsx39(
|
|
2253
2378
|
BasePopover.Positioner,
|
|
2254
2379
|
{
|
|
2255
2380
|
anchor: anchorTarget,
|
|
@@ -2258,15 +2383,15 @@ function Popover({ trigger, anchor, align = "left", side = "bottom", open, onOpe
|
|
|
2258
2383
|
sideOffset: GAP5,
|
|
2259
2384
|
collisionPadding: VIEWPORT_PAD5,
|
|
2260
2385
|
className: "z-50 data-[anchor-hidden]:hidden",
|
|
2261
|
-
children: /* @__PURE__ */
|
|
2386
|
+
children: /* @__PURE__ */ jsx39(
|
|
2262
2387
|
BasePopover.Popup,
|
|
2263
2388
|
{
|
|
2264
2389
|
"aria-label": ariaLabel,
|
|
2265
2390
|
initialFocus: trigger ? void 0 : false,
|
|
2266
2391
|
finalFocus,
|
|
2267
2392
|
className: cn("min-w-[180px] outline-none p-0", className),
|
|
2268
|
-
render: /* @__PURE__ */
|
|
2269
|
-
children: /* @__PURE__ */
|
|
2393
|
+
render: /* @__PURE__ */ jsx39(MenuPanel, {}),
|
|
2394
|
+
children: /* @__PURE__ */ jsx39(
|
|
2270
2395
|
ScrollArea,
|
|
2271
2396
|
{
|
|
2272
2397
|
viewportClassName: maxHeight ?? "max-h-[var(--available-height)]",
|
|
@@ -2285,9 +2410,9 @@ function Popover({ trigger, anchor, align = "left", side = "bottom", open, onOpe
|
|
|
2285
2410
|
|
|
2286
2411
|
// src/Toolbar.tsx
|
|
2287
2412
|
import { Toolbar as BaseToolbar } from "@base-ui/react/toolbar";
|
|
2288
|
-
import { jsx as
|
|
2413
|
+
import { jsx as jsx40 } from "react/jsx-runtime";
|
|
2289
2414
|
function Toolbar({ "aria-label": ariaLabel, orientation = "horizontal", loopFocus = true, surface = true, children, className }) {
|
|
2290
|
-
const strip = /* @__PURE__ */
|
|
2415
|
+
const strip = /* @__PURE__ */ jsx40(
|
|
2291
2416
|
BaseToolbar.Root,
|
|
2292
2417
|
{
|
|
2293
2418
|
"aria-label": ariaLabel,
|
|
@@ -2298,24 +2423,24 @@ function Toolbar({ "aria-label": ariaLabel, orientation = "horizontal", loopFocu
|
|
|
2298
2423
|
}
|
|
2299
2424
|
);
|
|
2300
2425
|
if (!surface) return strip;
|
|
2301
|
-
return /* @__PURE__ */
|
|
2426
|
+
return /* @__PURE__ */ jsx40(MenuPanel, { className: "w-fit p-1", children: strip });
|
|
2302
2427
|
}
|
|
2303
2428
|
function ToolbarButton({ ref, disabled, disabledReason, ...props }) {
|
|
2304
|
-
return /* @__PURE__ */
|
|
2429
|
+
return /* @__PURE__ */ jsx40(
|
|
2305
2430
|
BaseToolbar.Button,
|
|
2306
2431
|
{
|
|
2307
2432
|
ref,
|
|
2308
2433
|
disabled: disabled || !!disabledReason,
|
|
2309
2434
|
focusableWhenDisabled: true,
|
|
2310
|
-
render: /* @__PURE__ */
|
|
2435
|
+
render: /* @__PURE__ */ jsx40(IconButton, { disabled, disabledReason, ...props })
|
|
2311
2436
|
}
|
|
2312
2437
|
);
|
|
2313
2438
|
}
|
|
2314
2439
|
function ToolbarInput({ size, ...props }) {
|
|
2315
|
-
return /* @__PURE__ */
|
|
2440
|
+
return /* @__PURE__ */ jsx40(BaseToolbar.Input, { render: /* @__PURE__ */ jsx40(TextInput, { size }), ...props });
|
|
2316
2441
|
}
|
|
2317
2442
|
function ToolbarSeparator({ className }) {
|
|
2318
|
-
return /* @__PURE__ */
|
|
2443
|
+
return /* @__PURE__ */ jsx40(
|
|
2319
2444
|
BaseToolbar.Separator,
|
|
2320
2445
|
{
|
|
2321
2446
|
orientation: "vertical",
|
|
@@ -2325,9 +2450,9 @@ function ToolbarSeparator({ className }) {
|
|
|
2325
2450
|
}
|
|
2326
2451
|
|
|
2327
2452
|
// src/ReactionPicker.tsx
|
|
2328
|
-
import { jsx as
|
|
2453
|
+
import { jsx as jsx41 } from "react/jsx-runtime";
|
|
2329
2454
|
function ReactionPicker({ options, onSelect, "aria-label": ariaLabel = "Reactions", surface = true, className }) {
|
|
2330
|
-
return /* @__PURE__ */
|
|
2455
|
+
return /* @__PURE__ */ jsx41(Toolbar, { "aria-label": ariaLabel, surface, className: cn("gap-0.5", className), children: options.map((option) => (
|
|
2331
2456
|
/*
|
|
2332
2457
|
A `ToolbarButton`, not a button in a tooltip wrapper. The wrapper
|
|
2333
2458
|
would become the toolbar's item and the button inside it would never
|
|
@@ -2337,14 +2462,14 @@ function ReactionPicker({ options, onSelect, "aria-label": ariaLabel = "Reaction
|
|
|
2337
2462
|
The tooltip names it for a pointer, `aria-label` for everything else,
|
|
2338
2463
|
and both say the meaning rather than the glyph.
|
|
2339
2464
|
*/
|
|
2340
|
-
/* @__PURE__ */
|
|
2465
|
+
/* @__PURE__ */ jsx41(
|
|
2341
2466
|
ToolbarButton,
|
|
2342
2467
|
{
|
|
2343
2468
|
"aria-label": option.label,
|
|
2344
2469
|
tooltip: option.label,
|
|
2345
2470
|
onClick: () => onSelect(option.emoji),
|
|
2346
2471
|
className: "size-7 text-h3 leading-none",
|
|
2347
|
-
children: /* @__PURE__ */
|
|
2472
|
+
children: /* @__PURE__ */ jsx41("span", { "aria-hidden": "true", children: option.emoji })
|
|
2348
2473
|
},
|
|
2349
2474
|
option.emoji
|
|
2350
2475
|
)
|
|
@@ -2353,23 +2478,23 @@ function ReactionPicker({ options, onSelect, "aria-label": ariaLabel = "Reaction
|
|
|
2353
2478
|
|
|
2354
2479
|
// src/PreviewCard.tsx
|
|
2355
2480
|
import { PreviewCard as BasePreviewCard } from "@base-ui/react/preview-card";
|
|
2356
|
-
import { jsx as
|
|
2481
|
+
import { jsx as jsx42, jsxs as jsxs28 } from "react/jsx-runtime";
|
|
2357
2482
|
var GAP6 = 12;
|
|
2358
2483
|
var VIEWPORT_PAD6 = 8;
|
|
2359
2484
|
var OPEN_DELAY2 = 350;
|
|
2360
2485
|
var CLOSE_DELAY = 200;
|
|
2361
2486
|
function PreviewCard({ content, children, side = "right", delay = OPEN_DELAY2, closeDelay = CLOSE_DELAY, className, wrapperClassName }) {
|
|
2362
|
-
return /* @__PURE__ */
|
|
2363
|
-
/* @__PURE__ */
|
|
2487
|
+
return /* @__PURE__ */ jsxs28(BasePreviewCard.Root, { children: [
|
|
2488
|
+
/* @__PURE__ */ jsx42(
|
|
2364
2489
|
BasePreviewCard.Trigger,
|
|
2365
2490
|
{
|
|
2366
2491
|
delay,
|
|
2367
2492
|
closeDelay,
|
|
2368
|
-
render: /* @__PURE__ */
|
|
2493
|
+
render: /* @__PURE__ */ jsx42("span", { className: cn("inline-flex", wrapperClassName) }),
|
|
2369
2494
|
children
|
|
2370
2495
|
}
|
|
2371
2496
|
),
|
|
2372
|
-
/* @__PURE__ */
|
|
2497
|
+
/* @__PURE__ */ jsx42(BasePreviewCard.Portal, { children: /* @__PURE__ */ jsx42(
|
|
2373
2498
|
BasePreviewCard.Positioner,
|
|
2374
2499
|
{
|
|
2375
2500
|
side,
|
|
@@ -2377,12 +2502,12 @@ function PreviewCard({ content, children, side = "right", delay = OPEN_DELAY2, c
|
|
|
2377
2502
|
sideOffset: GAP6,
|
|
2378
2503
|
collisionPadding: VIEWPORT_PAD6,
|
|
2379
2504
|
className: "z-50 data-[anchor-hidden]:hidden",
|
|
2380
|
-
children: /* @__PURE__ */
|
|
2505
|
+
children: /* @__PURE__ */ jsx42(
|
|
2381
2506
|
BasePreviewCard.Popup,
|
|
2382
2507
|
{
|
|
2383
2508
|
className: cn("w-[360px] p-3 outline-none", className),
|
|
2384
|
-
render: /* @__PURE__ */
|
|
2385
|
-
children: /* @__PURE__ */
|
|
2509
|
+
render: /* @__PURE__ */ jsx42(MenuPanel, {}),
|
|
2510
|
+
children: /* @__PURE__ */ jsx42(ScrollArea, { viewportClassName: "max-h-[calc(min(300px,var(--available-height))_-_1.5rem)]", contentClassName: "flex flex-col gap-3 [&>*]:shrink-0", children: content })
|
|
2386
2511
|
}
|
|
2387
2512
|
)
|
|
2388
2513
|
}
|
|
@@ -2391,15 +2516,15 @@ function PreviewCard({ content, children, side = "right", delay = OPEN_DELAY2, c
|
|
|
2391
2516
|
}
|
|
2392
2517
|
|
|
2393
2518
|
// src/Rail.tsx
|
|
2394
|
-
import { jsx as
|
|
2519
|
+
import { jsx as jsx43 } from "react/jsx-runtime";
|
|
2395
2520
|
function Rail({ "aria-label": ariaLabel = "Navigation", children, className }) {
|
|
2396
|
-
return /* @__PURE__ */
|
|
2521
|
+
return /* @__PURE__ */ jsx43("nav", { "aria-label": ariaLabel, className: cn("flex w-16 shrink-0 flex-col items-start gap-2 px-2 py-3", className), children });
|
|
2397
2522
|
}
|
|
2398
2523
|
|
|
2399
2524
|
// src/RailItem.tsx
|
|
2400
|
-
import { jsx as
|
|
2525
|
+
import { jsx as jsx44, jsxs as jsxs29 } from "react/jsx-runtime";
|
|
2401
2526
|
function RailItem({ href, icon, label, active = false, className, ...props }) {
|
|
2402
|
-
return /* @__PURE__ */
|
|
2527
|
+
return /* @__PURE__ */ jsxs29(
|
|
2403
2528
|
"a",
|
|
2404
2529
|
{
|
|
2405
2530
|
href,
|
|
@@ -2407,14 +2532,14 @@ function RailItem({ href, icon, label, active = false, className, ...props }) {
|
|
|
2407
2532
|
className: cn("group flex w-full shrink-0 flex-col items-center gap-0.5 px-2 py-0.5", className),
|
|
2408
2533
|
...props,
|
|
2409
2534
|
children: [
|
|
2410
|
-
/* @__PURE__ */
|
|
2535
|
+
/* @__PURE__ */ jsx44(
|
|
2411
2536
|
"div",
|
|
2412
2537
|
{
|
|
2413
2538
|
className: cn(
|
|
2414
2539
|
"flex items-center justify-center rounded-lg p-2 transition-colors",
|
|
2415
2540
|
active ? "bg-bg-selected" : "group-hover:bg-bg-hover"
|
|
2416
2541
|
),
|
|
2417
|
-
children: /* @__PURE__ */
|
|
2542
|
+
children: /* @__PURE__ */ jsx44(
|
|
2418
2543
|
"span",
|
|
2419
2544
|
{
|
|
2420
2545
|
className: cn(
|
|
@@ -2426,27 +2551,27 @@ function RailItem({ href, icon, label, active = false, className, ...props }) {
|
|
|
2426
2551
|
)
|
|
2427
2552
|
}
|
|
2428
2553
|
),
|
|
2429
|
-
/* @__PURE__ */
|
|
2554
|
+
/* @__PURE__ */ jsx44("span", { className: cn("text-center text-menu", active ? "text-text-primary" : "text-text-secondary"), children: label })
|
|
2430
2555
|
]
|
|
2431
2556
|
}
|
|
2432
2557
|
);
|
|
2433
2558
|
}
|
|
2434
2559
|
|
|
2435
2560
|
// src/Sidebar.tsx
|
|
2436
|
-
import { jsx as
|
|
2561
|
+
import { jsx as jsx45 } from "react/jsx-runtime";
|
|
2437
2562
|
function Sidebar({ "aria-label": ariaLabel = "Workspace", children, className }) {
|
|
2438
|
-
return /* @__PURE__ */
|
|
2563
|
+
return /* @__PURE__ */ jsx45(
|
|
2439
2564
|
"nav",
|
|
2440
2565
|
{
|
|
2441
2566
|
"aria-label": ariaLabel,
|
|
2442
2567
|
className: cn("flex w-60 shrink-0 flex-col border-r border-border-default bg-bg-surface", className),
|
|
2443
|
-
children: /* @__PURE__ */
|
|
2568
|
+
children: /* @__PURE__ */ jsx45(ScrollArea, { className: "min-h-0 flex-1", contentClassName: "flex flex-col gap-px px-2.5 py-3", children })
|
|
2444
2569
|
}
|
|
2445
2570
|
);
|
|
2446
2571
|
}
|
|
2447
2572
|
|
|
2448
2573
|
// src/Property.tsx
|
|
2449
|
-
import { jsx as
|
|
2574
|
+
import { jsx as jsx46, jsxs as jsxs30 } from "react/jsx-runtime";
|
|
2450
2575
|
var VALUE_CLASSES = "contents";
|
|
2451
2576
|
function Property({ label, layout = "row", children, className }) {
|
|
2452
2577
|
if (layout === "stacked") {
|
|
@@ -2454,23 +2579,23 @@ function Property({ label, layout = "row", children, className }) {
|
|
|
2454
2579
|
// gap-3 — the one label-above-content distance (Katerina, 2026-09-01):
|
|
2455
2580
|
// Peek's topic-details sections already sit at 12px, Ship's rail was
|
|
2456
2581
|
// 6px, and unifying means the bigger, calmer one.
|
|
2457
|
-
/* @__PURE__ */
|
|
2458
|
-
/* @__PURE__ */
|
|
2459
|
-
/* @__PURE__ */
|
|
2582
|
+
/* @__PURE__ */ jsxs30("dl", { className: cn("flex flex-col gap-3", className), children: [
|
|
2583
|
+
/* @__PURE__ */ jsx46("dt", { className: "text-menu uppercase tracking-widest text-text-secondary", children: label }),
|
|
2584
|
+
/* @__PURE__ */ jsx46("dd", { className: VALUE_CLASSES, children })
|
|
2460
2585
|
] })
|
|
2461
2586
|
);
|
|
2462
2587
|
}
|
|
2463
|
-
return /* @__PURE__ */
|
|
2464
|
-
/* @__PURE__ */
|
|
2465
|
-
/* @__PURE__ */
|
|
2588
|
+
return /* @__PURE__ */ jsxs30("dl", { className: cn("flex items-center gap-2", className), children: [
|
|
2589
|
+
/* @__PURE__ */ jsx46("dt", { className: "w-[68px] shrink-0 text-caption text-text-secondary", children: label }),
|
|
2590
|
+
/* @__PURE__ */ jsx46("dd", { className: VALUE_CLASSES, children })
|
|
2466
2591
|
] });
|
|
2467
2592
|
}
|
|
2468
2593
|
|
|
2469
2594
|
// src/Reaction.tsx
|
|
2470
2595
|
import { Toggle } from "@base-ui/react/toggle";
|
|
2471
|
-
import { jsx as
|
|
2596
|
+
import { jsx as jsx47, jsxs as jsxs31 } from "react/jsx-runtime";
|
|
2472
2597
|
function Reaction({ emoji, count, pressed = false, className, ...props }) {
|
|
2473
|
-
return /* @__PURE__ */
|
|
2598
|
+
return /* @__PURE__ */ jsxs31(
|
|
2474
2599
|
Toggle,
|
|
2475
2600
|
{
|
|
2476
2601
|
pressed,
|
|
@@ -2492,8 +2617,8 @@ function Reaction({ emoji, count, pressed = false, className, ...props }) {
|
|
|
2492
2617
|
),
|
|
2493
2618
|
...props,
|
|
2494
2619
|
children: [
|
|
2495
|
-
/* @__PURE__ */
|
|
2496
|
-
/* @__PURE__ */
|
|
2620
|
+
/* @__PURE__ */ jsx47("span", { "aria-hidden": "true", className: "shrink-0 text-body-1 leading-none", children: emoji }),
|
|
2621
|
+
/* @__PURE__ */ jsx47("span", { className: "text-chip signal:font-mono signal:text-small signal:font-semibold signal:tabular-nums", children: count })
|
|
2497
2622
|
]
|
|
2498
2623
|
}
|
|
2499
2624
|
);
|
|
@@ -2502,19 +2627,21 @@ function Reaction({ emoji, count, pressed = false, className, ...props }) {
|
|
|
2502
2627
|
// src/SearchInput.tsx
|
|
2503
2628
|
import "react";
|
|
2504
2629
|
import { Input as Input3 } from "@base-ui/react/input";
|
|
2505
|
-
import { jsx as
|
|
2630
|
+
import { jsx as jsx48, jsxs as jsxs32 } from "react/jsx-runtime";
|
|
2506
2631
|
function SearchInput({ shortcut, className, placeholder = "Search\u2026", ...props }) {
|
|
2507
|
-
return /* @__PURE__ */
|
|
2632
|
+
return /* @__PURE__ */ jsxs32(
|
|
2508
2633
|
"div",
|
|
2509
2634
|
{
|
|
2510
2635
|
className: cn(
|
|
2511
2636
|
"flex gap-2 items-center px-3 py-2 rounded-lg",
|
|
2512
2637
|
"bg-bg-inset border border-border-default",
|
|
2513
|
-
|
|
2638
|
+
// Hover strengthens the border as it does on every other field (16 September). Here that
|
|
2639
|
+
// is also the focus look, which was the stronger border before hover existed.
|
|
2640
|
+
"hover:border-border-strong focus-within:border-border-strong transition-colors",
|
|
2514
2641
|
className
|
|
2515
2642
|
),
|
|
2516
2643
|
children: [
|
|
2517
|
-
/* @__PURE__ */
|
|
2644
|
+
/* @__PURE__ */ jsx48(
|
|
2518
2645
|
Input3,
|
|
2519
2646
|
{
|
|
2520
2647
|
className: "flex-1 min-w-0 bg-transparent text-input-value text-text-primary placeholder:text-text-muted outline-none",
|
|
@@ -2522,7 +2649,7 @@ function SearchInput({ shortcut, className, placeholder = "Search\u2026", ...pro
|
|
|
2522
2649
|
...props
|
|
2523
2650
|
}
|
|
2524
2651
|
),
|
|
2525
|
-
shortcut && /* @__PURE__ */
|
|
2652
|
+
shortcut && /* @__PURE__ */ jsx48(Kbd, { children: shortcut })
|
|
2526
2653
|
]
|
|
2527
2654
|
}
|
|
2528
2655
|
);
|
|
@@ -2531,17 +2658,17 @@ function SearchInput({ shortcut, className, placeholder = "Search\u2026", ...pro
|
|
|
2531
2658
|
// src/SectionHeader.tsx
|
|
2532
2659
|
import { IconChevronRight as IconChevronRight2 } from "@tabler/icons-react";
|
|
2533
2660
|
import { useRender } from "@base-ui/react/use-render";
|
|
2534
|
-
import { Fragment as Fragment8, jsx as
|
|
2661
|
+
import { Fragment as Fragment8, jsx as jsx49, jsxs as jsxs33 } from "react/jsx-runtime";
|
|
2535
2662
|
function SectionHeader({ title, chevron = false, isExpanded = true, onToggle, trailing, actions, showActions = "hover", render, className }) {
|
|
2536
2663
|
const titleElement = useRender({
|
|
2537
|
-
render: render ?? (chevron ? /* @__PURE__ */
|
|
2664
|
+
render: render ?? (chevron ? /* @__PURE__ */ jsx49("button", { type: "button", onClick: onToggle, "aria-expanded": isExpanded }) : /* @__PURE__ */ jsx49("span", {})),
|
|
2538
2665
|
props: {
|
|
2539
2666
|
// `text-left`: a button centres its text. `h-full` and `flex-1`: the
|
|
2540
2667
|
// whole row up to the actions is the hit target, as it was when the
|
|
2541
2668
|
// row itself carried the click.
|
|
2542
2669
|
className: "flex h-full min-w-0 flex-1 items-center gap-1 text-left",
|
|
2543
|
-
children: /* @__PURE__ */
|
|
2544
|
-
chevron && /* @__PURE__ */
|
|
2670
|
+
children: /* @__PURE__ */ jsxs33(Fragment8, { children: [
|
|
2671
|
+
chevron && /* @__PURE__ */ jsx49(
|
|
2545
2672
|
IconChevronRight2,
|
|
2546
2673
|
{
|
|
2547
2674
|
size: 12,
|
|
@@ -2549,11 +2676,11 @@ function SectionHeader({ title, chevron = false, isExpanded = true, onToggle, tr
|
|
|
2549
2676
|
className: cn("shrink-0 text-text-secondary transition-transform duration-150", isExpanded && "rotate-90")
|
|
2550
2677
|
}
|
|
2551
2678
|
),
|
|
2552
|
-
/* @__PURE__ */
|
|
2679
|
+
/* @__PURE__ */ jsx49(SectionLabel, { children: title })
|
|
2553
2680
|
] })
|
|
2554
2681
|
}
|
|
2555
2682
|
});
|
|
2556
|
-
return /* @__PURE__ */
|
|
2683
|
+
return /* @__PURE__ */ jsxs33(
|
|
2557
2684
|
"div",
|
|
2558
2685
|
{
|
|
2559
2686
|
className: cn(
|
|
@@ -2566,15 +2693,15 @@ function SectionHeader({ title, chevron = false, isExpanded = true, onToggle, tr
|
|
|
2566
2693
|
),
|
|
2567
2694
|
children: [
|
|
2568
2695
|
titleElement,
|
|
2569
|
-
trailing != null && /* @__PURE__ */
|
|
2570
|
-
actions && actions.length > 0 && /* @__PURE__ */
|
|
2696
|
+
trailing != null && /* @__PURE__ */ jsx49("div", { className: "flex shrink-0 items-center", children: trailing }),
|
|
2697
|
+
actions && actions.length > 0 && /* @__PURE__ */ jsx49(
|
|
2571
2698
|
"div",
|
|
2572
2699
|
{
|
|
2573
2700
|
className: cn(
|
|
2574
2701
|
"-mr-1 flex shrink-0 items-center gap-1",
|
|
2575
2702
|
showActions === "hover" && "opacity-0 transition-opacity focus-within:opacity-100 group-hover:opacity-100"
|
|
2576
2703
|
),
|
|
2577
|
-
children: actions.map((action) => /* @__PURE__ */
|
|
2704
|
+
children: actions.map((action) => /* @__PURE__ */ jsx49(IconButton, { tooltip: action.tooltip, "aria-label": action.tooltip, onClick: action.onClick, children: action.icon }, action.tooltip))
|
|
2578
2705
|
}
|
|
2579
2706
|
)
|
|
2580
2707
|
]
|
|
@@ -2585,7 +2712,7 @@ function SectionHeader({ title, chevron = false, isExpanded = true, onToggle, tr
|
|
|
2585
2712
|
// src/CollapsibleSection.tsx
|
|
2586
2713
|
import { useState as useState7 } from "react";
|
|
2587
2714
|
import { Collapsible } from "@base-ui/react/collapsible";
|
|
2588
|
-
import { jsx as
|
|
2715
|
+
import { jsx as jsx50, jsxs as jsxs34 } from "react/jsx-runtime";
|
|
2589
2716
|
var OPEN = "open";
|
|
2590
2717
|
var CLOSED = "closed";
|
|
2591
2718
|
function readStored(key) {
|
|
@@ -2612,14 +2739,14 @@ function CollapsibleSection({ title, defaultOpen = true, open: openProp, onOpenC
|
|
|
2612
2739
|
writeStored(storageKey, next);
|
|
2613
2740
|
onOpenChange?.(next);
|
|
2614
2741
|
};
|
|
2615
|
-
return /* @__PURE__ */
|
|
2616
|
-
/* @__PURE__ */
|
|
2617
|
-
/* @__PURE__ */
|
|
2742
|
+
return /* @__PURE__ */ jsxs34(Collapsible.Root, { open, onOpenChange: setOpen, className: cn("flex flex-col", className), children: [
|
|
2743
|
+
/* @__PURE__ */ jsx50(SectionHeader, { title, chevron: true, isExpanded: open, trailing, actions, showActions, className: "shrink-0", render: /* @__PURE__ */ jsx50(Collapsible.Trigger, {}) }),
|
|
2744
|
+
/* @__PURE__ */ jsx50(
|
|
2618
2745
|
Collapsible.Panel,
|
|
2619
2746
|
{
|
|
2620
2747
|
hiddenUntilFound: true,
|
|
2621
2748
|
className: "h-[var(--collapsible-panel-height)] overflow-hidden transition-[height] duration-150 ease-out motion-reduce:transition-none data-[starting-style]:h-0 data-[ending-style]:h-0",
|
|
2622
|
-
children: /* @__PURE__ */
|
|
2749
|
+
children: /* @__PURE__ */ jsx50("div", { className: cn("flex flex-col", contentClassName), children })
|
|
2623
2750
|
}
|
|
2624
2751
|
)
|
|
2625
2752
|
] });
|
|
@@ -2627,9 +2754,9 @@ function CollapsibleSection({ title, defaultOpen = true, open: openProp, onOpenC
|
|
|
2627
2754
|
|
|
2628
2755
|
// src/Tabs.tsx
|
|
2629
2756
|
import { Tabs as BaseTabs } from "@base-ui/react/tabs";
|
|
2630
|
-
import { jsx as
|
|
2757
|
+
import { jsx as jsx51, jsxs as jsxs35 } from "react/jsx-runtime";
|
|
2631
2758
|
function Tabs({ tabs, active, onChange, size = "default", className, ...aria }) {
|
|
2632
|
-
return /* @__PURE__ */
|
|
2759
|
+
return /* @__PURE__ */ jsx51(
|
|
2633
2760
|
BaseTabs.Root,
|
|
2634
2761
|
{
|
|
2635
2762
|
value: active,
|
|
@@ -2637,14 +2764,14 @@ function Tabs({ tabs, active, onChange, size = "default", className, ...aria })
|
|
|
2637
2764
|
if (details.reason === "none") onChange(value);
|
|
2638
2765
|
},
|
|
2639
2766
|
className,
|
|
2640
|
-
children: /* @__PURE__ */
|
|
2767
|
+
children: /* @__PURE__ */ jsx51(
|
|
2641
2768
|
BaseTabs.List,
|
|
2642
2769
|
{
|
|
2643
2770
|
activateOnFocus: true,
|
|
2644
2771
|
"aria-label": aria["aria-label"],
|
|
2645
2772
|
"aria-labelledby": aria["aria-labelledby"],
|
|
2646
2773
|
className: "flex items-center gap-2",
|
|
2647
|
-
children: tabs.map((tab) => /* @__PURE__ */
|
|
2774
|
+
children: tabs.map((tab) => /* @__PURE__ */ jsxs35(
|
|
2648
2775
|
BaseTabs.Tab,
|
|
2649
2776
|
{
|
|
2650
2777
|
value: tab.id,
|
|
@@ -2657,9 +2784,9 @@ function Tabs({ tabs, active, onChange, size = "default", className, ...aria })
|
|
|
2657
2784
|
),
|
|
2658
2785
|
children: [
|
|
2659
2786
|
tab.icon,
|
|
2660
|
-
/* @__PURE__ */
|
|
2787
|
+
/* @__PURE__ */ jsxs35("span", { className: "flex items-baseline", children: [
|
|
2661
2788
|
tab.label,
|
|
2662
|
-
tab.count !== void 0 ? /* @__PURE__ */
|
|
2789
|
+
tab.count !== void 0 ? /* @__PURE__ */ jsx51("span", { className: "ml-2.5 font-mono text-caption tabular-nums text-text-secondary", children: tab.count }) : null
|
|
2663
2790
|
] })
|
|
2664
2791
|
]
|
|
2665
2792
|
},
|
|
@@ -2672,10 +2799,10 @@ function Tabs({ tabs, active, onChange, size = "default", className, ...aria })
|
|
|
2672
2799
|
}
|
|
2673
2800
|
|
|
2674
2801
|
// src/Toast.tsx
|
|
2675
|
-
import { createContext as
|
|
2802
|
+
import { createContext as createContext4, useContext as useContext4, useMemo as useMemo4 } from "react";
|
|
2676
2803
|
import { Toast as BaseToast } from "@base-ui/react/toast";
|
|
2677
2804
|
import { IconAlertCircle as IconAlertCircle2, IconCircleCheck, IconCircleX } from "@tabler/icons-react";
|
|
2678
|
-
import { jsx as
|
|
2805
|
+
import { jsx as jsx52, jsxs as jsxs36 } from "react/jsx-runtime";
|
|
2679
2806
|
var SURFACE_STYLES = {
|
|
2680
2807
|
success: "bg-success-muted signal:bg-bg-inset signal:border signal:border-border-default signal:shadow-md",
|
|
2681
2808
|
brand: "bg-accent-muted signal:bg-bg-inset signal:border signal:border-border-default signal:shadow-md",
|
|
@@ -2711,19 +2838,19 @@ var pillClassName = (type, hasAction, className) => cn(
|
|
|
2711
2838
|
);
|
|
2712
2839
|
function LeadingIcon({ type }) {
|
|
2713
2840
|
const Icon = ICONS[type];
|
|
2714
|
-
return /* @__PURE__ */
|
|
2841
|
+
return /* @__PURE__ */ jsx52(Icon, { size: 16, stroke: 1.5, className: cn("text-text-primary shrink-0", ICON_STYLES[type]) });
|
|
2715
2842
|
}
|
|
2716
2843
|
function Toast({ label, type = "neutral", leadingIcon = true, actionLabel, onAction, className }) {
|
|
2717
2844
|
const hasAction = !!(actionLabel && onAction);
|
|
2718
|
-
return /* @__PURE__ */
|
|
2719
|
-
/* @__PURE__ */
|
|
2720
|
-
leadingIcon && /* @__PURE__ */
|
|
2721
|
-
/* @__PURE__ */
|
|
2845
|
+
return /* @__PURE__ */ jsxs36("div", { className: pillClassName(type, hasAction, className), children: [
|
|
2846
|
+
/* @__PURE__ */ jsxs36("div", { className: "flex items-center gap-2 shrink-0", children: [
|
|
2847
|
+
leadingIcon && /* @__PURE__ */ jsx52(LeadingIcon, { type }),
|
|
2848
|
+
/* @__PURE__ */ jsx52("span", { className: LABEL_CLASSES, children: label })
|
|
2722
2849
|
] }),
|
|
2723
|
-
hasAction && /* @__PURE__ */
|
|
2850
|
+
hasAction && /* @__PURE__ */ jsx52(Button, { variant: "outlined", size: "small", onClick: onAction, children: actionLabel })
|
|
2724
2851
|
] });
|
|
2725
2852
|
}
|
|
2726
|
-
var ToastContext =
|
|
2853
|
+
var ToastContext = createContext4(null);
|
|
2727
2854
|
var VISIBLE_TOASTS = 3;
|
|
2728
2855
|
function ToastProvider({ children }) {
|
|
2729
2856
|
const manager = useMemo4(() => BaseToast.createToastManager(), []);
|
|
@@ -2741,9 +2868,9 @@ function ToastProvider({ children }) {
|
|
|
2741
2868
|
}),
|
|
2742
2869
|
[manager]
|
|
2743
2870
|
);
|
|
2744
|
-
return /* @__PURE__ */
|
|
2871
|
+
return /* @__PURE__ */ jsx52(ToastContext.Provider, { value, children: /* @__PURE__ */ jsxs36(BaseToast.Provider, { toastManager: manager, limit: VISIBLE_TOASTS, children: [
|
|
2745
2872
|
children,
|
|
2746
|
-
/* @__PURE__ */
|
|
2873
|
+
/* @__PURE__ */ jsx52(BaseToast.Portal, { children: /* @__PURE__ */ jsx52(BaseToast.Viewport, { className: "fixed bottom-4 left-4 z-[100] flex flex-col-reverse items-start gap-2 pointer-events-none", children: /* @__PURE__ */ jsx52(ToastList, {}) }) })
|
|
2747
2874
|
] }) });
|
|
2748
2875
|
}
|
|
2749
2876
|
function ToastList() {
|
|
@@ -2751,25 +2878,25 @@ function ToastList() {
|
|
|
2751
2878
|
return toasts.map((toast) => {
|
|
2752
2879
|
const type = toast.type ?? "neutral";
|
|
2753
2880
|
const { leadingIcon = true, actionLabel, onAction } = toast.data ?? {};
|
|
2754
|
-
return /* @__PURE__ */
|
|
2881
|
+
return /* @__PURE__ */ jsxs36(
|
|
2755
2882
|
BaseToast.Root,
|
|
2756
2883
|
{
|
|
2757
2884
|
toast,
|
|
2758
2885
|
swipeDirection: ["left", "down"],
|
|
2759
2886
|
className: pillClassName(type, !!actionLabel, "pointer-events-auto data-[limited]:hidden"),
|
|
2760
2887
|
children: [
|
|
2761
|
-
/* @__PURE__ */
|
|
2762
|
-
leadingIcon && /* @__PURE__ */
|
|
2763
|
-
/* @__PURE__ */
|
|
2888
|
+
/* @__PURE__ */ jsxs36("div", { className: "flex items-center gap-2 shrink-0", children: [
|
|
2889
|
+
leadingIcon && /* @__PURE__ */ jsx52(LeadingIcon, { type }),
|
|
2890
|
+
/* @__PURE__ */ jsx52(BaseToast.Title, { render: /* @__PURE__ */ jsx52("span", {}), className: LABEL_CLASSES })
|
|
2764
2891
|
] }),
|
|
2765
|
-
actionLabel && /* @__PURE__ */
|
|
2892
|
+
actionLabel && /* @__PURE__ */ jsx52(
|
|
2766
2893
|
BaseToast.Action,
|
|
2767
2894
|
{
|
|
2768
2895
|
onClick: () => {
|
|
2769
2896
|
onAction?.();
|
|
2770
2897
|
close(toast.id);
|
|
2771
2898
|
},
|
|
2772
|
-
render: /* @__PURE__ */
|
|
2899
|
+
render: /* @__PURE__ */ jsx52(Button, { variant: "outlined", size: "small", children: actionLabel })
|
|
2773
2900
|
}
|
|
2774
2901
|
)
|
|
2775
2902
|
]
|
|
@@ -2779,7 +2906,7 @@ function ToastList() {
|
|
|
2779
2906
|
});
|
|
2780
2907
|
}
|
|
2781
2908
|
function useToast() {
|
|
2782
|
-
const ctx =
|
|
2909
|
+
const ctx = useContext4(ToastContext);
|
|
2783
2910
|
if (!ctx) throw new Error("useToast must be used within ToastProvider");
|
|
2784
2911
|
return ctx;
|
|
2785
2912
|
}
|
|
@@ -2810,6 +2937,8 @@ export {
|
|
|
2810
2937
|
EnterHint,
|
|
2811
2938
|
Field,
|
|
2812
2939
|
FieldLine,
|
|
2940
|
+
FilePicker,
|
|
2941
|
+
Form,
|
|
2813
2942
|
INLINE_CHIP_CLASSES,
|
|
2814
2943
|
INLINE_CHIP_TONE_CLASSES,
|
|
2815
2944
|
IconButton,
|