@drawnagency/primitives 0.1.12 → 0.1.14

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.
Files changed (60) hide show
  1. package/dist/{chunk-Q7OKHD6I.js → chunk-46QI4FDZ.js} +1 -1
  2. package/dist/{chunk-PHCEJP7I.js → chunk-EAEX6DS7.js} +4 -1
  3. package/dist/{chunk-2YYC2VJY.js → chunk-P24YUT3O.js} +1 -1
  4. package/dist/components/editor/AudienceIndicator.d.ts +9 -0
  5. package/dist/components/editor/AudienceIndicator.d.ts.map +1 -0
  6. package/dist/components/editor/IndicatorPill.d.ts +18 -0
  7. package/dist/components/editor/IndicatorPill.d.ts.map +1 -0
  8. package/dist/components/editor/SectionWrapper.d.ts.map +1 -1
  9. package/dist/components/editor/StatusIndicator.d.ts +29 -0
  10. package/dist/components/editor/StatusIndicator.d.ts.map +1 -0
  11. package/dist/components/editor/index.d.ts +3 -2
  12. package/dist/components/editor/index.d.ts.map +1 -1
  13. package/dist/components/sections/register-schemas.d.ts +1 -1
  14. package/dist/components/sections/register-schemas.d.ts.map +1 -1
  15. package/dist/components/sections/register.d.ts +1 -1
  16. package/dist/components/sections/register.d.ts.map +1 -1
  17. package/dist/components/shared/Popover.d.ts +1 -1
  18. package/dist/components/shared/Popover.d.ts.map +1 -1
  19. package/dist/components/shell/BuildStatusIndicator.d.ts +9 -0
  20. package/dist/components/shell/BuildStatusIndicator.d.ts.map +1 -0
  21. package/dist/components/shell/EditorShell.d.ts.map +1 -1
  22. package/dist/hooks/index.d.ts +1 -0
  23. package/dist/hooks/index.d.ts.map +1 -1
  24. package/dist/hooks/useBuildStatus.d.ts +11 -0
  25. package/dist/hooks/useBuildStatus.d.ts.map +1 -0
  26. package/dist/hooks/useEditorPublish.d.ts +2 -1
  27. package/dist/hooks/useEditorPublish.d.ts.map +1 -1
  28. package/dist/index.js +3 -3
  29. package/dist/lib/index.js +2 -2
  30. package/dist/schemas/index.js +2 -2
  31. package/dist/schemas/site-config.d.ts +6 -4
  32. package/dist/schemas/site-config.d.ts.map +1 -1
  33. package/package.json +1 -1
  34. package/src/components/editor/{AudiencePicker.tsx → AudienceIndicator.tsx} +25 -49
  35. package/src/components/editor/IndicatorPill.tsx +119 -0
  36. package/src/components/editor/SectionWrapper.tsx +18 -14
  37. package/src/components/editor/StatusIndicator.tsx +148 -0
  38. package/src/components/editor/index.ts +3 -2
  39. package/src/components/sections/register-schemas.ts +8 -2
  40. package/src/components/sections/register.ts +8 -2
  41. package/src/components/shared/Popover.tsx +26 -4
  42. package/src/components/shared/PopoverItem.tsx +1 -1
  43. package/src/components/shared/SplitButton.tsx +2 -2
  44. package/src/components/shell/BuildStatusIndicator.tsx +67 -0
  45. package/src/components/shell/EditorShell.tsx +23 -0
  46. package/src/hooks/index.ts +1 -0
  47. package/src/hooks/useBuildStatus.ts +139 -0
  48. package/src/hooks/useEditorPublish.ts +6 -2
  49. package/src/schemas/site-config.ts +5 -1
  50. package/dist/components/editor/AudiencePicker.d.ts +0 -9
  51. package/dist/components/editor/AudiencePicker.d.ts.map +0 -1
  52. package/dist/components/editor/StatusBadge.d.ts +0 -7
  53. package/dist/components/editor/StatusBadge.d.ts.map +0 -1
  54. package/dist/components/editor/StatusDots.d.ts +0 -25
  55. package/dist/components/editor/StatusDots.d.ts.map +0 -1
  56. package/dist/components/editor/StatusPicker.d.ts +0 -9
  57. package/dist/components/editor/StatusPicker.d.ts.map +0 -1
  58. package/src/components/editor/StatusBadge.tsx +0 -30
  59. package/src/components/editor/StatusDots.tsx +0 -131
  60. package/src/components/editor/StatusPicker.tsx +0 -86
@@ -2,7 +2,7 @@ import {
2
2
  IndexSchema,
3
3
  getAllSchemas,
4
4
  getSectionSchema
5
- } from "./chunk-PHCEJP7I.js";
5
+ } from "./chunk-EAEX6DS7.js";
6
6
  import {
7
7
  safeNextPath
8
8
  } from "./chunk-F3Z6RISI.js";
