@pihanga2/shadcn 0.2.1 → 0.2.2

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 (108) hide show
  1. package/AGENT.building-cards.md +208 -3
  2. package/AGENT.md +58 -3
  3. package/AGENT.using-cards.md +142 -4
  4. package/README.md +4 -1
  5. package/cards/badge/badge.component.js +1 -0
  6. package/cards/badge/badge.component.js.map +1 -1
  7. package/cards/box/box.component.js +1 -1
  8. package/cards/box/box.component.js.map +1 -1
  9. package/cards/box/box.types.d.ts +0 -8
  10. package/cards/box/box.types.js.map +1 -1
  11. package/cards/button/button.types.d.ts +1 -0
  12. package/cards/button/button.types.js.map +1 -1
  13. package/cards/{chartGraph/chartGraph.component.d.ts → chart/chart.component.d.ts} +1 -1
  14. package/cards/chart/chart.types.d.ts +223 -0
  15. package/cards/chart/index.d.ts +1 -0
  16. package/cards/checkbox/checkbox.component.js +1 -0
  17. package/cards/checkbox/checkbox.component.js.map +1 -1
  18. package/cards/checkbox/checkbox.types.d.ts +4 -0
  19. package/cards/checkbox/checkbox.types.js +2 -2
  20. package/cards/checkbox/checkbox.types.js.map +1 -1
  21. package/cards/checkbox/index.js +5 -5
  22. package/cards/conditional/conditional.types.d.ts +20 -5
  23. package/cards/conditional/conditional.types.js.map +1 -1
  24. package/cards/core-index.d.ts +3 -0
  25. package/cards/core-index.js +36 -29
  26. package/cards/dataTable/dataTable.types.js.map +1 -1
  27. package/cards/dataTable/dataTableRowDetail.component.js +1 -0
  28. package/cards/dataTable/dataTableRowDetail.component.js.map +1 -1
  29. package/cards/field/field.types.js.map +1 -1
  30. package/cards/fileDrop/fileDrop.component.js +90 -0
  31. package/cards/fileDrop/fileDrop.component.js.map +1 -0
  32. package/cards/fileDrop/fileDrop.types.js +11 -0
  33. package/cards/fileDrop/fileDrop.types.js.map +1 -0
  34. package/cards/fileDrop/index.js +13 -0
  35. package/cards/fileDrop/index.js.map +1 -0
  36. package/cards/flexGrid/flexGrid.types.js.map +1 -1
  37. package/cards/form/form.types.d.ts +4 -0
  38. package/cards/form/form.types.js +2 -2
  39. package/cards/form/form.types.js.map +1 -1
  40. package/cards/form/index.js +5 -5
  41. package/cards/graphin/graphin.types.d.ts +37 -4
  42. package/cards/input/index.js +5 -5
  43. package/cards/input/input.component.js +1 -0
  44. package/cards/input/input.component.js.map +1 -1
  45. package/cards/input/input.types.d.ts +10 -2
  46. package/cards/input/input.types.js +2 -2
  47. package/cards/input/input.types.js.map +1 -1
  48. package/cards/list/list.types.js +1 -0
  49. package/cards/list/list.types.js.map +1 -1
  50. package/cards/pageWithNavbar/pageWithNavbar.component.d.ts +5 -7
  51. package/cards/pageWithNavbar/pageWithNavbar.component.js +1 -1
  52. package/cards/pageWithNavbar/pageWithNavbar.component.js.map +1 -1
  53. package/cards/pasteTarget/pasteTarget.component.js.map +1 -1
  54. package/cards/select/index.js +5 -5
  55. package/cards/select/select.component.js +1 -0
  56. package/cards/select/select.component.js.map +1 -1
  57. package/cards/select/select.types.d.ts +12 -0
  58. package/cards/select/select.types.js +2 -2
  59. package/cards/select/select.types.js.map +1 -1
  60. package/cards/slider/index.js +13 -0
  61. package/cards/slider/index.js.map +1 -0
  62. package/cards/slider/slider.component.js +64 -0
  63. package/cards/slider/slider.component.js.map +1 -0
  64. package/cards/slider/slider.types.d.ts +12 -4
  65. package/cards/slider/slider.types.js +7 -0
  66. package/cards/slider/slider.types.js.map +1 -0
  67. package/cards/sliderValue/index.js +12 -0
  68. package/cards/sliderValue/index.js.map +1 -0
  69. package/cards/sliderValue/sliderValue.component.js +33 -0
  70. package/cards/sliderValue/sliderValue.component.js.map +1 -0
  71. package/cards/sliderValue/sliderValue.types.js +7 -0
  72. package/cards/sliderValue/sliderValue.types.js.map +1 -0
  73. package/cards/stack/stack.component.js +1 -1
  74. package/cards/stack/stack.component.js.map +1 -1
  75. package/cards/stack/stack.types.js.map +1 -1
  76. package/cards/switch/index.js +5 -5
  77. package/cards/switch/switch.component.js +1 -0
  78. package/cards/switch/switch.component.js.map +1 -1
  79. package/cards/switch/switch.types.d.ts +4 -0
  80. package/cards/switch/switch.types.js +2 -2
  81. package/cards/switch/switch.types.js.map +1 -1
  82. package/cards/textField/index.js +5 -5
  83. package/cards/textField/textField.component.js +1 -0
  84. package/cards/textField/textField.component.js.map +1 -1
  85. package/cards/textField/textField.types.d.ts +4 -0
  86. package/cards/textField/textField.types.js +2 -2
  87. package/cards/textField/textField.types.js.map +1 -1
  88. package/cards/toast/index.js +7 -7
  89. package/cards/toast/toast.component.js.map +1 -1
  90. package/cards/toast/toast.types.d.ts +4 -0
  91. package/cards/toast/toast.types.js +2 -2
  92. package/cards/toast/toast.types.js.map +1 -1
  93. package/cards/toggleGroup/index.js +5 -5
  94. package/cards/toggleGroup/toggleGroup.component.js +1 -0
  95. package/cards/toggleGroup/toggleGroup.component.js.map +1 -1
  96. package/cards/toggleGroup/toggleGroup.types.d.ts +4 -0
  97. package/cards/toggleGroup/toggleGroup.types.js +2 -2
  98. package/cards/toggleGroup/toggleGroup.types.js.map +1 -1
  99. package/cards/typography/typography.types.js +1 -0
  100. package/cards/typography/typography.types.js.map +1 -1
  101. package/components/ui/slider.js +36 -0
  102. package/components/ui/slider.js.map +1 -0
  103. package/components/ui/toggle-group.js +1 -0
  104. package/components/ui/toggle-group.js.map +1 -1
  105. package/package.json +19 -2
  106. package/pihanga-shadcn.css +1 -1
  107. package/cards/chartGraph/chartGraph.types.d.ts +0 -102
  108. package/cards/chartGraph/index.d.ts +0 -1