@@ -153,9 +153,12 @@ function getSectionSchema() {
153
153
 
154
154
  // src/schemas/site-config.ts
155
155
  import { z as z3 } from "zod";
156
+ var StatusSchema = z3.enum(["draft", "live", "archived", "published"]).transform(
157
+ (val) => val === "published" ? "live" : val
158
+ );
156
159
  var SectionMetaSchema = z3.object({
157
160
  type: z3.string(),
158
- status: z3.enum(["draft", "live", "archived"]),
161
+ status: StatusSchema,
159
162
  access: z3.array(z3.string())
160
163
  });
161
164
  var IndexSchema = z3.object({
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  HexColorSchema
3
- } from "./chunk-PHCEJP7I.js";
3
+ } from "./chunk-EAEX6DS7.js";
4
4
 
5
5
  // src/schemas/audience.ts
6
6
  import { z } from "zod";
@@ -0,0 +1,9 @@
1
+ import type { Audience } from "../../auth/types";
2
+ interface AudienceIndicatorProps {
3
+ access: string[];
4
+ audiences: Audience[];
5
+ onChange: (access: string[]) => void;
6
+ }
7
+ export declare function AudienceIndicator({ access, audiences, onChange }: AudienceIndicatorProps): import("react/jsx-runtime").JSX.Element;
8
+ export {};
9
+ //# sourceMappingURL=AudienceIndicator.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"AudienceIndicator.d.ts","sourceRoot":"","sources":["../../../src/components/editor/AudienceIndicator.tsx"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAEjD,UAAU,sBAAsB;IAC9B,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,SAAS,EAAE,QAAQ,EAAE,CAAC;IACtB,QAAQ,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,IAAI,CAAC;CACtC;AAED,wBAAgB,iBAAiB,CAAC,EAAE,MAAM,EAAE,SAAS,EAAE,QAAQ,EAAE,EAAE,sBAAsB,2CAkExF"}
@@ -0,0 +1,18 @@
1
+ import { type ReactNode } from "react";
2
+ interface DotDef {
3
+ color?: string;
4
+ className?: string;
5
+ }
6
+ interface IndicatorPillProps {
7
+ dots: DotDef[];
8
+ label: string;
9
+ /** Shown on hover above the pill. Currently unused — kept for planned tooltip feature. */
10
+ hoverContent?: ReactNode;
11
+ clickContent?: ReactNode | ((onClose: () => void) => ReactNode);
12
+ className?: string;
13
+ buttonClassName?: string;
14
+ ariaLabel?: string;
15
+ }
16
+ export declare function IndicatorPill({ dots, label, hoverContent, clickContent, className, buttonClassName, ariaLabel, }: IndicatorPillProps): import("react/jsx-runtime").JSX.Element;
17
+ export {};
18
+ //# sourceMappingURL=IndicatorPill.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"IndicatorPill.d.ts","sourceRoot":"","sources":["../../../src/components/editor/IndicatorPill.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAiC,KAAK,SAAS,EAAE,MAAM,OAAO,CAAC;AAItE,UAAU,MAAM;IACd,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,UAAU,kBAAkB;IAC1B,IAAI,EAAE,MAAM,EAAE,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;IACd,0FAA0F;IAC1F,YAAY,CAAC,EAAE,SAAS,CAAC;IACzB,YAAY,CAAC,EAAE,SAAS,GAAG,CAAC,CAAC,OAAO,EAAE,MAAM,IAAI,KAAK,SAAS,CAAC,CAAC;IAChE,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,wBAAgB,aAAa,CAAC,EAC5B,IAAI,EACJ,KAAK,EACL,YAAY,EACZ,YAAY,EACZ,SAAS,EACT,eAAe,EACf,SAAS,GACV,EAAE,kBAAkB,2CA0FpB"}
@@ -1 +1 @@
1
- {"version":3,"file":"SectionWrapper.d.ts","sourceRoot":"","sources":["../../../src/components/editor/SectionWrapper.tsx"],"names":[],"mappings":"AAaA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAIvD,wBAAgB,cAAc,CAAC,EAC7B,SAAS,EACT,WAAW,EACX,MAAM,EACN,KAAK,EACL,KAAK,EACL,MAAM,EACN,UAAU,EACV,OAAO,EACP,SAAS,EACT,MAAM,EACN,cAAc,EACd,cAAc,EACd,eAAe,EACf,SAAS,EACT,eAAe,EACf,QAAQ,EACR,SAAS,EACT,YAAY,EACZ,UAAU,EACV,sBAAsB,EACtB,WAAW,EACX,QAAQ,GACT,EAAE,YAAY,kDA+Rd"}
1
+ {"version":3,"file":"SectionWrapper.d.ts","sourceRoot":"","sources":["../../../src/components/editor/SectionWrapper.tsx"],"names":[],"mappings":"AAWA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAIvD,wBAAgB,cAAc,CAAC,EAC7B,SAAS,EACT,WAAW,EACX,MAAM,EACN,KAAK,EACL,KAAK,EACL,MAAM,EACN,UAAU,EACV,OAAO,EACP,SAAS,EACT,MAAM,EACN,cAAc,EACd,cAAc,EACd,eAAe,EACf,SAAS,EACT,eAAe,EACf,QAAQ,EACR,SAAS,EACT,YAAY,EACZ,UAAU,EACV,sBAAsB,EACtB,WAAW,EACX,QAAQ,GACT,EAAE,YAAY,kDAqSd"}
@@ -0,0 +1,29 @@
1
+ type StatusColor = "draft" | "live" | "archived" | "modified";
2
+ type Status = "draft" | "live" | "archived";
3
+ interface DotDef {
4
+ color: StatusColor;
5
+ }
6
+ interface DeriveInput {
7
+ mainStatus: string | null;
8
+ savedStatus: string;
9
+ contentDiffers: boolean;
10
+ isLocalOnly: boolean;
11
+ }
12
+ interface DeriveResult {
13
+ dots: DotDef[];
14
+ unsaved: boolean;
15
+ description: string;
16
+ }
17
+ export declare function deriveStatusDisplay({ mainStatus, savedStatus, contentDiffers, isLocalOnly }: DeriveInput): DeriveResult;
18
+ interface StatusIndicatorProps {
19
+ mainStatus: string | null;
20
+ savedStatus: string;
21
+ contentDiffers: boolean;
22
+ isLocalOnly: boolean;
23
+ status: Status;
24
+ dirty?: boolean;
25
+ onChange: (status: Status) => void;
26
+ }
27
+ export declare function StatusIndicator({ mainStatus, savedStatus, contentDiffers, isLocalOnly, status, dirty, onChange, }: StatusIndicatorProps): import("react/jsx-runtime").JSX.Element;
28
+ export {};
29
+ //# sourceMappingURL=StatusIndicator.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"StatusIndicator.d.ts","sourceRoot":"","sources":["../../../src/components/editor/StatusIndicator.tsx"],"names":[],"mappings":"AAKA,KAAK,WAAW,GAAG,OAAO,GAAG,MAAM,GAAG,UAAU,GAAG,UAAU,CAAC;AAC9D,KAAK,MAAM,GAAG,OAAO,GAAG,MAAM,GAAG,UAAU,CAAC;AAE5C,UAAU,MAAM;IACd,KAAK,EAAE,WAAW,CAAC;CACpB;AAED,UAAU,WAAW;IACnB,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,WAAW,EAAE,MAAM,CAAC;IACpB,cAAc,EAAE,OAAO,CAAC;IACxB,WAAW,EAAE,OAAO,CAAC;CACtB;AAED,UAAU,YAAY;IACpB,IAAI,EAAE,MAAM,EAAE,CAAC;IACf,OAAO,EAAE,OAAO,CAAC;IACjB,WAAW,EAAE,MAAM,CAAC;CACrB;AAgBD,wBAAgB,mBAAmB,CAAC,EAAE,UAAU,EAAE,WAAW,EAAE,cAAc,EAAE,WAAW,EAAE,EAAE,WAAW,GAAG,YAAY,CAkCvH;AAYD,UAAU,oBAAoB;IAC5B,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,WAAW,EAAE,MAAM,CAAC;IACpB,cAAc,EAAE,OAAO,CAAC;IACxB,WAAW,EAAE,OAAO,CAAC;IACrB,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,QAAQ,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,IAAI,CAAC;CACpC;AAED,wBAAgB,eAAe,CAAC,EAC9B,UAAU,EACV,WAAW,EACX,cAAc,EACd,WAAW,EACX,MAAM,EACN,KAAK,EACL,QAAQ,GACT,EAAE,oBAAoB,2CA4CtB"}
@@ -3,7 +3,8 @@ export { InsertButton } from "./InsertButton";
3
3
  export { DeleteButton } from "./DeleteButton";
4
4
  export { SettingsButton } from "./SettingsButton";
5
5
  export { SettingsForm } from "./SettingsForm";
6
- export { StatusBadge } from "./StatusBadge";
6
+ export { IndicatorPill } from "./IndicatorPill";
7
+ export { StatusIndicator, deriveStatusDisplay } from "./StatusIndicator";
8
+ export { AudienceIndicator } from "./AudienceIndicator";
7
9
  export { SectionWrapper } from "./SectionWrapper";
8
- export { StatusDots, deriveStatusDots } from "./StatusDots";
9
10
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/components/editor/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAC1C,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAC9C,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAC9C,OAAO,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAClD,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAC9C,OAAO,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAC5C,OAAO,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAClD,OAAO,EAAE,UAAU,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/components/editor/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAC1C,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAC9C,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAC9C,OAAO,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAClD,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAC9C,OAAO,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAChD,OAAO,EAAE,eAAe,EAAE,mBAAmB,EAAE,MAAM,mBAAmB,CAAC;AACzE,OAAO,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAC;AACxD,OAAO,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC"}
@@ -1,2 +1,2 @@
1
- export declare const schemaCount: number;
1
+ export declare function ensureSchemasRegistered(): number;
2
2
  //# sourceMappingURL=register-schemas.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"register-schemas.d.ts","sourceRoot":"","sources":["../../../src/components/sections/register-schemas.ts"],"names":[],"mappings":"AAkBA,eAAO,MAAM,WAAW,QAAiB,CAAC"}
1
+ {"version":3,"file":"register-schemas.d.ts","sourceRoot":"","sources":["../../../src/components/sections/register-schemas.ts"],"names":[],"mappings":"AAmBA,wBAAgB,uBAAuB,IAAI,MAAM,CAKhD"}
@@ -1,2 +1,2 @@
1
- export declare const sectionCount: number;
1
+ export declare function ensureSectionsRegistered(): number;
2
2
  //# sourceMappingURL=register.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"register.d.ts","sourceRoot":"","sources":["../../../src/components/sections/register.ts"],"names":[],"mappings":"AAkBA,eAAO,MAAM,YAAY,QAAiB,CAAC"}
1
+ {"version":3,"file":"register.d.ts","sourceRoot":"","sources":["../../../src/components/sections/register.ts"],"names":[],"mappings":"AAmBA,wBAAgB,wBAAwB,IAAI,MAAM,CAKjD"}
@@ -4,7 +4,7 @@ interface PopoverProps {
4
4
  onClose: () => void;
5
5
  anchorRef: RefObject<HTMLElement | null>;
6
6
  children: ReactNode;
7
- align?: "start" | "end";
7
+ align?: "start" | "end" | "auto";
8
8
  className?: string;
9
9
  }
10
10
  export declare function Popover({ isOpen, onClose, anchorRef, children, align, className, }: PopoverProps): import("react/jsx-runtime").JSX.Element | null;
@@ -1 +1 @@
1
- {"version":3,"file":"Popover.d.ts","sourceRoot":"","sources":["../../../src/components/shared/Popover.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAqB,KAAK,SAAS,EAAE,KAAK,SAAS,EAAE,MAAM,OAAO,CAAC;AAI1E,UAAU,YAAY;IACpB,MAAM,EAAE,OAAO,CAAC;IAChB,OAAO,EAAE,MAAM,IAAI,CAAC;IACpB,SAAS,EAAE,SAAS,CAAC,WAAW,GAAG,IAAI,CAAC,CAAC;IACzC,QAAQ,EAAE,SAAS,CAAC;IACpB,KAAK,CAAC,EAAE,OAAO,GAAG,KAAK,CAAC;IACxB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,wBAAgB,OAAO,CAAC,EACtB,MAAM,EACN,OAAO,EACP,SAAS,EACT,QAAQ,EACR,KAAe,EACf,SAAS,GACV,EAAE,YAAY,kDAwCd"}
1
+ {"version":3,"file":"Popover.d.ts","sourceRoot":"","sources":["../../../src/components/shared/Popover.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAgD,KAAK,SAAS,EAAE,KAAK,SAAS,EAAE,MAAM,OAAO,CAAC;AAIrG,UAAU,YAAY;IACpB,MAAM,EAAE,OAAO,CAAC;IAChB,OAAO,EAAE,MAAM,IAAI,CAAC;IACpB,SAAS,EAAE,SAAS,CAAC,WAAW,GAAG,IAAI,CAAC,CAAC;IACzC,QAAQ,EAAE,SAAS,CAAC;IACpB,KAAK,CAAC,EAAE,OAAO,GAAG,KAAK,GAAG,MAAM,CAAC;IACjC,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,wBAAgB,OAAO,CAAC,EACtB,MAAM,EACN,OAAO,EACP,SAAS,EACT,QAAQ,EACR,KAAc,EACd,SAAS,GACV,EAAE,YAAY,kDA8Dd"}
@@ -0,0 +1,9 @@
1
+ interface BuildStatusIndicatorProps {
2
+ state: "idle" | "building" | "ready" | "error";
3
+ deployUrl: string | null;
4
+ visible: boolean;
5
+ onDismiss: () => void;
6
+ }
7
+ export declare function BuildStatusIndicator({ state, deployUrl, visible, onDismiss, }: BuildStatusIndicatorProps): import("react/jsx-runtime").JSX.Element | null;
8
+ export {};
9
+ //# sourceMappingURL=BuildStatusIndicator.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"BuildStatusIndicator.d.ts","sourceRoot":"","sources":["../../../src/components/shell/BuildStatusIndicator.tsx"],"names":[],"mappings":"AAGA,UAAU,yBAAyB;IACjC,KAAK,EAAE,MAAM,GAAG,UAAU,GAAG,OAAO,GAAG,OAAO,CAAC;IAC/C,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,OAAO,EAAE,OAAO,CAAC;IACjB,SAAS,EAAE,MAAM,IAAI,CAAC;CACvB;AAoBD,wBAAgB,oBAAoB,CAAC,EACnC,KAAK,EACL,SAAS,EACT,OAAO,EACP,SAAS,GACV,EAAE,yBAAyB,kDAiC3B"}
@@ -1 +1 @@
1
- {"version":3,"file":"EditorShell.d.ts","sourceRoot":"","sources":["../../../src/components/shell/EditorShell.tsx"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAYjD,OAAO,sBAAsB,CAAC;AAiC9B,OAAO,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AAcxD,UAAU,KAAK;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,QAAQ,EAAE,CAAC;IACtB,YAAY,EAAE;QACZ,KAAK,EAAE,OAAO,CAAC;QACf,aAAa,EAAE,OAAO,CAAC;QACvB,YAAY,EAAE,OAAO,CAAC;QACtB,cAAc,EAAE,OAAO,CAAC;QACxB,kBAAkB,EAAE,OAAO,CAAC;QAC5B,cAAc,EAAE,OAAO,CAAC;KACzB,CAAC;IACF,WAAW,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,OAAO,GAAG,QAAQ,CAAA;KAAE,GAAG,IAAI,CAAC;CACjE;AAED,MAAM,CAAC,OAAO,UAAU,WAAW,CAAC,EAClC,OAAO,EACP,MAAM,EACN,SAAS,EAAE,gBAAgB,EAC3B,YAAY,EACZ,WAAW,GACZ,EAAE,KAAK,2CAkkBP"}
1
+ {"version":3,"file":"EditorShell.d.ts","sourceRoot":"","sources":["../../../src/components/shell/EditorShell.tsx"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAYjD,OAAO,sBAAsB,CAAC;AAmC9B,OAAO,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AAcxD,UAAU,KAAK;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,QAAQ,EAAE,CAAC;IACtB,YAAY,EAAE;QACZ,KAAK,EAAE,OAAO,CAAC;QACf,aAAa,EAAE,OAAO,CAAC;QACvB,YAAY,EAAE,OAAO,CAAC;QACtB,cAAc,EAAE,OAAO,CAAC;QACxB,kBAAkB,EAAE,OAAO,CAAC;QAC5B,cAAc,EAAE,OAAO,CAAC;KACzB,CAAC;IACF,WAAW,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,OAAO,GAAG,QAAQ,CAAA;KAAE,GAAG,IAAI,CAAC;CACjE;AAED,MAAM,CAAC,OAAO,UAAU,WAAW,CAAC,EAClC,OAAO,EACP,MAAM,EACN,SAAS,EAAE,gBAAgB,EAC3B,YAAY,EACZ,WAAW,GACZ,EAAE,KAAK,2CAykBP"}
@@ -1,4 +1,5 @@
1
1
  export { useActiveHeadings } from "./useActiveHeadings";
2
+ export { useBuildStatus } from "./useBuildStatus";
2
3
  export { useContentLifecycle } from "./useContentLifecycle";
3
4
  export { useEditorPersistence } from "./useEditorPersistence";
4
5
  export { useEditorPublish } from "./useEditorPublish";
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/hooks/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAC;AACxD,OAAO,EAAE,mBAAmB,EAAE,MAAM,uBAAuB,CAAC;AAC5D,OAAO,EAAE,oBAAoB,EAAE,MAAM,wBAAwB,CAAC;AAC9D,OAAO,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;AACtD,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAC9C,OAAO,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;AACtD,OAAO,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/hooks/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAC;AACxD,OAAO,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAClD,OAAO,EAAE,mBAAmB,EAAE,MAAM,uBAAuB,CAAC;AAC5D,OAAO,EAAE,oBAAoB,EAAE,MAAM,wBAAwB,CAAC;AAC9D,OAAO,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;AACtD,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAC9C,OAAO,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;AACtD,OAAO,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAC"}
@@ -0,0 +1,11 @@
1
+ type BuildState = "idle" | "building" | "ready" | "error";
2
+ interface BuildStatusResult {
3
+ state: BuildState;
4
+ deployUrl: string | null;
5
+ visible: boolean;
6
+ dismiss: () => void;
7
+ startTracking: () => void;
8
+ }
9
+ export declare function useBuildStatus(): BuildStatusResult;
10
+ export {};
11
+ //# sourceMappingURL=useBuildStatus.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useBuildStatus.d.ts","sourceRoot":"","sources":["../../src/hooks/useBuildStatus.ts"],"names":[],"mappings":"AAEA,KAAK,UAAU,GAAG,MAAM,GAAG,UAAU,GAAG,OAAO,GAAG,OAAO,CAAC;AAS1D,UAAU,iBAAiB;IACzB,KAAK,EAAE,UAAU,CAAC;IAClB,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,OAAO,EAAE,OAAO,CAAC;IACjB,OAAO,EAAE,MAAM,IAAI,CAAC;IACpB,aAAa,EAAE,MAAM,IAAI,CAAC;CAC3B;AAKD,wBAAgB,cAAc,IAAI,iBAAiB,CAoHlD"}
@@ -16,8 +16,9 @@ interface PublishDeps {
16
16
  pendingMediaDeletions: string[];
17
17
  onMediaPublished: (publishedItems: MediaItem[], publishedDeletions: string[]) => void;
18
18
  onShasUpdated: (savedSha: string | null, mainSha: string | null) => void;
19
+ onPublishComplete?: () => void;
19
20
  }
20
- export declare function useEditorPublish({ flushNow, cancelPendingFlush, isConfigDirty, clearConfigDirty, siteIndexRef, siteConfig, sections, deletedSectionIds, onSuccess, mediaManifest, pendingMediaItems, pendingMediaDeletions, onMediaPublished, onShasUpdated, }: PublishDeps): {
21
+ export declare function useEditorPublish({ flushNow, cancelPendingFlush, isConfigDirty, clearConfigDirty, siteIndexRef, siteConfig, sections, deletedSectionIds, onSuccess, mediaManifest, pendingMediaItems, pendingMediaDeletions, onMediaPublished, onShasUpdated, onPublishComplete, }: PublishDeps): {
21
22
  isPublishing: boolean;
22
23
  publishFeedback: string | null;
23
24
  handleSave: () => Promise<void>;
@@ -1 +1 @@
1
- {"version":3,"file":"useEditorPublish.d.ts","sourceRoot":"","sources":["../../src/hooks/useEditorPublish.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,SAAS,EAAE,UAAU,EAAE,MAAM,wBAAwB,CAAC;AACpE,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AACnD,OAAO,KAAK,EAAE,aAAa,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAe/D,UAAU,WAAW;IACnB,QAAQ,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IAC9B,kBAAkB,EAAE,MAAM,IAAI,CAAC;IAC/B,aAAa,EAAE,MAAM,OAAO,CAAC;IAC7B,gBAAgB,EAAE,MAAM,IAAI,CAAC;IAC7B,YAAY,EAAE,KAAK,CAAC,SAAS,CAAC,SAAS,CAAC,CAAC;IACzC,UAAU,EAAE,UAAU,GAAG,IAAI,CAAC;IAC9B,QAAQ,EAAE,aAAa,EAAE,CAAC;IAC1B,iBAAiB,CAAC,EAAE,MAAM,EAAE,CAAC;IAC7B,SAAS,EAAE,MAAM,IAAI,CAAC;IACtB,aAAa,EAAE,aAAa,CAAC;IAC7B,iBAAiB,EAAE,SAAS,EAAE,CAAC;IAC/B,qBAAqB,EAAE,MAAM,EAAE,CAAC;IAChC,gBAAgB,EAAE,CAAC,cAAc,EAAE,SAAS,EAAE,EAAE,kBAAkB,EAAE,MAAM,EAAE,KAAK,IAAI,CAAC;IACtF,aAAa,EAAE,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI,EAAE,OAAO,EAAE,MAAM,GAAG,IAAI,KAAK,IAAI,CAAC;CAC1E;AASD,wBAAgB,gBAAgB,CAAC,EAC/B,QAAQ,EACR,kBAAkB,EAClB,aAAa,EACb,gBAAgB,EAChB,YAAY,EACZ,UAAU,EACV,QAAQ,EACR,iBAAiB,EACjB,SAAS,EACT,aAAa,EACb,iBAAiB,EACjB,qBAAqB,EACrB,gBAAgB,EAChB,aAAa,GACd,EAAE,WAAW;;;;;;EAqSb"}
1
+ {"version":3,"file":"useEditorPublish.d.ts","sourceRoot":"","sources":["../../src/hooks/useEditorPublish.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,SAAS,EAAE,UAAU,EAAE,MAAM,wBAAwB,CAAC;AACpE,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AACnD,OAAO,KAAK,EAAE,aAAa,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAe/D,UAAU,WAAW;IACnB,QAAQ,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IAC9B,kBAAkB,EAAE,MAAM,IAAI,CAAC;IAC/B,aAAa,EAAE,MAAM,OAAO,CAAC;IAC7B,gBAAgB,EAAE,MAAM,IAAI,CAAC;IAC7B,YAAY,EAAE,KAAK,CAAC,SAAS,CAAC,SAAS,CAAC,CAAC;IACzC,UAAU,EAAE,UAAU,GAAG,IAAI,CAAC;IAC9B,QAAQ,EAAE,aAAa,EAAE,CAAC;IAC1B,iBAAiB,CAAC,EAAE,MAAM,EAAE,CAAC;IAC7B,SAAS,EAAE,MAAM,IAAI,CAAC;IACtB,aAAa,EAAE,aAAa,CAAC;IAC7B,iBAAiB,EAAE,SAAS,EAAE,CAAC;IAC/B,qBAAqB,EAAE,MAAM,EAAE,CAAC;IAChC,gBAAgB,EAAE,CAAC,cAAc,EAAE,SAAS,EAAE,EAAE,kBAAkB,EAAE,MAAM,EAAE,KAAK,IAAI,CAAC;IACtF,aAAa,EAAE,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI,EAAE,OAAO,EAAE,MAAM,GAAG,IAAI,KAAK,IAAI,CAAC;IACzE,iBAAiB,CAAC,EAAE,MAAM,IAAI,CAAC;CAChC;AASD,wBAAgB,gBAAgB,CAAC,EAC/B,QAAQ,EACR,kBAAkB,EAClB,aAAa,EACb,gBAAgB,EAChB,YAAY,EACZ,UAAU,EACV,QAAQ,EACR,iBAAiB,EACjB,SAAS,EACT,aAAa,EACb,iBAAiB,EACjB,qBAAqB,EACrB,gBAAgB,EAChB,aAAa,EACb,iBAAiB,GAClB,EAAE,WAAW;;;;;;EAuSb"}
package/dist/index.js CHANGED
@@ -7,7 +7,7 @@ import {
7
7
  SessionSchema,
8
8
  SiteUserSchema,
9
9
  slugifyAudienceName
10
- } from "./chunk-2YYC2VJY.js";
10
+ } from "./chunk-P24YUT3O.js";
11
11
  import {
12
12
  buildGoogleFontsUrl,
13
13
  cn,
@@ -27,7 +27,7 @@ import {
27
27
  safeRedirect,
28
28
  sanitizeHtml,
29
29
  toSectionId
30
- } from "./chunk-Q7OKHD6I.js";
30
+ } from "./chunk-46QI4FDZ.js";
31
31
  import {
32
32
  ColorItemSchema,
33
33
  ColorSpaceSchema,
@@ -48,7 +48,7 @@ import {
48
48
  getSectionSchema,
49
49
  registerSchema,
50
50
  registerSection
51
- } from "./chunk-PHCEJP7I.js";
51
+ } from "./chunk-EAEX6DS7.js";
52
52
  import {
53
53
  AUDIENCE_COOKIE,
54
54
  LastOwnerError,
package/dist/lib/index.js CHANGED
@@ -17,7 +17,7 @@ import {
17
17
  safeRedirect,
18
18
  sanitizeHtml,
19
19
  toSectionId
20
- } from "../chunk-Q7OKHD6I.js";
20
+ } from "../chunk-46QI4FDZ.js";
21
21
  import {
22
22
  clearRegistry,
23
23
  createRegistry,
@@ -28,7 +28,7 @@ import {
28
28
  getSection,
29
29
  registerSchema,
30
30
  registerSection
31
- } from "../chunk-PHCEJP7I.js";
31
+ } from "../chunk-EAEX6DS7.js";
32
32
  import "../chunk-F3Z6RISI.js";
33
33
  import "../chunk-UMSFICAC.js";
34
34
  export {
@@ -7,7 +7,7 @@ import {
7
7
  SessionSchema,
8
8
  SiteUserSchema,
9
9
  slugifyAudienceName
10
- } from "../chunk-2YYC2VJY.js";
10
+ } from "../chunk-P24YUT3O.js";
11
11
  import {
12
12
  ColorItemSchema,
13
13
  ColorSpaceSchema,
@@ -19,7 +19,7 @@ import {
19
19
  TextLineSchema,
20
20
  getSectionContentSchema,
21
21
  getSectionSchema
22
- } from "../chunk-PHCEJP7I.js";
22
+ } from "../chunk-EAEX6DS7.js";
23
23
  import {
24
24
  ImageManifestSchema,
25
25
  MediaConfigSchema,
@@ -1,11 +1,12 @@
1
1
  import { z } from "zod";
2
2
  export declare const SectionMetaSchema: z.ZodObject<{
3
3
  type: z.ZodString;
4
- status: z.ZodEnum<{
4
+ status: z.ZodPipe<z.ZodEnum<{
5
5
  draft: "draft";
6
6
  live: "live";
7
7
  archived: "archived";
8
- }>;
8
+ published: "published";
9
+ }>, z.ZodTransform<"draft" | "live" | "archived", "draft" | "live" | "archived" | "published">>;
9
10
  access: z.ZodArray<z.ZodString>;
10
11
  }, z.core.$strip>;
11
12
  export type SectionMeta = z.infer<typeof SectionMetaSchema>;
@@ -14,11 +15,12 @@ export declare const IndexSchema: z.ZodObject<{
14
15
  order: z.ZodArray<z.ZodString>;
15
16
  sections: z.ZodRecord<z.ZodString, z.ZodObject<{
16
17
  type: z.ZodString;
17
- status: z.ZodEnum<{
18
+ status: z.ZodPipe<z.ZodEnum<{
18
19
  draft: "draft";
19
20
  live: "live";
20
21
  archived: "archived";
21
- }>;
22
+ published: "published";
23
+ }>, z.ZodTransform<"draft" | "live" | "archived", "draft" | "live" | "archived" | "published">>;
22
24
  access: z.ZodArray<z.ZodString>;
23
25
  }, z.core.$strip>>;
24
26
  }, z.core.$strip>;
@@ -1 +1 @@
1
- {"version":3,"file":"site-config.d.ts","sourceRoot":"","sources":["../../src/schemas/site-config.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAIxB,eAAO,MAAM,iBAAiB;;;;;;;;iBAI5B,CAAC;AAEH,MAAM,MAAM,WAAW,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,iBAAiB,CAAC,CAAC;AAE5D,eAAO,MAAM,WAAW;;;;;;;;;;;;iBAOvB,CAAC;AAEF,MAAM,MAAM,SAAS,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,WAAW,CAAC,CAAC;AAEpD,eAAO,MAAM,gBAAgB;;;;;;;;;;;;;;;;;;;;iBAY3B,CAAC;AAEH,MAAM,MAAM,UAAU,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,gBAAgB,CAAC,CAAC"}
1
+ {"version":3,"file":"site-config.d.ts","sourceRoot":"","sources":["../../src/schemas/site-config.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAQxB,eAAO,MAAM,iBAAiB;;;;;;;;;iBAI5B,CAAC;AAEH,MAAM,MAAM,WAAW,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,iBAAiB,CAAC,CAAC;AAE5D,eAAO,MAAM,WAAW;;;;;;;;;;;;;iBAOvB,CAAC;AAEF,MAAM,MAAM,SAAS,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,WAAW,CAAC,CAAC;AAEpD,eAAO,MAAM,gBAAgB;;;;;;;;;;;;;;;;;;;;iBAY3B,CAAC;AAEH,MAAM,MAAM,UAAU,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,gBAAgB,CAAC,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@drawnagency/primitives",
3
- "version": "0.1.12",
3
+ "version": "0.1.14",
4
4
  "type": "module",
5
5
  "exports": {
6
6
  "./package.json": "./package.json",
@@ -1,22 +1,18 @@
1
- import { useRef, useState } from "react";
2
1
  import { Check } from "lucide-react";
3
2
  import { cn } from "../../lib/cn";
4
- import { Popover } from "../shared/Popover";
3
+ import { IndicatorPill } from "./IndicatorPill";
5
4
  import { PopoverItem } from "../shared/PopoverItem";
6
5
  import type { Audience } from "../../auth/types";
7
6
 
8
- interface Props {
7
+ interface AudienceIndicatorProps {
9
8
  access: string[];
10
9
  audiences: Audience[];
11
10
  onChange: (access: string[]) => void;
12
11
  }
13
12
 
14
- export function AudiencePicker({ access, audiences, onChange }: Props) {
15
- const [open, setOpen] = useState(false);
16
- const buttonRef = useRef<HTMLButtonElement>(null);
17
-
13
+ export function AudienceIndicator({ access, audiences, onChange }: AudienceIndicatorProps) {
18
14
  const selected = audiences.filter((a) => access.includes(a.name));
19
- const visibleCircles = selected.slice(0, 3);
15
+ const visibleDots = selected.slice(0, 3);
20
16
 
21
17
  function toggle(name: string) {
22
18
  const next = access.includes(name)
@@ -25,47 +21,27 @@ export function AudiencePicker({ access, audiences, onChange }: Props) {
25
21
  onChange(next);
26
22
  }
27
23
 
28
- return (
29
- <div className="relative">
30
- <button
31
- ref={buttonRef}
32
- type="button"
33
- onClick={() => setOpen((v) => !v)}
34
- className={cn(
35
- "cursor-pointer inline-flex items-center gap-1.5 rounded-full px-2 py-0.5 text-xs font-medium",
36
- "border border-base-200 hover:bg-base-accent",
37
- selected.length === 0 && "text-base-contrast-light",
38
- )}
39
- >
40
- {selected.length === 0 && <span>No audience</span>}
41
- {selected.length >= 1 && (
42
- <span className="flex -space-x-1.5">
43
- {visibleCircles.map((a) => (
44
- <span
45
- key={a.name}
46
- className="h-3 w-3 rounded-full border border-base"
47
- style={{ backgroundColor: a.color ?? "#9ca3af" }}
48
- />
49
- ))}
50
- </span>
51
- )}
52
- {selected.length === 1 && <span>{selected[0].displayName}</span>}
53
- {selected.length >= 2 && <span>{selected.length} audiences</span>}
54
- </button>
24
+ const pillDots = visibleDots.map((a) => ({
25
+ color: a.color ?? "#9ca3af",
26
+ }));
55
27
 
56
- <Popover
57
- isOpen={open}
58
- onClose={() => setOpen(false)}
59
- anchorRef={buttonRef}
60
- align="end"
61
- className="w-56"
62
- >
63
- {audiences.length === 0 ? (
64
- <div className="px-3 py-2 text-xs text-base-contrast-light">
28
+ let labelText: string;
29
+ if (selected.length === 0) labelText = "No audience";
30
+ else if (selected.length === 1) labelText = selected[0].displayName;
31
+ else labelText = `${selected.length} audiences`;
32
+
33
+ return (
34
+ <IndicatorPill
35
+ dots={pillDots}
36
+ label={labelText}
37
+ buttonClassName={selected.length === 0 ? "text-base-contrast-light" : undefined}
38
+ clickContent={
39
+ audiences.length === 0 ? (
40
+ <div className="px-3 py-2 text-xs text-base-contrast-light whitespace-nowrap">
65
41
  No audiences configured. Add audiences in Site Settings → Viewer Access.
66
42
  </div>
67
43
  ) : (
68
- <ul role="list" className="py-1">
44
+ <ul role="list" className="w-full overflow-hidden py-1">
69
45
  {audiences.map((a) => {
70
46
  const checked = access.includes(a.name);
71
47
  return (
@@ -90,14 +66,14 @@ export function AudiencePicker({ access, audiences, onChange }: Props) {
90
66
  className="h-3 w-3 shrink-0 rounded-full border border-base-200"
91
67
  style={{ backgroundColor: a.color ?? "#9ca3af" }}
92
68
  />
93
- <span className="text-sm font-medium text-base-contrast">{a.displayName}</span>
69
+ <span className="font-medium text-base-contrast">{a.displayName}</span>
94
70
  </PopoverItem>
95
71
  </li>
96
72
  );
97
73
  })}
98
74
  </ul>
99
- )}
100
- </Popover>
101
- </div>
75
+ )
76
+ }
77
+ />
102
78
  );
103
79
  }
@@ -0,0 +1,119 @@
1
+ import { useRef, useState, useCallback, type ReactNode } from "react";
2
+ import { cn } from "../../lib/cn";
3
+ import { Popover } from "../shared/Popover";
4
+
5
+ interface DotDef {
6
+ color?: string;
7
+ className?: string;
8
+ }
9
+
10
+ interface IndicatorPillProps {
11
+ dots: DotDef[];
12
+ label: string;
13
+ /** Shown on hover above the pill. Currently unused — kept for planned tooltip feature. */
14
+ hoverContent?: ReactNode;
15
+ clickContent?: ReactNode | ((onClose: () => void) => ReactNode);
16
+ className?: string;
17
+ buttonClassName?: string;
18
+ ariaLabel?: string;
19
+ }
20
+
21
+ export function IndicatorPill({
22
+ dots,
23
+ label,
24
+ hoverContent,
25
+ clickContent,
26
+ className,
27
+ buttonClassName,
28
+ ariaLabel,
29
+ }: IndicatorPillProps) {
30
+ const [hoverOpen, setHoverOpen] = useState(false);
31
+ const [clickOpen, setClickOpen] = useState(false);
32
+ const buttonRef = useRef<HTMLButtonElement>(null);
33
+ const hoverTimeout = useRef<ReturnType<typeof setTimeout> | null>(null);
34
+
35
+ const clearHoverTimeout = useCallback(() => {
36
+ if (hoverTimeout.current) {
37
+ clearTimeout(hoverTimeout.current);
38
+ hoverTimeout.current = null;
39
+ }
40
+ }, []);
41
+
42
+ function handleMouseEnter() {
43
+ clearHoverTimeout();
44
+ if (!clickOpen && hoverContent) {
45
+ setHoverOpen(true);
46
+ }
47
+ }
48
+
49
+ function handleMouseLeave() {
50
+ clearHoverTimeout();
51
+ hoverTimeout.current = setTimeout(() => {
52
+ setHoverOpen(false);
53
+ }, 150);
54
+ }
55
+
56
+ function handleClick() {
57
+ if (!clickContent) return;
58
+ setHoverOpen(false);
59
+ clearHoverTimeout();
60
+ setClickOpen((v) => !v);
61
+ }
62
+
63
+ function handleClickClose() {
64
+ setClickOpen(false);
65
+ }
66
+
67
+ return (
68
+ <div className={cn("relative", className)}>
69
+ <button
70
+ ref={buttonRef}
71
+ type="button"
72
+ onClick={handleClick}
73
+ onMouseEnter={handleMouseEnter}
74
+ onMouseLeave={handleMouseLeave}
75
+ aria-haspopup={clickContent ? "true" : undefined}
76
+ aria-expanded={clickContent ? clickOpen : undefined}
77
+ aria-label={ariaLabel}
78
+ className={cn(
79
+ "cursor-pointer inline-flex items-center gap-1.5 rounded-full px-2 py-0.5 text-xs font-medium",
80
+ "border border-base-200 hover:bg-base-accent",
81
+ buttonClassName,
82
+ )}
83
+ >
84
+ {dots.length > 0 && (
85
+ <span className="flex -space-x-1">
86
+ {dots.map((dot, i) => (
87
+ <span
88
+ key={i}
89
+ aria-hidden="true"
90
+ className={cn("h-3 w-3 rounded-full border border-base", dot.className)}
91
+ style={dot.color ? { backgroundColor: dot.color } : undefined}
92
+ />
93
+ ))}
94
+ </span>
95
+ )}
96
+ <span>{label}</span>
97
+ </button>
98
+
99
+ {/* Hover tooltip — above the button */}
100
+ {hoverOpen && hoverContent && (
101
+ <div
102
+ className="absolute bottom-full right-0 z-50 mb-1 rounded-full bg-base-accent px-3 py-1 text-xs text-base-contrast-light whitespace-nowrap"
103
+ >
104
+ {hoverContent}
105
+ </div>
106
+ )}
107
+
108
+ {/* Click dropdown — below the button */}
109
+ <Popover
110
+ isOpen={clickOpen}
111
+ onClose={handleClickClose}
112
+ anchorRef={buttonRef}
113
+ className="min-w-full max-w-xs"
114
+ >
115
+ {typeof clickContent === "function" ? clickContent(handleClickClose) : clickContent}
116
+ </Popover>
117
+ </div>
118
+ );
119
+ }