@@ -1 +1 @@
1
- {"version":3,"file":"field.types.js","names":[],"sources":["../../../src/cards/field/field.types.ts"],"sourcesContent":["import {createCardDeclaration, PiCardRef} from \"@pihanga2/core\";\n\nexport const PI_FIELD_CARD = \"pi/field\";\n\nexport const Field = createCardDeclaration<PiFieldProps>(PI_FIELD_CARD);\n\nexport type PiFieldProps = {\n /** Visible label rendered above the control. */\n label: string;\n\n /**\n * The Pihanga card reference for the plain control to render\n * (e.g. pi/text-field, pi/select, pi/checkbox, …).\n *\n * The field card will forward an `id` prop and an `invalid` boolean to the\n * inner card via Pihanga's standard prop-passing mechanism so the control\n * can wire up accessibility attributes without needing its own label logic.\n */\n fieldCard: PiCardRef;\n\n /**\n * Field name — when provided the card reads the current error from\n * FormContext (when inside a pi/form) to decide whether to show an error.\n */\n name?: string;\n\n /** Secondary help text rendered below the control. */\n description?: string;\n\n /**\n * Static error message. Overridden by the form-context error when `name`\n * is provided and the card is inside a pi/form.\n */\n error?: string;\n\n /** Additional CSS classes applied to the outer Field wrapper. */\n className?: string;\n};\n"],"mappings":";;AAEA,IAAa,IAAgB,YAEhB,IAAQ,EAAoC,CAAa"}
1
+ {"version":3,"file":"field.types.js","names":[],"sources":["../../../src/cards/field/field.types.ts"],"sourcesContent":["import {createCardDeclaration, type PiCardRef} from \"@pihanga2/core\";\n\nexport const PI_FIELD_CARD = \"pi/field\";\n\nexport const Field = createCardDeclaration<PiFieldProps>(PI_FIELD_CARD);\n\nexport type PiFieldProps = {\n /** Visible label rendered above the control. */\n label: string;\n\n /**\n * The Pihanga card reference for the plain control to render\n * (e.g. pi/text-field, pi/select, pi/checkbox, …).\n *\n * The field card will forward an `id` prop and an `invalid` boolean to the\n * inner card via Pihanga's standard prop-passing mechanism so the control\n * can wire up accessibility attributes without needing its own label logic.\n */\n fieldCard: PiCardRef;\n\n /**\n * Field name — when provided the card reads the current error from\n * FormContext (when inside a pi/form) to decide whether to show an error.\n */\n name?: string;\n\n /** Secondary help text rendered below the control. */\n description?: string;\n\n /**\n * Static error message. Overridden by the form-context error when `name`\n * is provided and the card is inside a pi/form.\n */\n error?: string;\n\n /** Additional CSS classes applied to the outer Field wrapper. */\n className?: string;\n};\n"],"mappings":";;AAEA,IAAa,IAAgB,YAEhB,IAAQ,EAAoC,CAAa"}
@@ -0,0 +1,90 @@
1
+ import { k as e } from "../../node_modules/react-drag-drop-files/dist/index.es.js";
2
+ import { DEF_FILE_DROP_FILE_TYPES as t } from "./fileDrop.types.js";
3
+ /* empty css */
4
+ import "react";
5
+ import { jsx as n, jsxs as r } from "react/jsx-runtime";
6
+ //#region src/cards/fileDrop/fileDrop.component.tsx
7
+ var i = Symbol.for("pihanga.card.FileDrop.LastDropped"), a = globalThis;
8
+ function o(e) {
9
+ let t = a[i] ??= null;
10
+ return t?.name === e ? t.file : null;
11
+ }
12
+ function s(e) {
13
+ a[i] = e;
14
+ }
15
+ function c() {
16
+ a[i] = null;
17
+ }
18
+ var l = (i) => {
19
+ let { fileTypes: a = t, title: o = "Click or drop a file right here", description: l, showProgress: u = !1, progress: d = 0, progressStyle: f = {}, dropStyle: p = {}, onFileDropped: m, onError: h, cardName: g, className: _, _cls: v } = i;
20
+ function y(e) {
21
+ let t = Array.isArray(e) ? e[0] : e;
22
+ if (!t) return;
23
+ let { name: n, size: r, type: i } = t;
24
+ s({
25
+ name: n,
26
+ file: t
27
+ }), m({
28
+ name: n,
29
+ size: r,
30
+ type: i
31
+ }), setTimeout(c, 2e3);
32
+ }
33
+ function b(e) {
34
+ h({ error: String(e) });
35
+ }
36
+ function x() {
37
+ let e = `${d}%`, t = `${e} Complete`, i = {
38
+ width: "50%",
39
+ ...f
40
+ };
41
+ return /* @__PURE__ */ r("div", {
42
+ className: "pi-progress",
43
+ children: [/* @__PURE__ */ n("div", {
44
+ className: "pi-progress-label",
45
+ children: e
46
+ }), /* @__PURE__ */ n("div", {
47
+ className: "pi-progress-container",
48
+ style: i,
49
+ children: /* @__PURE__ */ n("div", {
50
+ className: "pi-progress-bar",
51
+ style: { width: e },
52
+ role: "progressbar",
53
+ "aria-label": t
54
+ })
55
+ })]
56
+ });
57
+ }
58
+ function S() {
59
+ return /* @__PURE__ */ r("div", {
60
+ className: "dropzone-msg",
61
+ style: p,
62
+ children: [o && /* @__PURE__ */ n("h3", {
63
+ className: "dropzone-msg-title",
64
+ children: o
65
+ }), l && /* @__PURE__ */ n("span", {
66
+ className: "dropzone-msg-desc",
67
+ children: l
68
+ })]
69
+ });
70
+ }
71
+ function C() {
72
+ return /* @__PURE__ */ n(e, {
73
+ handleChange: y,
74
+ onTypeError: b,
75
+ name: "file",
76
+ types: a,
77
+ hoverTitle: " ",
78
+ children: S()
79
+ });
80
+ }
81
+ return /* @__PURE__ */ n("div", {
82
+ className: v("root", _),
83
+ "data-pihanga": g,
84
+ children: u ? x() : C()
85
+ });
86
+ };
87
+ //#endregion
88
+ export { l as FileDropComponent, o as get_last_dropped };
89
+
90
+ //# sourceMappingURL=fileDrop.component.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fileDrop.component.js","names":[],"sources":["../../../src/cards/fileDrop/fileDrop.component.tsx"],"sourcesContent":["import React from \"react\";\nimport type {PiCardProps} from \"@pihanga2/core\";\nimport {FileUploader} from \"react-drag-drop-files\";\nimport {\n DEF_FILE_DROP_FILE_TYPES,\n type FileDropEvents,\n type FileDropProps,\n} from \"./fileDrop.types\";\nimport \"./fileDrop.css\";\n\ntype LastDropped = {name: string; file: File};\n\nconst KEY = Symbol.for(\"pihanga.card.FileDrop.LastDropped\");\n\nconst globalForCache = globalThis as unknown as Record<\n symbol,\n LastDropped | null | undefined\n>;\n\nexport function get_last_dropped(name: string): File | null {\n const slot = (globalForCache[KEY] ??= null);\n if (slot?.name === name) {\n return slot.file;\n }\n return null;\n}\n\nfunction setLastDropped(value: LastDropped): void {\n globalForCache[KEY] = value;\n}\n\nfunction clearLastDropped(): void {\n globalForCache[KEY] = null;\n}\n\nexport const FileDropComponent = (\n props: PiCardProps<FileDropProps, FileDropEvents>,\n): React.ReactNode => {\n const {\n fileTypes = DEF_FILE_DROP_FILE_TYPES,\n title = \"Click or drop a file right here\",\n description,\n showProgress = false,\n progress = 0,\n progressStyle = {},\n dropStyle = {},\n onFileDropped,\n onError,\n cardName,\n className,\n _cls,\n } = props;\n\n function handleChange(file: File | File[]): void {\n const f = Array.isArray(file) ? file[0] : file;\n if (!f) return;\n const {name, size, type} = f;\n setLastDropped({name, file: f});\n onFileDropped({name, size, type});\n // clean up reference to File in a few sec to avoid dangling reference\n setTimeout(clearLastDropped, 2000);\n }\n\n function handleTypeError(err: unknown): void {\n onError({error: String(err)});\n }\n\n function renderProgress(): React.ReactNode {\n const label = `${progress}%`;\n const msg = `${label} Complete`;\n const containerStyle = {width: \"50%\", ...progressStyle};\n return (\n <div className=\"pi-progress\">\n <div className=\"pi-progress-label\">{label}</div>\n <div\n className=\"pi-progress-container\"\n style={containerStyle as React.CSSProperties}\n >\n <div\n className=\"pi-progress-bar\"\n style={{width: label}}\n role=\"progressbar\"\n aria-label={msg}\n />\n </div>\n </div>\n );\n }\n\n function renderDropZone(): React.ReactElement {\n return (\n <div className=\"dropzone-msg\" style={dropStyle as React.CSSProperties}>\n {title && <h3 className=\"dropzone-msg-title\">{title}</h3>}\n {description && (\n <span className=\"dropzone-msg-desc\">{description}</span>\n )}\n </div>\n );\n }\n\n function renderFileUploader(): React.ReactNode {\n return (\n <FileUploader\n handleChange={handleChange}\n onTypeError={handleTypeError}\n name=\"file\"\n types={fileTypes}\n hoverTitle=\" \"\n >\n {renderDropZone()}\n </FileUploader>\n );\n }\n\n const cn = _cls(\"root\", className);\n return (\n <div className={cn} data-pihanga={cardName}>\n {showProgress ? renderProgress() : renderFileUploader()}\n </div>\n );\n};\n"],"mappings":";;;;;;AAYA,IAAM,IAAM,OAAO,IAAI,mCAAmC,GAEpD,IAAiB;AAKvB,SAAgB,EAAiB,GAA2B;CAC1D,IAAM,IAAQ,EAAe,OAAS;CAItC,OAHI,GAAM,SAAS,IACV,EAAK,OAEP;AACT;AAEA,SAAS,EAAe,GAA0B;CAChD,EAAe,KAAO;AACxB;AAEA,SAAS,IAAyB;CAChC,EAAe,KAAO;AACxB;AAEA,IAAa,KACX,MACoB;CACpB,IAAM,EACJ,eAAY,GACZ,WAAQ,mCACR,gBACA,kBAAe,IACf,cAAW,GACX,mBAAgB,CAAC,GACjB,eAAY,CAAC,GACb,kBACA,YACA,aACA,cACA,YACE;CAEJ,SAAS,EAAa,GAA2B;EAC/C,IAAM,IAAI,MAAM,QAAQ,CAAI,IAAI,EAAK,KAAK;EAC1C,IAAI,CAAC,GAAG;EACR,IAAM,EAAC,SAAM,SAAM,YAAQ;EAI3B,AAHA,EAAe;GAAC;GAAM,MAAM;EAAC,CAAC,GAC9B,EAAc;GAAC;GAAM;GAAM;EAAI,CAAC,GAEhC,WAAW,GAAkB,GAAI;CACnC;CAEA,SAAS,EAAgB,GAAoB;EAC3C,EAAQ,EAAC,OAAO,OAAO,CAAG,EAAC,CAAC;CAC9B;CAEA,SAAS,IAAkC;EACzC,IAAM,IAAQ,GAAG,EAAS,IACpB,IAAM,GAAG,EAAM,YACf,IAAiB;GAAC,OAAO;GAAO,GAAG;EAAa;EACtD,OACE,kBAAC,OAAD;GAAK,WAAU;aAAf,CACE,kBAAC,OAAD;IAAK,WAAU;cAAqB;GAAW,CAAA,GAC/C,kBAAC,OAAD;IACE,WAAU;IACV,OAAO;cAEP,kBAAC,OAAD;KACE,WAAU;KACV,OAAO,EAAC,OAAO,EAAK;KACpB,MAAK;KACL,cAAY;IACb,CAAA;GACE,CAAA,CACF;;CAET;CAEA,SAAS,IAAqC;EAC5C,OACE,kBAAC,OAAD;GAAK,WAAU;GAAe,OAAO;aAArC,CACG,KAAS,kBAAC,MAAD;IAAI,WAAU;cAAsB;GAAU,CAAA,GACvD,KACC,kBAAC,QAAD;IAAM,WAAU;cAAqB;GAAkB,CAAA,CAEtD;;CAET;CAEA,SAAS,IAAsC;EAC7C,OACE,kBAAC,GAAD;GACgB;GACd,aAAa;GACb,MAAK;GACL,OAAO;GACP,YAAW;aAEV,EAAe;EACJ,CAAA;CAElB;CAGA,OACE,kBAAC,OAAD;EAAK,WAFI,EAAK,QAAQ,CAEN;EAAI,gBAAc;YAC/B,IAAe,EAAe,IAAI,EAAmB;CACnD,CAAA;AAET"}
@@ -0,0 +1,11 @@
1
+ import { createCardDeclaration as e, createOnAction as t, registerActions as n } from "@pihanga2/core";
2
+ //#region src/cards/fileDrop/fileDrop.types.ts
3
+ var r = "shad/file-drop", i = e(r), a = n(r, ["file_dropped", "error"]), o = t(a.FILE_DROPPED), s = t(a.ERROR), c = [
4
+ "JPG",
5
+ "PNG",
6
+ "GIF"
7
+ ];
8
+ //#endregion
9
+ export { c as DEF_FILE_DROP_FILE_TYPES, a as FILE_DROP_ACTION, r as FILE_DROP_CARD, i as FileDrop, s as onFileDropError, o as onFileDropped };
10
+
11
+ //# sourceMappingURL=fileDrop.types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fileDrop.types.js","names":[],"sources":["../../../src/cards/fileDrop/fileDrop.types.ts"],"sourcesContent":["import {\n createCardDeclaration,\n createOnAction,\n registerActions,\n} from \"@pihanga2/core\";\n\nexport const FILE_DROP_CARD = \"shad/file-drop\";\n\nexport const FileDrop = createCardDeclaration<FileDropProps, FileDropEvents>(\n FILE_DROP_CARD,\n);\n\nexport const FILE_DROP_ACTION = registerActions(FILE_DROP_CARD, [\n \"file_dropped\",\n \"error\",\n]);\n\nexport const onFileDropped = createOnAction<FileDroppedEvent>(\n FILE_DROP_ACTION.FILE_DROPPED,\n);\n\nexport const onFileDropError = createOnAction<FileDropErrorEvent>(\n FILE_DROP_ACTION.ERROR,\n);\n\nexport type FileDropProps<S = Record<string, unknown>> = {\n /** Accepted file extensions, e.g. [\"JPG\", \"PNG\", \"PDF\"]. Defaults to [\"JPG\", \"PNG\", \"GIF\"]. */\n fileTypes?: string[];\n /** Heading shown inside the drop zone. */\n title?: string;\n /** Secondary text shown inside the drop zone. */\n description?: string;\n /** When true, shows a progress bar instead of the drop zone. */\n showProgress?: boolean;\n /** Inline styles applied to the drop zone wrapper. */\n dropStyle?: Record<string, unknown>;\n /** Inline styles applied to the progress bar container. */\n progressStyle?: Record<string, unknown>;\n /** Current upload progress (0–100). Only used when `showProgress` is true. */\n progress?: number;\n\n style?: S;\n className?: string;\n};\n\nexport const DEF_FILE_DROP_FILE_TYPES = [\"JPG\", \"PNG\", \"GIF\"];\n\nexport type FileDroppedEvent = {\n name: string;\n size: number;\n type: string;\n};\n\nexport type FileDropErrorEvent = {\n error: string;\n};\n\nexport type FileDropEvents = {\n onFileDropped: FileDroppedEvent;\n onError: FileDropErrorEvent;\n};\n"],"mappings":";;AAMA,IAAa,IAAiB,kBAEjB,IAAW,EACtB,CACF,GAEa,IAAmB,EAAgB,GAAgB,CAC9D,gBACA,OACF,CAAC,GAEY,IAAgB,EAC3B,EAAiB,YACnB,GAEa,IAAkB,EAC7B,EAAiB,KACnB,GAsBa,IAA2B;CAAC;CAAO;CAAO;AAAK"}
@@ -0,0 +1,13 @@
1
+ import { DEF_FILE_DROP_FILE_TYPES as e, FILE_DROP_ACTION as t, FILE_DROP_CARD as n, FileDrop as r, onFileDropError as i, onFileDropped as a } from "./fileDrop.types.js";
2
+ import { FileDropComponent as o, get_last_dropped as s } from "./fileDrop.component.js";
3
+ import { actionTypesToEvents as c, registerCardComponent as l } from "@pihanga2/core";
4
+ //#region src/cards/fileDrop/index.ts
5
+ l({
6
+ name: n,
7
+ component: o,
8
+ events: c(t)
9
+ });
10
+ //#endregion
11
+ export { e as DEF_FILE_DROP_FILE_TYPES, t as FILE_DROP_ACTION, n as FILE_DROP_CARD, r as FileDrop, s as get_last_dropped, i as onFileDropError, a as onFileDropped };
12
+
13
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","names":[],"sources":["../../../src/cards/fileDrop/index.ts"],"sourcesContent":["import {actionTypesToEvents, registerCardComponent} from \"@pihanga2/core\";\n\nimport {FileDropComponent} from \"./fileDrop.component\";\nimport {FILE_DROP_ACTION, FILE_DROP_CARD} from \"./fileDrop.types\";\n\nexport * from \"./fileDrop.types\";\nexport {get_last_dropped} from \"./fileDrop.component\";\n\nregisterCardComponent({\n name: FILE_DROP_CARD,\n component: FileDropComponent,\n events: actionTypesToEvents(FILE_DROP_ACTION),\n});\n"],"mappings":";;;;AAQA,EAAsB;CACpB,MAAM;CACN,WAAW;CACX,QAAQ,EAAoB,CAAgB;AAC9C,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"flexGrid.types.js","names":[],"sources":["../../../src/cards/flexGrid/flexGrid.types.ts"],"sourcesContent":["import { createCardDeclaration, PiCardRef } from \"@pihanga2/core\"\n\nexport const FLEX_GRID_CARD = \"flex_grid\"\nexport const FlexGrid = createCardDeclaration<FlexGridProps>(FLEX_GRID_CARD)\n\nexport type FlexGridProps = {\n cards: { [name: string]: PiCardRef }\n template: TemplateT\n height?: string\n margin?: string\n overflow?: string\n\n style?: {\n root?: React.CSSProperties\n item?: React.CSSProperties\n }\n className?: string\n}\n\n// body {\n// display: grid;\n// grid-template-areas:\n// \"header header header\"\n// \"nav article ads\"\n// \"footer footer footer\";\n// grid-template-rows: 60px 1fr 60px;\n// grid-template-columns: 20% 1fr 15%;\n// grid-gap: 10px;\n// height: 100vh;\n// margin: 0;\n// }\n\n// https://css-tricks.com/snippets/css/complete-guide-grid/\nexport type TemplateT = {\n area?: string[][] // name of card dict\n rows?: string[] // grid-template-rows (e.g [\"min-content\", \"1fr\", \"min-content\"])\n columns?: string[] // grid-template-cols (e.g. [\"1fr\", \"50px\", \"1fr\", \"1fr\"])\n gap?: string\n}\n"],"mappings":";;AAEA,IAAa,IAAiB,aACjB,IAAW,EAAqC,CAAc"}
1
+ {"version":3,"file":"flexGrid.types.js","names":[],"sources":["../../../src/cards/flexGrid/flexGrid.types.ts"],"sourcesContent":["import {createCardDeclaration, type PiCardRef} from \"@pihanga2/core\";\n\nexport const FLEX_GRID_CARD = \"flex_grid\";\nexport const FlexGrid = createCardDeclaration<FlexGridProps>(FLEX_GRID_CARD);\n\nexport type FlexGridProps = {\n cards: {[name: string]: PiCardRef};\n template: TemplateT;\n height?: string;\n margin?: string;\n overflow?: string;\n\n style?: {\n root?: React.CSSProperties;\n item?: React.CSSProperties;\n };\n className?: string;\n};\n\n// body {\n// display: grid;\n// grid-template-areas:\n// \"header header header\"\n// \"nav article ads\"\n// \"footer footer footer\";\n// grid-template-rows: 60px 1fr 60px;\n// grid-template-columns: 20% 1fr 15%;\n// grid-gap: 10px;\n// height: 100vh;\n// margin: 0;\n// }\n\n// https://css-tricks.com/snippets/css/complete-guide-grid/\nexport type TemplateT = {\n area?: string[][]; // name of card dict\n rows?: string[]; // grid-template-rows (e.g [\"min-content\", \"1fr\", \"min-content\"])\n columns?: string[]; // grid-template-cols (e.g. [\"1fr\", \"50px\", \"1fr\", \"1fr\"])\n gap?: string;\n};\n"],"mappings":";;AAEA,IAAa,IAAiB,aACjB,IAAW,EAAqC,CAAc"}
@@ -4,6 +4,10 @@ export declare const Form: <S extends import('@pihanga2/core').ReduxState>(p: im
4
4
  export declare const PI_FORM_ACTION: {
5
5
  SUBMITTED: string;
6
6
  };
7
+ export declare const onFormSubmitted: <S extends import('@pihanga2/core').ReduxState>(register: import('@pihanga2/core').PiRegister, f: import('@pihanga2/core').ReduceF<S, import('@pihanga2/core').ReduxAction & {
8
+ cardID: string;
9
+ } & PiFormSubmittedEvent>) => void;
10
+ /** @deprecated Use `onFormSubmitted` instead. */
7
11
  export declare const onPiFormSubmitted: <S extends import('@pihanga2/core').ReduxState>(register: import('@pihanga2/core').PiRegister, f: import('@pihanga2/core').ReduceF<S, import('@pihanga2/core').ReduxAction & {
8
12
  cardID: string;
9
13
  } & PiFormSubmittedEvent>) => void;
@@ -1,7 +1,7 @@
1
1
  import { createCardDeclaration as e, createOnAction as t, registerActions as n } from "@pihanga2/core";
2
2
  //#region src/cards/form/form.types.ts
3
- var r = "pi/form", i = e(r), a = n(r, ["submitted"]), o = t(a.SUBMITTED);
3
+ var r = "pi/form", i = e(r), a = n(r, ["submitted"]), o = t(a.SUBMITTED), s = o;
4
4
  //#endregion
5
- export { i as Form, a as PI_FORM_ACTION, r as PI_FORM_CARD, o as onPiFormSubmitted };
5
+ export { i as Form, a as PI_FORM_ACTION, r as PI_FORM_CARD, o as onFormSubmitted, s as onPiFormSubmitted };
6
6
 
7
7
  //# sourceMappingURL=form.types.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"form.types.js","names":[],"sources":["../../../src/cards/form/form.types.ts"],"sourcesContent":["import {\n createCardDeclaration,\n createOnAction,\n type PiCardRef,\n registerActions,\n} from \"@pihanga2/core\";\n\nexport const PI_FORM_CARD = \"pi/form\";\n\nexport const Form = createCardDeclaration<PiFormProps, PiFormEvents>(\n PI_FORM_CARD,\n);\n\nexport const PI_FORM_ACTION = registerActions(PI_FORM_CARD, [\"submitted\"]);\n\nexport const onPiFormSubmitted = createOnAction<PiFormSubmittedEvent>(\n PI_FORM_ACTION.SUBMITTED,\n);\n\nexport type PiFormProps = {\n /**\n * Optional id passed through to events for identification.\n */\n id?: string;\n\n /**\n * Ordered list of field card refs to render inside the form.\n * Each card (e.g. TextField, Checkbox, FormSelect) will be rendered\n * in order and wrapped by a FormContext.Provider so they can read\n * and write shared form state.\n */\n content?: PiCardRef[];\n\n /**\n * Initial values for the form fields, keyed by field name.\n */\n initialValues?: Record<string, unknown>;\n\n /**\n * Label for the submit button.\n * @default \"Submit\"\n */\n submitLabel?: string;\n\n /**\n * Additional CSS classes for the <form> element.\n */\n className?: string;\n};\n\nexport type PiFormSubmittedEvent = {\n id?: string;\n /** Snapshot of form state at the time of submission. */\n formData: Record<string, unknown>;\n};\n\nexport type PiFormEvents = {\n onSubmitted: PiFormSubmittedEvent;\n};\n"],"mappings":";;AAOA,IAAa,IAAe,WAEf,IAAO,EAClB,CACF,GAEa,IAAiB,EAAgB,GAAc,CAAC,WAAW,CAAC,GAE5D,IAAoB,EAC/B,EAAe,SACjB"}
1
+ {"version":3,"file":"form.types.js","names":[],"sources":["../../../src/cards/form/form.types.ts"],"sourcesContent":["import {\n createCardDeclaration,\n createOnAction,\n type PiCardRef,\n registerActions,\n} from \"@pihanga2/core\";\n\nexport const PI_FORM_CARD = \"pi/form\";\n\nexport const Form = createCardDeclaration<PiFormProps, PiFormEvents>(\n PI_FORM_CARD,\n);\n\nexport const PI_FORM_ACTION = registerActions(PI_FORM_CARD, [\"submitted\"]);\n\nexport const onFormSubmitted = createOnAction<PiFormSubmittedEvent>(\n PI_FORM_ACTION.SUBMITTED,\n);\n\n/** @deprecated Use `onFormSubmitted` instead. */\nexport const onPiFormSubmitted = onFormSubmitted;\n\nexport type PiFormProps = {\n /**\n * Optional id passed through to events for identification.\n */\n id?: string;\n\n /**\n * Ordered list of field card refs to render inside the form.\n * Each card (e.g. TextField, Checkbox, FormSelect) will be rendered\n * in order and wrapped by a FormContext.Provider so they can read\n * and write shared form state.\n */\n content?: PiCardRef[];\n\n /**\n * Initial values for the form fields, keyed by field name.\n */\n initialValues?: Record<string, unknown>;\n\n /**\n * Label for the submit button.\n * @default \"Submit\"\n */\n submitLabel?: string;\n\n /**\n * Additional CSS classes for the <form> element.\n */\n className?: string;\n};\n\nexport type PiFormSubmittedEvent = {\n id?: string;\n /** Snapshot of form state at the time of submission. */\n formData: Record<string, unknown>;\n};\n\nexport type PiFormEvents = {\n onSubmitted: PiFormSubmittedEvent;\n};\n"],"mappings":";;AAOA,IAAa,IAAe,WAEf,IAAO,EAClB,CACF,GAEa,IAAiB,EAAgB,GAAc,CAAC,WAAW,CAAC,GAE5D,IAAkB,EAC7B,EAAe,SACjB,GAGa,IAAoB"}
@@ -1,14 +1,14 @@
1
1
  import { FormContext as e, useFormContext as t } from "./form.context.js";
2
2
  import { FormComponent as n } from "./form.component.js";
3
- import { Form as r, PI_FORM_ACTION as i, PI_FORM_CARD as a, onPiFormSubmitted as o } from "./form.types.js";
4
- import { actionTypesToEvents as s, registerCardComponent as c } from "@pihanga2/core";
3
+ import { Form as r, PI_FORM_ACTION as i, PI_FORM_CARD as a, onFormSubmitted as o, onPiFormSubmitted as s } from "./form.types.js";
4
+ import { actionTypesToEvents as c, registerCardComponent as l } from "@pihanga2/core";
5
5
  //#region src/cards/form/index.ts
6
- c({
6
+ l({
7
7
  name: a,
8
8
  component: n,
9
- events: s(i)
9
+ events: c(i)
10
10
  });
11
11
  //#endregion
12
- export { r as Form, e as FormContext, i as PI_FORM_ACTION, a as PI_FORM_CARD, o as onPiFormSubmitted, t as useFormContext };
12
+ export { r as Form, e as FormContext, i as PI_FORM_ACTION, a as PI_FORM_CARD, o as onFormSubmitted, s as onPiFormSubmitted, t as useFormContext };
13
13
 
14
14
  //# sourceMappingURL=index.js.map
@@ -226,6 +226,20 @@ export type GraphinTooltip = {
226
226
  * @default 4
227
227
  */
228
228
  offset?: number | [number, number];
229
+ /**
230
+ * Extra CSS class name(s) applied to the tooltip wrapper `<div>`.
231
+ * Use this to override the default background / border / shadow via
232
+ * Tailwind utilities or your own CSS classes.
233
+ *
234
+ * @example "rounded-xl shadow-lg border-primary"
235
+ */
236
+ className?: string;
237
+ /**
238
+ * Inline style overrides for the tooltip wrapper `<div>`.
239
+ * Merged on top of the built-in positioning / pointer-events styles so
240
+ * you can safely override individual properties (e.g. `padding`, `zIndex`).
241
+ */
242
+ style?: React.CSSProperties;
229
243
  };
230
244
  export type GraphinContextMenu = {
231
245
  /** Registered card name whose component is rendered inside the click panel. */
@@ -239,6 +253,20 @@ export type GraphinContextMenu = {
239
253
  * @default 4
240
254
  */
241
255
  offset?: number | [number, number];
256
+ /**
257
+ * Extra CSS class name(s) applied to the context-menu wrapper `<div>`.
258
+ * Use this to override the default background / border / shadow via
259
+ * Tailwind utilities or your own CSS classes.
260
+ *
261
+ * @example "rounded-xl shadow-lg border-primary"
262
+ */
263
+ className?: string;
264
+ /**
265
+ * Inline style overrides for the context-menu wrapper `<div>`.
266
+ * Merged on top of the built-in positioning / pointer-events styles so
267
+ * you can safely override individual properties (e.g. `padding`, `zIndex`).
268
+ */
269
+ style?: React.CSSProperties;
242
270
  };
243
271
  /**
244
272
  * Node shape types supported by G6 v5.
@@ -256,8 +284,10 @@ export type GraphinNodeStyleDef = {
256
284
  fill?: string;
257
285
  /** Border colour. */
258
286
  stroke?: string;
259
- /** Border thickness in pixels. */
287
+ /** Border thickness in pixels. Alias: `strokeWidth` (takes precedence). */
260
288
  lineWidth?: number;
289
+ /** Border thickness in pixels. Takes precedence over `lineWidth`. */
290
+ strokeWidth?: number;
261
291
  /**
262
292
  * Node size in pixels.
263
293
  * Pass a number for a square/circle; pass `[width, height]` for a rectangle.
@@ -322,10 +352,13 @@ export declare const GRAPHIN_OP_ACTION: {
322
352
  export type GraphinSetNodeStylesAction = {
323
353
  cardName: string;
324
354
  /**
325
- * Map of `nodeId` → style name (must be a key in the card's `nodeStyles` map).
326
- * @example { n1: "failed", n2: "degraded", n3: "ok" }
355
+ * Map of `nodeId` → style name or ordered list of style names to merge
356
+ * (each must be a key in the card's `nodeStyles` map).
357
+ * When an array is given, styles are merged left-to-right with later entries
358
+ * overriding duplicate properties from earlier ones.
359
+ * @example { n1: "failed", n2: ["base", "degraded"], n3: "ok" }
327
360
  */
328
- nodeStyles: Record<string, string>;
361
+ nodeStyles: Record<string, string | string[]>;
329
362
  };
330
363
  /**
331
364
  * Payload for `GRAPHIN_OP_ACTION.CLEAR_NODE_STYLES`.
@@ -1,13 +1,13 @@
1
1
  import { InputComponent as e } from "./input.component.js";
2
- import { PI_INPUT_ACTION as t, PI_INPUT_CARD as n, PiInput as r, onPiInputChanged as i, onPiInputCommitted as a } from "./input.types.js";
3
- import { actionTypesToEvents as o, registerCardComponent as s } from "@pihanga2/core";
2
+ import { PI_INPUT_ACTION as t, PI_INPUT_CARD as n, PiInput as r, onInputChanged as i, onInputCommitted as a, onPiInputChanged as o, onPiInputCommitted as s } from "./input.types.js";
3
+ import { actionTypesToEvents as c, registerCardComponent as l } from "@pihanga2/core";
4
4
  //#region src/cards/input/index.ts
5
- s({
5
+ l({
6
6
  name: n,
7
7
  component: e,
8
- events: o(t)
8
+ events: c(t)
9
9
  });
10
10
  //#endregion
11
- export { t as PI_INPUT_ACTION, n as PI_INPUT_CARD, r as PiInput, i as onPiInputChanged, a as onPiInputCommitted };
11
+ export { t as PI_INPUT_ACTION, n as PI_INPUT_CARD, r as PiInput, i as onInputChanged, a as onInputCommitted, o as onPiInputChanged, s as onPiInputCommitted };
12
12
 
13
13
  //# sourceMappingURL=index.js.map
@@ -1,6 +1,7 @@
1
1
  import { useFormContext as e } from "../form/form.context.js";
2
2
  import { Label as t } from "../../components/ui/label.js";
3
3
  import { Input as n } from "../../components/ui/input.js";
4
+ import "@pihanga2/core";
4
5
  import r from "react";
5
6
  import { jsx as i, jsxs as a } from "react/jsx-runtime";
6
7
  //#region src/cards/input/input.component.tsx
@@ -1 +1 @@
1
- {"version":3,"file":"input.component.js","names":[],"sources":["../../../src/cards/input/input.component.tsx"],"sourcesContent":["import React from \"react\";\nimport {type PiCardProps} from \"@pihanga2/core\";\nimport {Input} from \"@/components/ui/input\";\nimport {Label} from \"@/components/ui/label\";\nimport {useFormContext} from \"@/cards/form/form.context\";\nimport type {PiInputEvents, PiInputProps} from \"./input.types\";\n\nexport const InputComponent = (\n props: PiCardProps<PiInputProps, PiInputEvents>,\n): React.ReactNode => {\n const {\n name,\n value: propValue = \"\",\n type = \"text\",\n placeholder,\n disabled,\n label,\n description,\n className,\n cardName,\n onChanged,\n onCommitted,\n } = props;\n\n // `id` and `invalid` may be injected by a parent pi/field card via\n // Pihanga's extra-prop forwarding. They are not in the official type so\n // we read them through an escape hatch.\n const injectedId = (props as {id?: string}).id;\n const invalid = Boolean((props as {invalid?: boolean}).invalid);\n\n // Detect if we are inside a pi/form card via React context.\n const form = useFormContext();\n const useFormData = form.isInForm && Boolean(name);\n\n // Local display state — lets the user type freely without triggering Redux\n // on every keystroke. Only `onCommitted` (blur / Enter) writes back to\n // Redux, so callers that use `onCommitted` instead of `onChanged` avoid\n // per-keystroke panel rebuilds.\n const [localValue, setLocalValue] = React.useState<string>(\n type === \"file\" ? \"\" : propValue,\n );\n\n // Sync local value when the prop changes from outside (e.g. when a different\n // card is selected in the playground, resetting the controls panel).\n React.useEffect(() => {\n if (!useFormData && type !== \"file\") {\n setLocalValue(propValue);\n }\n }, [propValue, useFormData, type]);\n\n // Derive the effective value for the controlled <Input>:\n // - inside a form → read from form.formData[name]\n // - standalone → use localValue (updated on every keystroke locally)\n // - file inputs → uncontrolled (no value binding)\n const inputValue =\n type === \"file\"\n ? undefined\n : useFormData\n ? ((form.formData[name!] as string | undefined) ?? \"\")\n : localValue;\n\n function handleChange(e: React.ChangeEvent<HTMLInputElement>) {\n const newValue =\n type === \"file\" ? (e.target.files?.[0]?.name ?? \"\") : e.target.value;\n if (useFormData) {\n form.handleChange(name!, newValue);\n } else {\n setLocalValue(newValue);\n onChanged({name, value: newValue});\n }\n }\n\n // Fire onCommitted when the user leaves the field or presses Enter.\n function handleBlur() {\n if (!useFormData && type !== \"file\") {\n onCommitted({name, value: localValue});\n }\n }\n\n function handleKeyDown(e: React.KeyboardEvent<HTMLInputElement>) {\n if (e.key === \"Enter\" && !useFormData && type !== \"file\") {\n onCommitted({name, value: localValue});\n }\n }\n\n // Use the id injected by pi/field (for label↔control linking); fall back\n // to a locally-generated id when used standalone.\n const selfId = `${cardName}-${name ?? \"input\"}`;\n const fieldId = injectedId ?? selfId;\n\n return (\n <div data-pihanga={cardName} className=\"grid w-full gap-1.5\">\n {label && <Label htmlFor={fieldId}>{label}</Label>}\n <Input\n id={fieldId}\n type={type}\n value={inputValue}\n onChange={handleChange}\n onBlur={handleBlur}\n onKeyDown={handleKeyDown}\n placeholder={placeholder}\n disabled={disabled}\n className={className}\n aria-invalid={invalid || undefined}\n aria-describedby={description ? `${fieldId}-desc` : undefined}\n />\n {description && (\n <p id={`${fieldId}-desc`} className=\"text-sm text-muted-foreground\">\n {description}\n </p>\n )}\n </div>\n );\n};\n"],"mappings":";;;;;;AAOA,IAAa,KACX,MACoB;CACpB,IAAM,EACJ,SACA,OAAO,IAAY,IACnB,UAAO,QACP,gBACA,aACA,UACA,gBACA,cACA,aACA,cACA,mBACE,GAKE,IAAc,EAAwB,IACtC,IAAU,EAAS,EAA8B,SAGjD,IAAO,EAAe,GACtB,IAAc,EAAK,YAAY,EAAQ,GAMvC,CAAC,GAAY,KAAiB,EAAM,SACxC,MAAS,SAAS,KAAK,CACzB;CAIA,EAAM,gBAAgB;EACpB,AAAI,CAAC,KAAe,MAAS,UAC3B,EAAc,CAAS;CAE3B,GAAG;EAAC;EAAW;EAAa;CAAI,CAAC;CAMjC,IAAM,IACJ,MAAS,SACL,KAAA,IACA,IACI,EAAK,SAAS,MAAiC,KACjD;CAER,SAAS,EAAa,GAAwC;EAC5D,IAAM,IACJ,MAAS,SAAU,EAAE,OAAO,QAAQ,IAAI,QAAQ,KAAM,EAAE,OAAO;EACjE,AAAI,IACF,EAAK,aAAa,GAAO,CAAQ,KAEjC,EAAc,CAAQ,GACtB,EAAU;GAAC;GAAM,OAAO;EAAQ,CAAC;CAErC;CAGA,SAAS,IAAa;EACpB,AAAI,CAAC,KAAe,MAAS,UAC3B,EAAY;GAAC;GAAM,OAAO;EAAU,CAAC;CAEzC;CAEA,SAAS,EAAc,GAA0C;EAC/D,AAAI,EAAE,QAAQ,WAAW,CAAC,KAAe,MAAS,UAChD,EAAY;GAAC;GAAM,OAAO;EAAU,CAAC;CAEzC;CAIA,IAAM,IAAS,GAAG,EAAS,GAAG,KAAQ,WAChC,IAAU,KAAc;CAE9B,OACE,kBAAC,OAAD;EAAK,gBAAc;EAAU,WAAU;YAAvC;GACG,KAAS,kBAAC,GAAD;IAAO,SAAS;cAAU;GAAa,CAAA;GACjD,kBAAC,GAAD;IACE,IAAI;IACE;IACN,OAAO;IACP,UAAU;IACV,QAAQ;IACR,WAAW;IACE;IACH;IACC;IACX,gBAAc,KAAW,KAAA;IACzB,oBAAkB,IAAc,GAAG,EAAQ,SAAS,KAAA;GACrD,CAAA;GACA,KACC,kBAAC,KAAD;IAAG,IAAI,GAAG,EAAQ;IAAQ,WAAU;cACjC;GACA,CAAA;EAEF;;AAET"}
1
+ {"version":3,"file":"input.component.js","names":[],"sources":["../../../src/cards/input/input.component.tsx"],"sourcesContent":["import React from \"react\";\nimport {type PiCardProps} from \"@pihanga2/core\";\nimport {Input} from \"@/components/ui/input\";\nimport {Label} from \"@/components/ui/label\";\nimport {useFormContext} from \"@/cards/form/form.context\";\nimport type {PiInputEvents, PiInputProps} from \"./input.types\";\n\nexport const InputComponent = (\n props: PiCardProps<PiInputProps, PiInputEvents>,\n): React.ReactNode => {\n const {\n name,\n value: propValue = \"\",\n type = \"text\",\n placeholder,\n disabled,\n label,\n description,\n className,\n cardName,\n onChanged,\n onCommitted,\n } = props;\n\n // `id` and `invalid` may be injected by a parent pi/field card via\n // Pihanga's extra-prop forwarding. They are not in the official type so\n // we read them through an escape hatch.\n const injectedId = (props as {id?: string}).id;\n const invalid = Boolean((props as {invalid?: boolean}).invalid);\n\n // Detect if we are inside a pi/form card via React context.\n const form = useFormContext();\n const useFormData = form.isInForm && Boolean(name);\n\n // Local display state — lets the user type freely without triggering Redux\n // on every keystroke. Only `onCommitted` (blur / Enter) writes back to\n // Redux, so callers that use `onCommitted` instead of `onChanged` avoid\n // per-keystroke panel rebuilds.\n const [localValue, setLocalValue] = React.useState<string>(\n type === \"file\" ? \"\" : propValue,\n );\n\n // Sync local value when the prop changes from outside (e.g. when a different\n // card is selected in the playground, resetting the controls panel).\n React.useEffect(() => {\n if (!useFormData && type !== \"file\") {\n setLocalValue(propValue);\n }\n }, [propValue, useFormData, type]);\n\n // Derive the effective value for the controlled <Input>:\n // - inside a form → read from form.formData[name]\n // - standalone → use localValue (updated on every keystroke locally)\n // - file inputs → uncontrolled (no value binding)\n const inputValue =\n type === \"file\"\n ? undefined\n : useFormData\n ? ((form.formData[name!] as string | undefined) ?? \"\")\n : localValue;\n\n function handleChange(e: React.ChangeEvent<HTMLInputElement>) {\n const newValue =\n type === \"file\" ? (e.target.files?.[0]?.name ?? \"\") : e.target.value;\n if (useFormData) {\n form.handleChange(name!, newValue);\n } else {\n setLocalValue(newValue);\n onChanged({name, value: newValue});\n }\n }\n\n // Fire onCommitted when the user leaves the field or presses Enter.\n function handleBlur() {\n if (!useFormData && type !== \"file\") {\n onCommitted({name, value: localValue});\n }\n }\n\n function handleKeyDown(e: React.KeyboardEvent<HTMLInputElement>) {\n if (e.key === \"Enter\" && !useFormData && type !== \"file\") {\n onCommitted({name, value: localValue});\n }\n }\n\n // Use the id injected by pi/field (for label↔control linking); fall back\n // to a locally-generated id when used standalone.\n const selfId = `${cardName}-${name ?? \"input\"}`;\n const fieldId = injectedId ?? selfId;\n\n return (\n <div data-pihanga={cardName} className=\"grid w-full gap-1.5\">\n {label && <Label htmlFor={fieldId}>{label}</Label>}\n <Input\n id={fieldId}\n type={type}\n value={inputValue}\n onChange={handleChange}\n onBlur={handleBlur}\n onKeyDown={handleKeyDown}\n placeholder={placeholder}\n disabled={disabled}\n className={className}\n aria-invalid={invalid || undefined}\n aria-describedby={description ? `${fieldId}-desc` : undefined}\n />\n {description && (\n <p id={`${fieldId}-desc`} className=\"text-sm text-muted-foreground\">\n {description}\n </p>\n )}\n </div>\n );\n};\n"],"mappings":";;;;;;;AAOA,IAAa,KACX,MACoB;CACpB,IAAM,EACJ,SACA,OAAO,IAAY,IACnB,UAAO,QACP,gBACA,aACA,UACA,gBACA,cACA,aACA,cACA,mBACE,GAKE,IAAc,EAAwB,IACtC,IAAU,EAAS,EAA8B,SAGjD,IAAO,EAAe,GACtB,IAAc,EAAK,YAAY,EAAQ,GAMvC,CAAC,GAAY,KAAiB,EAAM,SACxC,MAAS,SAAS,KAAK,CACzB;CAIA,EAAM,gBAAgB;EACpB,AAAI,CAAC,KAAe,MAAS,UAC3B,EAAc,CAAS;CAE3B,GAAG;EAAC;EAAW;EAAa;CAAI,CAAC;CAMjC,IAAM,IACJ,MAAS,SACL,KAAA,IACA,IACI,EAAK,SAAS,MAAiC,KACjD;CAER,SAAS,EAAa,GAAwC;EAC5D,IAAM,IACJ,MAAS,SAAU,EAAE,OAAO,QAAQ,IAAI,QAAQ,KAAM,EAAE,OAAO;EACjE,AAAI,IACF,EAAK,aAAa,GAAO,CAAQ,KAEjC,EAAc,CAAQ,GACtB,EAAU;GAAC;GAAM,OAAO;EAAQ,CAAC;CAErC;CAGA,SAAS,IAAa;EACpB,AAAI,CAAC,KAAe,MAAS,UAC3B,EAAY;GAAC;GAAM,OAAO;EAAU,CAAC;CAEzC;CAEA,SAAS,EAAc,GAA0C;EAC/D,AAAI,EAAE,QAAQ,WAAW,CAAC,KAAe,MAAS,UAChD,EAAY;GAAC;GAAM,OAAO;EAAU,CAAC;CAEzC;CAIA,IAAM,IAAS,GAAG,EAAS,GAAG,KAAQ,WAChC,IAAU,KAAc;CAE9B,OACE,kBAAC,OAAD;EAAK,gBAAc;EAAU,WAAU;YAAvC;GACG,KAAS,kBAAC,GAAD;IAAO,SAAS;cAAU;GAAa,CAAA;GACjD,kBAAC,GAAD;IACE,IAAI;IACE;IACN,OAAO;IACP,UAAU;IACV,QAAQ;IACR,WAAW;IACE;IACH;IACC;IACX,gBAAc,KAAW,KAAA;IACzB,oBAAkB,IAAc,GAAG,EAAQ,SAAS,KAAA;GACrD,CAAA;GACA,KACC,kBAAC,KAAD;IAAG,IAAI,GAAG,EAAQ;IAAQ,WAAU;cACjC;GACA,CAAA;EAEF;;AAET"}
@@ -4,15 +4,23 @@ export declare const PI_INPUT_ACTION: {
4
4
  CHANGED: string;
5
5
  COMMITTED: string;
6
6
  };
7
+ export declare const onInputChanged: <S extends import('@pihanga2/core').ReduxState>(register: import('@pihanga2/core').PiRegister, f: import('@pihanga2/core').ReduceF<S, import('@pihanga2/core').ReduxAction & {
8
+ cardID: string;
9
+ } & PiInputChangedEvent>) => void;
10
+ /** @deprecated Use `onInputChanged` instead. */
7
11
  export declare const onPiInputChanged: <S extends import('@pihanga2/core').ReduxState>(register: import('@pihanga2/core').PiRegister, f: import('@pihanga2/core').ReduceF<S, import('@pihanga2/core').ReduxAction & {
8
12
  cardID: string;
9
13
  } & PiInputChangedEvent>) => void;
10
14
  /**
11
15
  * Subscribe to "committed" events — fired when the user finishes editing
12
- * (blur or Enter key). Unlike `onChanged` (which fires on every keystroke),
13
- * `onCommitted` only fires once per editing session, making it the preferred
16
+ * (blur or Enter key). Unlike `onInputChanged` (which fires on every keystroke),
17
+ * `onInputCommitted` only fires once per editing session, making it the preferred
14
18
  * handler for controlled inputs that trigger expensive downstream work.
15
19
  */
20
+ export declare const onInputCommitted: <S extends import('@pihanga2/core').ReduxState>(register: import('@pihanga2/core').PiRegister, f: import('@pihanga2/core').ReduceF<S, import('@pihanga2/core').ReduxAction & {
21
+ cardID: string;
22
+ } & PiInputCommittedEvent>) => void;
23
+ /** @deprecated Use `onInputCommitted` instead. */
16
24
  export declare const onPiInputCommitted: <S extends import('@pihanga2/core').ReduxState>(register: import('@pihanga2/core').PiRegister, f: import('@pihanga2/core').ReduceF<S, import('@pihanga2/core').ReduxAction & {
17
25
  cardID: string;
18
26
  } & PiInputCommittedEvent>) => void;
@@ -1,7 +1,7 @@
1
1
  import { createCardDeclaration as e, createOnAction as t, registerActions as n } from "@pihanga2/core";
2
2
  //#region src/cards/input/input.types.ts
3
- var r = "pi/input", i = e(r), a = n(r, ["changed", "committed"]), o = t(a.CHANGED), s = t(a.COMMITTED);
3
+ var r = "pi/input", i = e(r), a = n(r, ["changed", "committed"]), o = t(a.CHANGED), s = o, c = t(a.COMMITTED), l = c;
4
4
  //#endregion
5
- export { a as PI_INPUT_ACTION, r as PI_INPUT_CARD, i as PiInput, o as onPiInputChanged, s as onPiInputCommitted };
5
+ export { a as PI_INPUT_ACTION, r as PI_INPUT_CARD, i as PiInput, o as onInputChanged, c as onInputCommitted, s as onPiInputChanged, l as onPiInputCommitted };
6
6
 
7
7
  //# sourceMappingURL=input.types.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"input.types.js","names":[],"sources":["../../../src/cards/input/input.types.ts"],"sourcesContent":["import {\n createCardDeclaration,\n createOnAction,\n registerActions,\n} from \"@pihanga2/core\";\n\nexport const PI_INPUT_CARD = \"pi/input\";\n\nexport const PiInput = createCardDeclaration<PiInputProps, PiInputEvents>(\n PI_INPUT_CARD,\n);\n\nexport const PI_INPUT_ACTION = registerActions(PI_INPUT_CARD, [\n \"changed\",\n \"committed\",\n]);\n\nexport const onPiInputChanged = createOnAction<PiInputChangedEvent>(\n PI_INPUT_ACTION.CHANGED,\n);\n\n/**\n * Subscribe to \"committed\" events — fired when the user finishes editing\n * (blur or Enter key). Unlike `onChanged` (which fires on every keystroke),\n * `onCommitted` only fires once per editing session, making it the preferred\n * handler for controlled inputs that trigger expensive downstream work.\n */\nexport const onPiInputCommitted = createOnAction<PiInputCommittedEvent>(\n PI_INPUT_ACTION.COMMITTED,\n);\n\n// ---------------------------------------------------------------------------\n// Props & Events\n// ---------------------------------------------------------------------------\n\nexport type PiInputProps = {\n /**\n * Field name used to bind to FormContext when inside a pi/form card.\n * When provided the component reads its value from form state and writes\n * back via form.handleChange.\n */\n name?: string;\n\n /**\n * Controlled value used in standalone mode (outside a Form).\n * Ignored when `name` is set and the component is inside a pi/form.\n */\n value?: string;\n\n /**\n * HTML input type.\n * Common values: \"text\" | \"email\" | \"password\" | \"number\" | \"search\" |\n * \"url\" | \"tel\" | \"date\" | \"file\".\n * Defaults to \"text\".\n */\n type?: string;\n\n /** Placeholder text shown when the input is empty. */\n placeholder?: string;\n\n /** When true, the input is disabled and non-interactive. */\n disabled?: boolean;\n\n /**\n * Optional label text rendered in a `<label>` element above the input.\n * The label is automatically associated with the input via `htmlFor`.\n */\n label?: string;\n\n /**\n * Optional helper / description text rendered below the input.\n * Use this for hints, formatting guidance, or validation feedback.\n */\n description?: string;\n\n /** Extra Tailwind / CSS classes forwarded to the underlying <input> element. */\n className?: string;\n};\n\nexport type PiInputChangedEvent = {\n /** Field name, mirrors the `name` prop if provided. */\n name?: string;\n /** New value after the change. */\n value: string;\n};\n\n/**\n * Payload for the `onCommitted` event (blur / Enter key).\n * Same shape as `PiInputChangedEvent`.\n */\nexport type PiInputCommittedEvent = {\n /** Field name, mirrors the `name` prop if provided. */\n name?: string;\n /** Final committed value. */\n value: string;\n};\n\nexport type PiInputEvents = {\n /** Fires on every keystroke. */\n onChanged: PiInputChangedEvent;\n /**\n * Fires once when the user finishes editing (blur or Enter key).\n * Use this instead of `onChanged` when each change triggers expensive\n * downstream work (e.g. rebuilding a panel).\n */\n onCommitted: PiInputCommittedEvent;\n};\n"],"mappings":";;AAMA,IAAa,IAAgB,YAEhB,IAAU,EACrB,CACF,GAEa,IAAkB,EAAgB,GAAe,CAC5D,WACA,WACF,CAAC,GAEY,IAAmB,EAC9B,EAAgB,OAClB,GAQa,IAAqB,EAChC,EAAgB,SAClB"}
1
+ {"version":3,"file":"input.types.js","names":[],"sources":["../../../src/cards/input/input.types.ts"],"sourcesContent":["import {\n createCardDeclaration,\n createOnAction,\n registerActions,\n} from \"@pihanga2/core\";\n\nexport const PI_INPUT_CARD = \"pi/input\";\n\nexport const PiInput = createCardDeclaration<PiInputProps, PiInputEvents>(\n PI_INPUT_CARD,\n);\n\nexport const PI_INPUT_ACTION = registerActions(PI_INPUT_CARD, [\n \"changed\",\n \"committed\",\n]);\n\nexport const onInputChanged = createOnAction<PiInputChangedEvent>(\n PI_INPUT_ACTION.CHANGED,\n);\n\n/** @deprecated Use `onInputChanged` instead. */\nexport const onPiInputChanged = onInputChanged;\n\n/**\n * Subscribe to \"committed\" events — fired when the user finishes editing\n * (blur or Enter key). Unlike `onInputChanged` (which fires on every keystroke),\n * `onInputCommitted` only fires once per editing session, making it the preferred\n * handler for controlled inputs that trigger expensive downstream work.\n */\nexport const onInputCommitted = createOnAction<PiInputCommittedEvent>(\n PI_INPUT_ACTION.COMMITTED,\n);\n\n/** @deprecated Use `onInputCommitted` instead. */\nexport const onPiInputCommitted = onInputCommitted;\n\n// ---------------------------------------------------------------------------\n// Props & Events\n// ---------------------------------------------------------------------------\n\nexport type PiInputProps = {\n /**\n * Field name used to bind to FormContext when inside a pi/form card.\n * When provided the component reads its value from form state and writes\n * back via form.handleChange.\n */\n name?: string;\n\n /**\n * Controlled value used in standalone mode (outside a Form).\n * Ignored when `name` is set and the component is inside a pi/form.\n */\n value?: string;\n\n /**\n * HTML input type.\n * Common values: \"text\" | \"email\" | \"password\" | \"number\" | \"search\" |\n * \"url\" | \"tel\" | \"date\" | \"file\".\n * Defaults to \"text\".\n */\n type?: string;\n\n /** Placeholder text shown when the input is empty. */\n placeholder?: string;\n\n /** When true, the input is disabled and non-interactive. */\n disabled?: boolean;\n\n /**\n * Optional label text rendered in a `<label>` element above the input.\n * The label is automatically associated with the input via `htmlFor`.\n */\n label?: string;\n\n /**\n * Optional helper / description text rendered below the input.\n * Use this for hints, formatting guidance, or validation feedback.\n */\n description?: string;\n\n /** Extra Tailwind / CSS classes forwarded to the underlying <input> element. */\n className?: string;\n};\n\nexport type PiInputChangedEvent = {\n /** Field name, mirrors the `name` prop if provided. */\n name?: string;\n /** New value after the change. */\n value: string;\n};\n\n/**\n * Payload for the `onCommitted` event (blur / Enter key).\n * Same shape as `PiInputChangedEvent`.\n */\nexport type PiInputCommittedEvent = {\n /** Field name, mirrors the `name` prop if provided. */\n name?: string;\n /** Final committed value. */\n value: string;\n};\n\nexport type PiInputEvents = {\n /** Fires on every keystroke. */\n onChanged: PiInputChangedEvent;\n /**\n * Fires once when the user finishes editing (blur or Enter key).\n * Use this instead of `onChanged` when each change triggers expensive\n * downstream work (e.g. rebuilding a panel).\n */\n onCommitted: PiInputCommittedEvent;\n};\n"],"mappings":";;AAMA,IAAa,IAAgB,YAEhB,IAAU,EACrB,CACF,GAEa,IAAkB,EAAgB,GAAe,CAC5D,WACA,WACF,CAAC,GAEY,IAAiB,EAC5B,EAAgB,OAClB,GAGa,IAAmB,GAQnB,IAAmB,EAC9B,EAAgB,SAClB,GAGa,IAAqB"}
@@ -1,4 +1,5 @@
1
1
  import { createCardDeclaration as e, createOnAction as t, registerActions as n } from "@pihanga2/core";
2
+ import "react";
2
3
  //#region src/cards/list/list.types.ts
3
4
  var r = "shad/list", i = e(r), a = n(r, ["item_clicked"]), o = t(a.ITEM_CLICKED);
4
5
  //#endregion
@@ -1 +1 @@
1
- {"version":3,"file":"list.types.js","names":[],"sources":["../../../src/cards/list/list.types.ts"],"sourcesContent":["import * as React from \"react\";\nimport {\n PiCardRef,\n createCardDeclaration,\n createOnAction,\n registerActions,\n} from \"@pihanga2/core\";\n\nexport const LIST_CARD = \"shad/list\";\n\nexport const List = createCardDeclaration<ListProps, ListEvents>(LIST_CARD);\n\nexport const LIST_ACTION = registerActions(LIST_CARD, [\"item_clicked\"]);\n\nexport const onListItemClicked = createOnAction<ItemClickedEvent>(\n LIST_ACTION.ITEM_CLICKED,\n);\n\n// Decorator shown before or after an item's title/subtitle block.\nexport type IconDecorator = {\n type: \"icon\";\n /** Name registered via registerIcon() in src/cards/icons.ts */\n name: string;\n className?: string;\n};\n\nexport type AvatarDecorator = {\n type: \"avatar\";\n src: string;\n fallback?: string;\n className?: string;\n};\n\nexport type ChipDecorator = {\n type: \"chip\";\n text: string;\n className?: string;\n};\n\nexport type CardDecorator = {\n type: \"card\";\n cardName: PiCardRef;\n};\n\nexport type DecoratorT =\n | IconDecorator\n | AvatarDecorator\n | ChipDecorator\n | CardDecorator;\n\nexport type ListItem = {\n /** React key — must be unique within the list. */\n id: string | number;\n isSelected?: boolean;\n title: string;\n subTitle?: string;\n /** When present the item becomes a collapsible parent. */\n nested?: ListItem[];\n startDecorator?: DecoratorT;\n endDecorator?: DecoratorT;\n /** Extra CSS classes on this item's button. */\n className?: string;\n};\n\nexport type ListProps = {\n items: ListItem[];\n /** Visual density — defaults to \"md\". */\n size?: \"sm\" | \"md\" | \"lg\";\n /**\n * CSS `list-style-type` value (e.g. \"disc\", \"decimal\", \"none\").\n * Defaults to \"none\" (plain list without markers).\n */\n marker?: string;\n /** CSS classes applied directly to the root `<ul>`. */\n className?: string;\n /** Inline styles applied directly to the root `<ul>`. */\n style?: React.CSSProperties;\n};\n\nexport type ItemClickedEvent = {\n itemID: string | number;\n};\n\nexport type ListEvents = {\n onItemClicked: ItemClickedEvent;\n};\n"],"mappings":";;AAQA,IAAa,IAAY,aAEZ,IAAO,EAA6C,CAAS,GAE7D,IAAc,EAAgB,GAAW,CAAC,cAAc,CAAC,GAEzD,IAAoB,EAC/B,EAAY,YACd"}
1
+ {"version":3,"file":"list.types.js","names":[],"sources":["../../../src/cards/list/list.types.ts"],"sourcesContent":["import * as React from \"react\";\nimport {\n type PiCardRef,\n createCardDeclaration,\n createOnAction,\n registerActions,\n} from \"@pihanga2/core\";\n\nexport const LIST_CARD = \"shad/list\";\n\nexport const List = createCardDeclaration<ListProps, ListEvents>(LIST_CARD);\n\nexport const LIST_ACTION = registerActions(LIST_CARD, [\"item_clicked\"]);\n\nexport const onListItemClicked = createOnAction<ItemClickedEvent>(\n LIST_ACTION.ITEM_CLICKED,\n);\n\n// Decorator shown before or after an item's title/subtitle block.\nexport type IconDecorator = {\n type: \"icon\";\n /** Name registered via registerIcon() in src/cards/icons.ts */\n name: string;\n className?: string;\n};\n\nexport type AvatarDecorator = {\n type: \"avatar\";\n src: string;\n fallback?: string;\n className?: string;\n};\n\nexport type ChipDecorator = {\n type: \"chip\";\n text: string;\n className?: string;\n};\n\nexport type CardDecorator = {\n type: \"card\";\n cardName: PiCardRef;\n};\n\nexport type DecoratorT =\n | IconDecorator\n | AvatarDecorator\n | ChipDecorator\n | CardDecorator;\n\nexport type ListItem = {\n /** React key — must be unique within the list. */\n id: string | number;\n isSelected?: boolean;\n title: string;\n subTitle?: string;\n /** When present the item becomes a collapsible parent. */\n nested?: ListItem[];\n startDecorator?: DecoratorT;\n endDecorator?: DecoratorT;\n /** Extra CSS classes on this item's button. */\n className?: string;\n};\n\nexport type ListProps = {\n items: ListItem[];\n /** Visual density — defaults to \"md\". */\n size?: \"sm\" | \"md\" | \"lg\";\n /**\n * CSS `list-style-type` value (e.g. \"disc\", \"decimal\", \"none\").\n * Defaults to \"none\" (plain list without markers).\n */\n marker?: string;\n /** CSS classes applied directly to the root `<ul>`. */\n className?: string;\n /** Inline styles applied directly to the root `<ul>`. */\n style?: React.CSSProperties;\n};\n\nexport type ItemClickedEvent = {\n itemID: string | number;\n};\n\nexport type ListEvents = {\n onItemClicked: ItemClickedEvent;\n};\n"],"mappings":";;;AAQA,IAAa,IAAY,aAEZ,IAAO,EAA6C,CAAS,GAE7D,IAAc,EAAgB,GAAW,CAAC,cAAc,CAAC,GAEzD,IAAoB,EAC/B,EAAY,YACd"}
@@ -1,12 +1,10 @@
1
1
  import { PiCardProps } from '@pihanga2/core';
2
2
  import { PageWithNavbarEvents, PageWithNavbarProps } from './pageWithNavbar.type';
3
3
  export type ShadStyle = {
4
- shad?: {
5
- window?: string;
6
- page?: string;
7
- header?: string;
8
- main?: string;
9
- footer?: string;
10
- };
4
+ window?: string;
5
+ page?: string;
6
+ header?: string;
7
+ main?: string;
8
+ footer?: string;
11
9
  };
12
10
  export declare const Component: (props: PiCardProps<PageWithNavbarProps<ShadStyle>, PageWithNavbarEvents>) => React.ReactNode;
@@ -38,7 +38,7 @@ function y(e, t) {
38
38
  return e.default ?? void 0;
39
39
  }
40
40
  var b = (a) => {
41
- let { title: o, iconName: s, main: d, footer: g, navLinks: v = [], scrollResetKey: b, headerLeftCard: S, headerRightCard: C, style: w, onNavigateTo: T, cardName: E } = a, D = _(), O = w?.shad ?? {}, k = u(null);
41
+ let { title: o, iconName: s, main: d, footer: g, navLinks: v = [], scrollResetKey: b, headerLeftCard: S, headerRightCard: C, style: w, onNavigateTo: T, cardName: E } = a, D = _(), O = w ?? {}, k = u(null);
42
42
  l(() => {
43
43
  k.current?.scrollTo({
44
44
  top: 0,
@@ -1 +1 @@
1
- {"version":3,"file":"pageWithNavbar.component.js","names":[],"sources":["../../../src/cards/pageWithNavbar/pageWithNavbar.component.tsx"],"sourcesContent":["import {Menu} from \"lucide-react\";\nimport clsx from \"clsx\";\nimport {useEffect, useRef, useState} from \"react\";\n\nimport {Button} from \"@/components/ui/button\";\nimport {Sheet, SheetContent, SheetTrigger} from \"@/components/ui/sheet\";\nimport {Card, type PiCardProps} from \"@pihanga2/core\";\nimport {getIconElement} from \"@/cards/icons\";\nimport {ScreenSize, SCREEN_SIZE_ORDER, SCREEN_SIZE_WIDTHS} from \"@/cards/types\";\nimport type {\n PageWithNavbarEvents,\n PageWithNavbarProps,\n ResponsiveCardRef,\n} from \"./pageWithNavbar.type\";\nimport type {SizeMap} from \"@/cards/types\";\nimport type {PiCardRef} from \"@pihanga2/core\";\n\nimport \"./pageWithNavbar.css\";\n\n// ---------------------------------------------------------------------------\n// Style override type — consumers can extend this to add Tailwind class names\n// to any structural section of the layout.\n// ---------------------------------------------------------------------------\n\nexport type ShadStyle = {\n shad?: {\n window?: string;\n page?: string;\n header?: string;\n main?: string;\n footer?: string;\n };\n};\n\n// ---------------------------------------------------------------------------\n// Screen-size hook\n// ---------------------------------------------------------------------------\n\nfunction getCurrentScreenSize(): ScreenSize {\n const w = typeof window !== \"undefined\" ? window.innerWidth : 0;\n // Walk breakpoints from largest to smallest\n for (let i = SCREEN_SIZE_ORDER.length - 1; i >= 0; i--) {\n const size = SCREEN_SIZE_ORDER[i];\n if (w >= SCREEN_SIZE_WIDTHS[size]) return size;\n }\n return ScreenSize.XS;\n}\n\nfunction useScreenSize(): ScreenSize {\n const [size, setSize] = useState<ScreenSize>(getCurrentScreenSize);\n\n useEffect(() => {\n const handleResize = () => setSize(getCurrentScreenSize());\n window.addEventListener(\"resize\", handleResize);\n return () => window.removeEventListener(\"resize\", handleResize);\n }, []);\n\n return size;\n}\n\n// ---------------------------------------------------------------------------\n// Responsive card-ref resolution\n// ---------------------------------------------------------------------------\n\nfunction isSizeMap(ref: ResponsiveCardRef): ref is SizeMap<PiCardRef> {\n return typeof ref === \"object\" && ref !== null;\n}\n\n/**\n * Resolve a {@link ResponsiveCardRef} to a concrete `PiCardRef` (or\n * `undefined`) for the given screen size.\n *\n * Uses a **mobile-first cascade**: the entry with the largest breakpoint\n * that is still ≤ `currentSize` wins; falls back to `default` otherwise.\n */\nfunction resolveCardRef(\n ref: ResponsiveCardRef | undefined,\n currentSize: ScreenSize,\n): PiCardRef | undefined {\n if (!ref) return undefined;\n if (!isSizeMap(ref)) return ref; // plain PiCardRef (string or PiCardDef)\n\n // Cascade: find the highest breakpoint that is ≤ current size\n const currentIdx = SCREEN_SIZE_ORDER.indexOf(currentSize);\n for (let i = currentIdx; i >= 0; i--) {\n const size = SCREEN_SIZE_ORDER[i];\n if (Object.prototype.hasOwnProperty.call(ref, size)) {\n const val = ref[size];\n // null means \"explicitly hidden at this breakpoint\"\n return val ?? undefined;\n }\n }\n // Fall back to the default entry\n return ref.default ?? undefined;\n}\n\n// ---------------------------------------------------------------------------\n// Component\n// ---------------------------------------------------------------------------\n\nexport const Component = (\n props: PiCardProps<PageWithNavbarProps<ShadStyle>, PageWithNavbarEvents>,\n): React.ReactNode => {\n const {\n title,\n iconName,\n main,\n footer,\n navLinks = [],\n scrollResetKey,\n headerLeftCard,\n headerRightCard,\n style,\n onNavigateTo,\n cardName,\n } = props;\n\n const screenSize = useScreenSize();\n const cl = style?.shad ?? {};\n const mainRef = useRef<HTMLElement | null>(null);\n\n // Scroll main back to top when the logical \"page\" changes\n useEffect(() => {\n mainRef.current?.scrollTo({top: 0, behavior: \"auto\"});\n }, [scrollResetKey]);\n\n const resolvedLeftCard = resolveCardRef(headerLeftCard, screenSize);\n const resolvedRightCard = resolveCardRef(headerRightCard, screenSize);\n const hasNavLinks = navLinks.length > 0;\n\n // ------------------------------------------------------------------\n // Sub-components\n // ------------------------------------------------------------------\n\n /** Title section: optional icon + text */\n function TitleSection({className}: {className?: string}) {\n const IconEl = iconName ? getIconElement(iconName) : undefined;\n return (\n <div className={clsx(\"title-section\", className)}>\n {IconEl && <IconEl className=\"title-icon\" aria-hidden />}\n <span className=\"title-text\">{title}</span>\n </div>\n );\n }\n\n /** Nav bar rendered on medium screens and above */\n function NavMd() {\n return (\n <nav className=\"nav-md\" aria-label=\"Main navigation\">\n <TitleSection />\n {navLinks.map((el) => (\n <Button\n key={el.id}\n onClick={() => onNavigateTo({id: el.id})}\n variant=\"link\"\n className=\"nav-link\"\n >\n {el.title ?? capitalize(el.id)}\n </Button>\n ))}\n </nav>\n );\n }\n\n /** Hamburger trigger that opens a slide-out sheet on small screens */\n function NavSm() {\n return (\n <Sheet>\n <SheetTrigger asChild>\n <Button\n variant=\"outline\"\n size=\"icon\"\n className=\"shrink-0 md:hidden\"\n aria-label=\"Toggle navigation menu\"\n >\n <Menu className=\"h-5 w-5\" aria-hidden />\n </Button>\n </SheetTrigger>\n <SheetContent side=\"left\">\n <nav className=\"nav-sm\" aria-label=\"Mobile navigation\">\n <TitleSection />\n {navLinks.map((el) => (\n <Button\n key={el.id}\n onClick={() => onNavigateTo({id: el.id})}\n variant=\"link\"\n className=\"nav-link\"\n >\n {el.title ?? capitalize(el.id)}\n </Button>\n ))}\n </nav>\n </SheetContent>\n </Sheet>\n );\n }\n\n // ------------------------------------------------------------------\n // Layout\n // ------------------------------------------------------------------\n\n return (\n <div className={clsx(\"window\", cl.window)} data-pihanga={cardName}>\n <div className={clsx(\"page\", cl.page)}>\n {/* ── Header ─────────────────────────────────────────────── */}\n <header className={clsx(\"header\", cl.header)}>\n {/* Medium+ nav (title + links) */}\n <NavMd />\n\n {/* Small-screen hamburger (only when there are nav links) */}\n {hasNavLinks && <NavSm />}\n\n {/* On small screens with no nav links, show the title inline.\n Wrap in a plain div so md:hidden isn't overridden by the\n .title-section CSS class that sets display:inline-flex. */}\n {!hasNavLinks && (\n <div className=\"md:hidden\">\n <TitleSection />\n </div>\n )}\n\n {/* Push action cards to the right */}\n <div className=\"header-actions\">\n {resolvedLeftCard && (\n <Card cardName={resolvedLeftCard} parentCard={cardName} />\n )}\n {resolvedRightCard && (\n <Card cardName={resolvedRightCard} parentCard={cardName} />\n )}\n </div>\n </header>\n\n {/* ── Main ───────────────────────────────────────────────── */}\n <main className={clsx(\"main\", cl.main)} ref={mainRef}>\n <Card cardName={main} parentCard={cardName} />\n </main>\n\n {/* ── Footer (optional) ──────────────────────────────────── */}\n {footer && (\n <footer className={clsx(\"footer\", cl.footer)}>\n <Card cardName={footer} parentCard={cardName} />\n </footer>\n )}\n </div>\n </div>\n );\n};\n\n// ---------------------------------------------------------------------------\n// Helpers\n// ---------------------------------------------------------------------------\n\nfunction capitalize(str: string): string {\n if (!str) return \"\";\n return str.charAt(0).toLocaleUpperCase() + str.slice(1);\n}\n"],"mappings":";;;;;;;;;;;AAsCA,SAAS,IAAmC;CAC1C,IAAM,IAAI,OAAO,SAAW,MAAc,OAAO,aAAa;CAE9D,KAAK,IAAI,IAAI,EAAkB,SAAS,GAAG,KAAK,GAAG,KAAK;EACtD,IAAM,IAAO,EAAkB;EAC/B,IAAI,KAAK,EAAmB,IAAO,OAAO;CAC5C;CACA,OAAO,EAAW;AACpB;AAEA,SAAS,IAA4B;CACnC,IAAM,CAAC,GAAM,KAAW,EAAqB,CAAoB;CAQjE,OANA,QAAgB;EACd,IAAM,UAAqB,EAAQ,EAAqB,CAAC;EAEzD,OADA,OAAO,iBAAiB,UAAU,CAAY,SACjC,OAAO,oBAAoB,UAAU,CAAY;CAChE,GAAG,CAAC,CAAC,GAEE;AACT;AAMA,SAAS,EAAU,GAAmD;CACpE,OAAO,OAAO,KAAQ,cAAY;AACpC;AASA,SAAS,EACP,GACA,GACuB;CACvB,IAAI,CAAC,GAAK;CACV,IAAI,CAAC,EAAU,CAAG,GAAG,OAAO;CAG5B,IAAM,IAAa,EAAkB,QAAQ,CAAW;CACxD,KAAK,IAAI,IAAI,GAAY,KAAK,GAAG,KAAK;EACpC,IAAM,IAAO,EAAkB;EAC/B,IAAI,OAAO,UAAU,eAAe,KAAK,GAAK,CAAI,GAGhD,OAFY,EAAI,MAEF,KAAA;CAElB;CAEA,OAAO,EAAI,WAAW,KAAA;AACxB;AAMA,IAAa,KACX,MACoB;CACpB,IAAM,EACJ,UACA,aACA,SACA,WACA,cAAW,CAAC,GACZ,mBACA,mBACA,oBACA,UACA,iBACA,gBACE,GAEE,IAAa,EAAc,GAC3B,IAAK,GAAO,QAAQ,CAAC,GACrB,IAAU,EAA2B,IAAI;CAG/C,QAAgB;EACd,EAAQ,SAAS,SAAS;GAAC,KAAK;GAAG,UAAU;EAAM,CAAC;CACtD,GAAG,CAAC,CAAc,CAAC;CAEnB,IAAM,IAAmB,EAAe,GAAgB,CAAU,GAC5D,IAAoB,EAAe,GAAiB,CAAU,GAC9D,IAAc,EAAS,SAAS;CAOtC,SAAS,EAAa,EAAC,gBAAkC;EACvD,IAAM,IAAS,IAAW,EAAe,CAAQ,IAAI,KAAA;EACrD,OACE,kBAAC,OAAD;GAAK,WAAW,EAAK,iBAAiB,CAAS;aAA/C,CACG,KAAU,kBAAC,GAAD;IAAQ,WAAU;IAAa,eAAA;GAAa,CAAA,GACvD,kBAAC,QAAD;IAAM,WAAU;cAAc;GAAY,CAAA,CACvC;;CAET;CAGA,SAAS,IAAQ;EACf,OACE,kBAAC,OAAD;GAAK,WAAU;GAAS,cAAW;aAAnC,CACE,kBAAC,GAAD,CAAe,CAAA,GACd,EAAS,KAAK,MACb,kBAAC,GAAD;IAEE,eAAe,EAAa,EAAC,IAAI,EAAG,GAAE,CAAC;IACvC,SAAQ;IACR,WAAU;cAET,EAAG,SAAS,EAAW,EAAG,EAAE;GACvB,GAND,EAAG,EAMF,CACT,CACE;;CAET;CAGA,SAAS,IAAQ;EACf,OACE,kBAAC,GAAD,EAAA,UAAA,CACE,kBAAC,GAAD;GAAc,SAAA;aACZ,kBAAC,GAAD;IACE,SAAQ;IACR,MAAK;IACL,WAAU;IACV,cAAW;cAEX,kBAAC,GAAD;KAAM,WAAU;KAAU,eAAA;IAAa,CAAA;GACjC,CAAA;EACI,CAAA,GACd,kBAAC,GAAD;GAAc,MAAK;aACjB,kBAAC,OAAD;IAAK,WAAU;IAAS,cAAW;cAAnC,CACE,kBAAC,GAAD,CAAe,CAAA,GACd,EAAS,KAAK,MACb,kBAAC,GAAD;KAEE,eAAe,EAAa,EAAC,IAAI,EAAG,GAAE,CAAC;KACvC,SAAQ;KACR,WAAU;eAET,EAAG,SAAS,EAAW,EAAG,EAAE;IACvB,GAND,EAAG,EAMF,CACT,CACE;;EACO,CAAA,CACT,EAAA,CAAA;CAEX;CAMA,OACE,kBAAC,OAAD;EAAK,WAAW,EAAK,UAAU,EAAG,MAAM;EAAG,gBAAc;YACvD,kBAAC,OAAD;GAAK,WAAW,EAAK,QAAQ,EAAG,IAAI;aAApC;IAEE,kBAAC,UAAD;KAAQ,WAAW,EAAK,UAAU,EAAG,MAAM;eAA3C;MAEE,kBAAC,GAAD,CAAQ,CAAA;MAGP,KAAe,kBAAC,GAAD,CAAQ,CAAA;MAKvB,CAAC,KACA,kBAAC,OAAD;OAAK,WAAU;iBACb,kBAAC,GAAD,CAAe,CAAA;MACZ,CAAA;MAIP,kBAAC,OAAD;OAAK,WAAU;iBAAf,CACG,KACC,kBAAC,GAAD;QAAM,UAAU;QAAkB,YAAY;OAAW,CAAA,GAE1D,KACC,kBAAC,GAAD;QAAM,UAAU;QAAmB,YAAY;OAAW,CAAA,CAEzD;;KACC;;IAGR,kBAAC,QAAD;KAAM,WAAW,EAAK,QAAQ,EAAG,IAAI;KAAG,KAAK;eAC3C,kBAAC,GAAD;MAAM,UAAU;MAAM,YAAY;KAAW,CAAA;IACzC,CAAA;IAGL,KACC,kBAAC,UAAD;KAAQ,WAAW,EAAK,UAAU,EAAG,MAAM;eACzC,kBAAC,GAAD;MAAM,UAAU;MAAQ,YAAY;KAAW,CAAA;IACzC,CAAA;GAEP;;CACF,CAAA;AAET;AAMA,SAAS,EAAW,GAAqB;CAEvC,OADK,IACE,EAAI,OAAO,CAAC,EAAE,kBAAkB,IAAI,EAAI,MAAM,CAAC,IADrC;AAEnB"}
1
+ {"version":3,"file":"pageWithNavbar.component.js","names":[],"sources":["../../../src/cards/pageWithNavbar/pageWithNavbar.component.tsx"],"sourcesContent":["import {Menu} from \"lucide-react\";\nimport clsx from \"clsx\";\nimport {useEffect, useRef, useState} from \"react\";\n\nimport {Button} from \"@/components/ui/button\";\nimport {Sheet, SheetContent, SheetTrigger} from \"@/components/ui/sheet\";\nimport {Card, type PiCardProps} from \"@pihanga2/core\";\nimport {getIconElement} from \"@/cards/icons\";\nimport {ScreenSize, SCREEN_SIZE_ORDER, SCREEN_SIZE_WIDTHS} from \"@/cards/types\";\nimport type {\n PageWithNavbarEvents,\n PageWithNavbarProps,\n ResponsiveCardRef,\n} from \"./pageWithNavbar.type\";\nimport type {SizeMap} from \"@/cards/types\";\nimport type {PiCardRef} from \"@pihanga2/core\";\n\nimport \"./pageWithNavbar.css\";\n\n// ---------------------------------------------------------------------------\n// Style override type — consumers can extend this to add Tailwind class names\n// to any structural section of the layout.\n// ---------------------------------------------------------------------------\n\nexport type ShadStyle = {\n window?: string;\n page?: string;\n header?: string;\n main?: string;\n footer?: string;\n};\n\n// ---------------------------------------------------------------------------\n// Screen-size hook\n// ---------------------------------------------------------------------------\n\nfunction getCurrentScreenSize(): ScreenSize {\n const w = typeof window !== \"undefined\" ? window.innerWidth : 0;\n // Walk breakpoints from largest to smallest\n for (let i = SCREEN_SIZE_ORDER.length - 1; i >= 0; i--) {\n const size = SCREEN_SIZE_ORDER[i];\n if (w >= SCREEN_SIZE_WIDTHS[size]) return size;\n }\n return ScreenSize.XS;\n}\n\nfunction useScreenSize(): ScreenSize {\n const [size, setSize] = useState<ScreenSize>(getCurrentScreenSize);\n\n useEffect(() => {\n const handleResize = () => setSize(getCurrentScreenSize());\n window.addEventListener(\"resize\", handleResize);\n return () => window.removeEventListener(\"resize\", handleResize);\n }, []);\n\n return size;\n}\n\n// ---------------------------------------------------------------------------\n// Responsive card-ref resolution\n// ---------------------------------------------------------------------------\n\nfunction isSizeMap(ref: ResponsiveCardRef): ref is SizeMap<PiCardRef> {\n return typeof ref === \"object\" && ref !== null;\n}\n\n/**\n * Resolve a {@link ResponsiveCardRef} to a concrete `PiCardRef` (or\n * `undefined`) for the given screen size.\n *\n * Uses a **mobile-first cascade**: the entry with the largest breakpoint\n * that is still ≤ `currentSize` wins; falls back to `default` otherwise.\n */\nfunction resolveCardRef(\n ref: ResponsiveCardRef | undefined,\n currentSize: ScreenSize,\n): PiCardRef | undefined {\n if (!ref) return undefined;\n if (!isSizeMap(ref)) return ref; // plain PiCardRef (string or PiCardDef)\n\n // Cascade: find the highest breakpoint that is ≤ current size\n const currentIdx = SCREEN_SIZE_ORDER.indexOf(currentSize);\n for (let i = currentIdx; i >= 0; i--) {\n const size = SCREEN_SIZE_ORDER[i];\n if (Object.prototype.hasOwnProperty.call(ref, size)) {\n const val = ref[size];\n // null means \"explicitly hidden at this breakpoint\"\n return val ?? undefined;\n }\n }\n // Fall back to the default entry\n return ref.default ?? undefined;\n}\n\n// ---------------------------------------------------------------------------\n// Component\n// ---------------------------------------------------------------------------\n\nexport const Component = (\n props: PiCardProps<PageWithNavbarProps<ShadStyle>, PageWithNavbarEvents>,\n): React.ReactNode => {\n const {\n title,\n iconName,\n main,\n footer,\n navLinks = [],\n scrollResetKey,\n headerLeftCard,\n headerRightCard,\n style,\n onNavigateTo,\n cardName,\n } = props;\n\n const screenSize = useScreenSize();\n const cl = style ?? {};\n const mainRef = useRef<HTMLElement | null>(null);\n\n // Scroll main back to top when the logical \"page\" changes\n useEffect(() => {\n mainRef.current?.scrollTo({top: 0, behavior: \"auto\"});\n }, [scrollResetKey]);\n\n const resolvedLeftCard = resolveCardRef(headerLeftCard, screenSize);\n const resolvedRightCard = resolveCardRef(headerRightCard, screenSize);\n const hasNavLinks = navLinks.length > 0;\n\n // ------------------------------------------------------------------\n // Sub-components\n // ------------------------------------------------------------------\n\n /** Title section: optional icon + text */\n function TitleSection({className}: {className?: string}) {\n const IconEl = iconName ? getIconElement(iconName) : undefined;\n return (\n <div className={clsx(\"title-section\", className)}>\n {IconEl && <IconEl className=\"title-icon\" aria-hidden />}\n <span className=\"title-text\">{title}</span>\n </div>\n );\n }\n\n /** Nav bar rendered on medium screens and above */\n function NavMd() {\n return (\n <nav className=\"nav-md\" aria-label=\"Main navigation\">\n <TitleSection />\n {navLinks.map((el) => (\n <Button\n key={el.id}\n onClick={() => onNavigateTo({id: el.id})}\n variant=\"link\"\n className=\"nav-link\"\n >\n {el.title ?? capitalize(el.id)}\n </Button>\n ))}\n </nav>\n );\n }\n\n /** Hamburger trigger that opens a slide-out sheet on small screens */\n function NavSm() {\n return (\n <Sheet>\n <SheetTrigger asChild>\n <Button\n variant=\"outline\"\n size=\"icon\"\n className=\"shrink-0 md:hidden\"\n aria-label=\"Toggle navigation menu\"\n >\n <Menu className=\"h-5 w-5\" aria-hidden />\n </Button>\n </SheetTrigger>\n <SheetContent side=\"left\">\n <nav className=\"nav-sm\" aria-label=\"Mobile navigation\">\n <TitleSection />\n {navLinks.map((el) => (\n <Button\n key={el.id}\n onClick={() => onNavigateTo({id: el.id})}\n variant=\"link\"\n className=\"nav-link\"\n >\n {el.title ?? capitalize(el.id)}\n </Button>\n ))}\n </nav>\n </SheetContent>\n </Sheet>\n );\n }\n\n // ------------------------------------------------------------------\n // Layout\n // ------------------------------------------------------------------\n\n return (\n <div className={clsx(\"window\", cl.window)} data-pihanga={cardName}>\n <div className={clsx(\"page\", cl.page)}>\n {/* ── Header ─────────────────────────────────────────────── */}\n <header className={clsx(\"header\", cl.header)}>\n {/* Medium+ nav (title + links) */}\n <NavMd />\n\n {/* Small-screen hamburger (only when there are nav links) */}\n {hasNavLinks && <NavSm />}\n\n {/* On small screens with no nav links, show the title inline.\n Wrap in a plain div so md:hidden isn't overridden by the\n .title-section CSS class that sets display:inline-flex. */}\n {!hasNavLinks && (\n <div className=\"md:hidden\">\n <TitleSection />\n </div>\n )}\n\n {/* Push action cards to the right */}\n <div className=\"header-actions\">\n {resolvedLeftCard && (\n <Card cardName={resolvedLeftCard} parentCard={cardName} />\n )}\n {resolvedRightCard && (\n <Card cardName={resolvedRightCard} parentCard={cardName} />\n )}\n </div>\n </header>\n\n {/* ── Main ───────────────────────────────────────────────── */}\n <main className={clsx(\"main\", cl.main)} ref={mainRef}>\n <Card cardName={main} parentCard={cardName} />\n </main>\n\n {/* ── Footer (optional) ──────────────────────────────────── */}\n {footer && (\n <footer className={clsx(\"footer\", cl.footer)}>\n <Card cardName={footer} parentCard={cardName} />\n </footer>\n )}\n </div>\n </div>\n );\n};\n\n// ---------------------------------------------------------------------------\n// Helpers\n// ---------------------------------------------------------------------------\n\nfunction capitalize(str: string): string {\n if (!str) return \"\";\n return str.charAt(0).toLocaleUpperCase() + str.slice(1);\n}\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"pasteTarget.component.js","names":[],"sources":["../../../src/cards/pasteTarget/pasteTarget.component.tsx"],"sourcesContent":["import {\n DEF_ERROR_COLOR,\n DEF_EVENT_DURATION_SECONDS,\n DEF_HEIGHT,\n DEF_OVERLAY_OPACITY,\n DEF_PASTE_FIRST_REMINDER,\n DEF_SUCCESS_COLOR,\n PasteItem,\n PasteTargetEvents,\n PasteTargetProps,\n} from \"@/cards/pasteTarget/pasteTarget.types\";\nimport {PiCardProps} from \"@pihanga2/core\";\nimport React, {useRef, useState} from \"react\";\n\nimport \"./pasteTarget.css\";\nimport clsx from \"clsx\";\nimport {Button} from \"@/registry/ui/button\";\n\n// /* 3. Stretch the child to cover the parent entirely */\n// top: 0,\n// left: 0,\n// right: 0,\n// bottom: 0,\n\n// /* 4. Ensure the child is visually stacked above the parent's content */\n// z-index: 10,\n\n// /* Optional: Make the overlay visible (e.g., semi-transparent black) */\n// overlayOpacity: \"rgba(0, 0, 0, 0.5)\";\n// }\n\nexport const PasteTargetComponent = (\n props: PiCardProps<PasteTargetProps, PasteTargetEvents>,\n): React.ReactNode => {\n const {\n title, // = \"Paste content right here\",\n description,\n pasteFirstReminder = DEF_PASTE_FIRST_REMINDER,\n withUpload,\n fileTypes,\n\n height = DEF_HEIGHT,\n\n onPastedContent,\n\n successColor = DEF_SUCCESS_COLOR,\n errorColor = DEF_ERROR_COLOR,\n eventDurationSeconds = DEF_EVENT_DURATION_SECONDS,\n eventOpacity = DEF_OVERLAY_OPACITY,\n cardName,\n className,\n _cls,\n } = props;\n const [overlayOpacity, setOverlayOpacity] = useState<number>(0);\n const [overlayBackground, setOverlayBackground] = useState<string>(\"gray\");\n const timerRef = React.useRef<number | null>(null);\n const textareaRef = useRef<HTMLTextAreaElement>(null);\n const fileInputRef = useRef<HTMLInputElement>(null);\n const [isFocused, setIsFocused] = useState<boolean>(false);\n\n const overlayStyle: React.CSSProperties = {\n backgroundColor: overlayBackground,\n opacity: overlayOpacity,\n };\n\n const handlePaste = (event: React.ClipboardEvent<HTMLTextAreaElement>) => {\n if (timerRef.current) {\n clearTimeout(timerRef.current);\n }\n\n event.preventDefault();\n const clipboardData =\n event.clipboardData ||\n (window as Window & {clipboardData?: DataTransfer}).clipboardData;\n\n if (clipboardData) {\n const pItems = clipboardData.items;\n const items: PasteItem[] = [];\n for (let i = 0; i < pItems.length; i++) {\n const pi = pItems[i];\n const mimeType = pi.type;\n const content = clipboardData.getData(pi.type);\n if (!(content == \"\" || content == \"\\n\")) {\n items.push({mimeType, content});\n }\n }\n onPastedContent({items});\n setOverlayBackground(successColor);\n } else {\n setOverlayBackground(errorColor);\n }\n setOverlayOpacity(eventOpacity);\n if (textareaRef.current) textareaRef.current.blur();\n\n // Set a timeout to switch the color back after 'n' seconds\n const newTimerId = setTimeout(() => {\n setOverlayOpacity(0);\n timerRef.current = null; // Clear the ref after the timeout executes\n }, eventDurationSeconds * 1000); // Convert seconds to milliseconds\n\n // Save the new timer ID to the ref (unknown necessary type casting)\n timerRef.current = newTimerId as unknown as number;\n };\n\n // Cleanup function to clear the timer when the component unmounts\n React.useEffect(() => {\n return () => {\n const ref = timerRef.current;\n if (ref) {\n clearTimeout(ref);\n }\n };\n }, []); // runs only on mount/unmount\n\n function renderPasteHandler() {\n return (\n <textarea\n onPaste={handlePaste}\n onFocus={() => {\n setIsFocused(true);\n }}\n onBlur={() => setIsFocused(false)}\n style={{\n position: \"absolute\",\n top: 0,\n left: 0,\n right: 0,\n bottom: 0,\n zIndex: 10,\n resize: \"none\",\n background: \"transparent\",\n cursor: isFocused ? \"copy\" : \"pointer\",\n ...overlayStyle,\n }}\n // 🔑 CRUCIAL: Focus the element before pasting so it receives the paste event.\n // autoFocus\n readOnly\n ref={textareaRef}\n />\n );\n }\n\n /** Map friendly file-type labels to accept= strings for the hidden input. */\n function buildAccept(): string | undefined {\n if (!fileTypes?.length) return undefined;\n return fileTypes\n .map((t) => {\n const ext = t.toLowerCase();\n if (ext === \"jpg\" || ext === \"jpeg\") return \".jpg,.jpeg\";\n return `.${ext}`;\n })\n .join(\",\");\n }\n\n function flashOverlay(color: string) {\n if (timerRef.current) clearTimeout(timerRef.current);\n setOverlayBackground(color);\n setOverlayOpacity(eventOpacity);\n const id = setTimeout(() => {\n setOverlayOpacity(0);\n timerRef.current = null;\n }, eventDurationSeconds * 1000);\n timerRef.current = id as unknown as number;\n }\n\n function handleFileChange(ev: React.ChangeEvent<HTMLInputElement>) {\n const file = ev.target.files?.[0];\n // Reset input so the same file can be re-selected later.\n ev.target.value = \"\";\n if (!file) return;\n\n const reader = new FileReader();\n reader.onload = (e) => {\n const content = (e.target?.result as string) ?? \"\";\n onPastedContent({items: [{mimeType: file.type, content}]});\n flashOverlay(successColor);\n };\n reader.onerror = () => {\n flashOverlay(errorColor);\n };\n reader.readAsDataURL(file);\n }\n\n function onUpload(ev: React.MouseEvent<HTMLButtonElement>) {\n ev.stopPropagation();\n ev.preventDefault();\n fileInputRef.current?.click();\n }\n\n function renderTitle() {\n const uploadBtn = !isFocused && withUpload && (\n <Button\n onClick={onUpload}\n style={{position: \"relative\", zIndex: 30, pointerEvents: \"auto\"}}\n >\n Upload\n </Button>\n );\n\n if (title) {\n return (\n <div className=\"paste-target-msg-title\">\n <h2>{title}</h2>\n {uploadBtn}\n </div>\n );\n } else {\n return (\n <div className=\"paste-target-msg-title\">\n Paste\n {uploadBtn && (\n <>\n &nbsp;or&nbsp;\n {uploadBtn}\n </>\n )}\n &nbsp;here\n </div>\n );\n }\n }\n\n const cn = _cls(\"root\", className || \"pihanga-paste-target\");\n\n return (\n <div\n className={clsx(cn, isFocused && \"pihanga-paste-target-focused\")}\n data-pihanga={cardName}\n style={{position: \"relative\", height, width: \"100%\"}}\n >\n <div\n className=\"paste-target-label\"\n style={{\n position: \"absolute\",\n top: 0,\n left: 0,\n right: 0,\n bottom: 0,\n display: \"flex\",\n flexDirection: \"column\",\n justifyContent: \"center\",\n alignItems: \"center\",\n textAlign: \"center\",\n // zIndex must be above textarea (10) so the Upload button is clickable.\n // pointer-events: none lets click/focus pass through to the textarea,\n // EXCEPT on children that override with pointer-events: auto (the button).\n pointerEvents: \"none\",\n zIndex: 20,\n }}\n >\n {renderTitle()}\n {description && (\n <span className=\"paste-target-msg-desc\">{description}</span>\n )}\n <div className=\"paste-target-focus-reminder\">{pasteFirstReminder}</div>\n </div>\n {renderPasteHandler()}\n {/* Hidden file input — triggered programmatically by the Upload button */}\n {withUpload && (\n <input\n type=\"file\"\n ref={fileInputRef}\n accept={buildAccept()}\n onChange={handleFileChange}\n style={{display: \"none\"}}\n />\n )}\n </div>\n );\n};\n"],"mappings":"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"}
1
+ {"version":3,"file":"pasteTarget.component.js","names":[],"sources":["../../../src/cards/pasteTarget/pasteTarget.component.tsx"],"sourcesContent":["import {\n DEF_ERROR_COLOR,\n DEF_EVENT_DURATION_SECONDS,\n DEF_HEIGHT,\n DEF_OVERLAY_OPACITY,\n DEF_PASTE_FIRST_REMINDER,\n DEF_SUCCESS_COLOR,\n type PasteItem,\n type PasteTargetEvents,\n type PasteTargetProps,\n} from \"@/cards/pasteTarget/pasteTarget.types\";\nimport type {PiCardProps} from \"@pihanga2/core\";\nimport React, {useRef, useState} from \"react\";\n\nimport \"./pasteTarget.css\";\nimport clsx from \"clsx\";\nimport {Button} from \"@/registry/ui/button\";\n\n// /* 3. Stretch the child to cover the parent entirely */\n// top: 0,\n// left: 0,\n// right: 0,\n// bottom: 0,\n\n// /* 4. Ensure the child is visually stacked above the parent's content */\n// z-index: 10,\n\n// /* Optional: Make the overlay visible (e.g., semi-transparent black) */\n// overlayOpacity: \"rgba(0, 0, 0, 0.5)\";\n// }\n\nexport const PasteTargetComponent = (\n props: PiCardProps<PasteTargetProps, PasteTargetEvents>,\n): React.ReactNode => {\n const {\n title, // = \"Paste content right here\",\n description,\n pasteFirstReminder = DEF_PASTE_FIRST_REMINDER,\n withUpload,\n fileTypes,\n\n height = DEF_HEIGHT,\n\n onPastedContent,\n\n successColor = DEF_SUCCESS_COLOR,\n errorColor = DEF_ERROR_COLOR,\n eventDurationSeconds = DEF_EVENT_DURATION_SECONDS,\n eventOpacity = DEF_OVERLAY_OPACITY,\n cardName,\n className,\n _cls,\n } = props;\n const [overlayOpacity, setOverlayOpacity] = useState<number>(0);\n const [overlayBackground, setOverlayBackground] = useState<string>(\"gray\");\n const timerRef = React.useRef<number | null>(null);\n const textareaRef = useRef<HTMLTextAreaElement>(null);\n const fileInputRef = useRef<HTMLInputElement>(null);\n const [isFocused, setIsFocused] = useState<boolean>(false);\n\n const overlayStyle: React.CSSProperties = {\n backgroundColor: overlayBackground,\n opacity: overlayOpacity,\n };\n\n const handlePaste = (event: React.ClipboardEvent<HTMLTextAreaElement>) => {\n if (timerRef.current) {\n clearTimeout(timerRef.current);\n }\n\n event.preventDefault();\n const clipboardData =\n event.clipboardData ||\n (window as Window & {clipboardData?: DataTransfer}).clipboardData;\n\n if (clipboardData) {\n const pItems = clipboardData.items;\n const items: PasteItem[] = [];\n for (let i = 0; i < pItems.length; i++) {\n const pi = pItems[i];\n const mimeType = pi.type;\n const content = clipboardData.getData(pi.type);\n if (!(content == \"\" || content == \"\\n\")) {\n items.push({mimeType, content});\n }\n }\n onPastedContent({items});\n setOverlayBackground(successColor);\n } else {\n setOverlayBackground(errorColor);\n }\n setOverlayOpacity(eventOpacity);\n if (textareaRef.current) textareaRef.current.blur();\n\n // Set a timeout to switch the color back after 'n' seconds\n const newTimerId = setTimeout(() => {\n setOverlayOpacity(0);\n timerRef.current = null; // Clear the ref after the timeout executes\n }, eventDurationSeconds * 1000); // Convert seconds to milliseconds\n\n // Save the new timer ID to the ref (unknown necessary type casting)\n timerRef.current = newTimerId as unknown as number;\n };\n\n // Cleanup function to clear the timer when the component unmounts\n React.useEffect(() => {\n return () => {\n const ref = timerRef.current;\n if (ref) {\n clearTimeout(ref);\n }\n };\n }, []); // runs only on mount/unmount\n\n function renderPasteHandler() {\n return (\n <textarea\n onPaste={handlePaste}\n onFocus={() => {\n setIsFocused(true);\n }}\n onBlur={() => setIsFocused(false)}\n style={{\n position: \"absolute\",\n top: 0,\n left: 0,\n right: 0,\n bottom: 0,\n zIndex: 10,\n resize: \"none\",\n background: \"transparent\",\n cursor: isFocused ? \"copy\" : \"pointer\",\n ...overlayStyle,\n }}\n // 🔑 CRUCIAL: Focus the element before pasting so it receives the paste event.\n // autoFocus\n readOnly\n ref={textareaRef}\n />\n );\n }\n\n /** Map friendly file-type labels to accept= strings for the hidden input. */\n function buildAccept(): string | undefined {\n if (!fileTypes?.length) return undefined;\n return fileTypes\n .map((t) => {\n const ext = t.toLowerCase();\n if (ext === \"jpg\" || ext === \"jpeg\") return \".jpg,.jpeg\";\n return `.${ext}`;\n })\n .join(\",\");\n }\n\n function flashOverlay(color: string) {\n if (timerRef.current) clearTimeout(timerRef.current);\n setOverlayBackground(color);\n setOverlayOpacity(eventOpacity);\n const id = setTimeout(() => {\n setOverlayOpacity(0);\n timerRef.current = null;\n }, eventDurationSeconds * 1000);\n timerRef.current = id as unknown as number;\n }\n\n function handleFileChange(ev: React.ChangeEvent<HTMLInputElement>) {\n const file = ev.target.files?.[0];\n // Reset input so the same file can be re-selected later.\n ev.target.value = \"\";\n if (!file) return;\n\n const reader = new FileReader();\n reader.onload = (e) => {\n const content = (e.target?.result as string) ?? \"\";\n onPastedContent({items: [{mimeType: file.type, content}]});\n flashOverlay(successColor);\n };\n reader.onerror = () => {\n flashOverlay(errorColor);\n };\n reader.readAsDataURL(file);\n }\n\n function onUpload(ev: React.MouseEvent<HTMLButtonElement>) {\n ev.stopPropagation();\n ev.preventDefault();\n fileInputRef.current?.click();\n }\n\n function renderTitle() {\n const uploadBtn = !isFocused && withUpload && (\n <Button\n onClick={onUpload}\n style={{position: \"relative\", zIndex: 30, pointerEvents: \"auto\"}}\n >\n Upload\n </Button>\n );\n\n if (title) {\n return (\n <div className=\"paste-target-msg-title\">\n <h2>{title}</h2>\n {uploadBtn}\n </div>\n );\n } else {\n return (\n <div className=\"paste-target-msg-title\">\n Paste\n {uploadBtn && (\n <>\n &nbsp;or&nbsp;\n {uploadBtn}\n </>\n )}\n &nbsp;here\n </div>\n );\n }\n }\n\n const cn = _cls(\"root\", className || \"pihanga-paste-target\");\n\n return (\n <div\n className={clsx(cn, isFocused && \"pihanga-paste-target-focused\")}\n data-pihanga={cardName}\n style={{position: \"relative\", height, width: \"100%\"}}\n >\n <div\n className=\"paste-target-label\"\n style={{\n position: \"absolute\",\n top: 0,\n left: 0,\n right: 0,\n bottom: 0,\n display: \"flex\",\n flexDirection: \"column\",\n justifyContent: \"center\",\n alignItems: \"center\",\n textAlign: \"center\",\n // zIndex must be above textarea (10) so the Upload button is clickable.\n // pointer-events: none lets click/focus pass through to the textarea,\n // EXCEPT on children that override with pointer-events: auto (the button).\n pointerEvents: \"none\",\n zIndex: 20,\n }}\n >\n {renderTitle()}\n {description && (\n <span className=\"paste-target-msg-desc\">{description}</span>\n )}\n <div className=\"paste-target-focus-reminder\">{pasteFirstReminder}</div>\n </div>\n {renderPasteHandler()}\n {/* Hidden file input — triggered programmatically by the Upload button */}\n {withUpload && (\n <input\n type=\"file\"\n ref={fileInputRef}\n accept={buildAccept()}\n onChange={handleFileChange}\n style={{display: \"none\"}}\n />\n )}\n </div>\n );\n};\n"],"mappings":"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"}
@@ -1,13 +1,13 @@
1
1
  import { SelectComponent as e } from "./select.component.js";
2
- import { PI_SELECT_ACTION as t, PI_SELECT_CARD as n, Select as r, onPiSelectChanged as i, onPiSelectClosed as a, onPiSelectOpened as o } from "./select.types.js";
3
- import { actionTypesToEvents as s, registerCardComponent as c } from "@pihanga2/core";
2
+ import { PI_SELECT_ACTION as t, PI_SELECT_CARD as n, Select as r, onPiSelectChanged as i, onPiSelectClosed as a, onPiSelectOpened as o, onSelectChanged as s, onSelectClosed as c, onSelectOpened as l } from "./select.types.js";
3
+ import { actionTypesToEvents as u, registerCardComponent as d } from "@pihanga2/core";
4
4
  //#region src/cards/select/index.ts
5
- c({
5
+ d({
6
6
  name: n,
7
7
  component: e,
8
- events: s(t)
8
+ events: u(t)
9
9
  });
10
10
  //#endregion
11
- export { t as PI_SELECT_ACTION, n as PI_SELECT_CARD, r as Select, i as onPiSelectChanged, a as onPiSelectClosed, o as onPiSelectOpened };
11
+ export { t as PI_SELECT_ACTION, n as PI_SELECT_CARD, r as Select, i as onPiSelectChanged, a as onPiSelectClosed, o as onPiSelectOpened, s as onSelectChanged, c as onSelectClosed, l as onSelectOpened };
12
12
 
13
13
  //# sourceMappingURL=index.js.map