@pihanga2/shadcn 0.2.14 → 0.2.17

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 (52) hide show
  1. package/AGENT.building-cards.md +68 -0
  2. package/AGENT.using-cards.md +15 -3
  3. package/README.md +3 -1
  4. package/cards/conditional/conditional.component.js +30 -12
  5. package/cards/conditional/conditional.component.js.map +1 -1
  6. package/cards/conditional/conditional.types.d.ts +21 -3
  7. package/cards/conditional/conditional.types.js +1 -1
  8. package/cards/conditional/conditional.types.js.map +1 -1
  9. package/cards/fileDrop/fileDrop.component.js +64 -49
  10. package/cards/fileDrop/fileDrop.component.js.map +1 -1
  11. package/cards/fileDrop/fileDrop.css +1 -1
  12. package/cards/fileDrop/fileDrop.types.d.ts +49 -0
  13. package/cards/fileDrop/fileDrop.types.js +9 -2
  14. package/cards/fileDrop/fileDrop.types.js.map +1 -1
  15. package/cards/fileDrop/index.js +14 -8
  16. package/cards/fileDrop/index.js.map +1 -1
  17. package/cards/jsonViewer/jsonViewer.css +1 -1
  18. package/cards/keyboardOverlay/index.d.ts +1 -0
  19. package/cards/keyboardOverlay/index.js +13 -0
  20. package/cards/keyboardOverlay/index.js.map +1 -0
  21. package/cards/keyboardOverlay/keyboardOverlay.component.d.ts +4 -0
  22. package/cards/keyboardOverlay/keyboardOverlay.component.js +76 -0
  23. package/cards/keyboardOverlay/keyboardOverlay.component.js.map +1 -0
  24. package/cards/keyboardOverlay/keyboardOverlay.types.d.ts +92 -0
  25. package/cards/keyboardOverlay/keyboardOverlay.types.js +7 -0
  26. package/cards/keyboardOverlay/keyboardOverlay.types.js.map +1 -0
  27. package/cards/loadingOverlay/loadingOverlay.css +1 -1
  28. package/cards/modeToggle/mode-toggle.component.js +1 -1
  29. package/cards/modeToggle/mode-toggle.component.js.map +1 -1
  30. package/cards/pageWithNavbar/pageWithNavbar.css +1 -1
  31. package/cards/pasteTarget/pasteTarget.css +1 -1
  32. package/cards/resizableColumns/index.d.ts +1 -0
  33. package/cards/resizableColumns/resizableColumns.component.d.ts +4 -0
  34. package/cards/resizableColumns/resizableColumns.types.d.ts +22 -0
  35. package/cards/resizableGrid/index.d.ts +1 -0
  36. package/cards/resizableGrid/resizableGrid.component.d.ts +4 -0
  37. package/cards/resizableGrid/resizableGrid.types.d.ts +33 -0
  38. package/cards/scrollbarWithAnnotations/scrollbarWithAnnotations.css +1 -1
  39. package/cards/suspense/index.d.ts +1 -0
  40. package/cards/suspense/index.js +12 -0
  41. package/cards/suspense/index.js.map +1 -0
  42. package/cards/suspense/suspense.component.d.ts +15 -0
  43. package/cards/suspense/suspense.component.js +45 -0
  44. package/cards/suspense/suspense.component.js.map +1 -0
  45. package/cards/suspense/suspense.types.d.ts +67 -0
  46. package/cards/suspense/suspense.types.js +7 -0
  47. package/cards/suspense/suspense.types.js.map +1 -0
  48. package/cards/typography/typography.component.js +13 -12
  49. package/cards/typography/typography.component.js.map +1 -1
  50. package/cards/typography/typography.types.d.ts +2 -0
  51. package/cards/typography/typography.types.js.map +1 -1
  52. package/package.json +12 -2
@@ -1,13 +1,19 @@
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({
1
+ import { DEF_FILE_DROP_FILE_TYPES as e, FILE_DROP_ACTION as t, FILE_DROP_CARD as n, FileDrop as r, getFileDropTheme as i, onFileDropError as a, onFileDropped as o, registerFileDropTheme as s } from "./fileDrop.types.js";
2
+ import { FileDropComponent as c, get_last_dropped as l } from "./fileDrop.component.js";
3
+ import { actionTypesToEvents as u, registerCardComponent as d } from "@pihanga2/core";
4
+ d({
6
5
  name: n,
7
- component: o,
8
- events: c(t)
6
+ component: c,
7
+ events: u(t)
8
+ }), s("default-1", {
9
+ root: "flex items-center justify-center p-8 rounded-2xl border border-dashed border-slate-300 bg-slate-50",
10
+ dropZone: "flex flex-col items-center gap-3 text-center",
11
+ icon: "flex items-center justify-center size-12 rounded-xl bg-slate-100",
12
+ title: "text-lg font-bold text-slate-900",
13
+ description: "text-sm text-slate-500 max-w-xs",
14
+ browseButton: "mt-2 px-5 py-2 rounded-lg bg-slate-900 text-white text-sm font-semibold cursor-pointer"
9
15
  });
10
16
  //#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 };
17
+ export { e as DEF_FILE_DROP_FILE_TYPES, t as FILE_DROP_ACTION, n as FILE_DROP_CARD, r as FileDrop, i as getFileDropTheme, l as get_last_dropped, a as onFileDropError, o as onFileDropped, s as registerFileDropTheme };
12
18
 
13
19
  //# sourceMappingURL=index.js.map
@@ -1 +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
+ {"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 {\n FILE_DROP_ACTION,\n FILE_DROP_CARD,\n registerFileDropTheme,\n} 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\nregisterFileDropTheme(\"default-1\", {\n root: \"flex items-center justify-center p-8 rounded-2xl border border-dashed border-slate-300 bg-slate-50\",\n dropZone: \"flex flex-col items-center gap-3 text-center\",\n icon: \"flex items-center justify-center size-12 rounded-xl bg-slate-100\",\n title: \"text-lg font-bold text-slate-900\",\n description: \"text-sm text-slate-500 max-w-xs\",\n browseButton:\n \"mt-2 px-5 py-2 rounded-lg bg-slate-900 text-white text-sm font-semibold cursor-pointer\",\n});\n"],"mappings":";;;AAYA,EAAsB;CACpB,MAAM;CACN,WAAW;CACX,QAAQ,EAAoB,CAAgB;AAC9C,CAAC,GAED,EAAsB,aAAa;CACjC,MAAM;CACN,UAAU;CACV,MAAM;CACN,OAAO;CACP,aAAa;CACb,cACE;AACJ,CAAC"}
@@ -1 +1 @@
1
- .jv-wrapper{position:relative}.jv-copy-btn{z-index:10;width:22px;height:22px;color:hsl(var(--muted-foreground,0 0% 55%));cursor:pointer;opacity:.35;background:0 0;border:1px solid #0000;border-radius:4px;justify-content:center;align-items:center;padding:0;transition:opacity .15s,color .15s,background .15s,border-color .15s;display:inline-flex;position:absolute;top:4px;right:4px}.jv-wrapper:hover .jv-copy-btn,.jv-wrapper:focus-within .jv-copy-btn{opacity:1}.jv-copy-btn:hover{color:hsl(var(--foreground));background:hsl(var(--muted,0 0% 96%));border-color:hsl(var(--border,0 0% 88%))}.jv-copy-btn--done{opacity:1;color:#2e7d32}.dark .jv-copy-btn--done{color:#81c784}.jv-container{white-space:pre-wrap;word-wrap:break-word;color:var(--foreground);background:0 0;font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,monospace;font-size:12.5px;font-weight:400;line-height:1.55}.jv-basic-child{margin:0;padding:0 0 0 1.1em;display:block;position:relative}.jv-label{color:var(--foreground);margin-right:0;font-weight:400}.jv-clickable-label{color:var(--foreground);cursor:pointer;margin-right:0;font-weight:400}.jv-clickable-label:hover{-webkit-text-decoration:underline dotted;text-decoration:underline dotted}.jv-punctuation{color:var(--foreground);font-weight:400}.jv-expand-icon,.jv-collapse-icon{width:1em;color:hsl(var(--muted-foreground,0 0% 55%));-webkit-user-select:none;user-select:none;cursor:pointer;justify-content:center;align-items:center;font-size:.65em;line-height:1;display:inline-flex;position:absolute;top:.775em;left:0;transform:translateY(-50%)}.jv-expand-icon:after{content:"▶"}.jv-collapse-icon:after{content:"▼"}.jv-collapsed-content{cursor:pointer;color:hsl(var(--muted-foreground,0 0% 55%))}.jv-collapsed-content:after{content:" …"}.jv-child-fields-container{border-left:1px solid hsl(var(--border,0 0% 88%));margin:0 0 0 .3em;padding:0 0 0 1.25em}.jv-null,.jv-undefined{color:#c0392b}.jv-string{color:#2e7d32}.jv-number{color:#1565c0}.jv-boolean{color:#00796b}.jv-other{color:var(--foreground)}.dark .jv-string{color:#81c784}.dark .jv-number{color:#64b5f6}.dark .jv-boolean{color:#4db6ac}.dark .jv-null,.dark .jv-undefined{color:#ef9a9a}.dark .jv-child-fields-container{border-left-color:hsl(var(--border,0 0% 25%))}
1
+ :root{--jv-color-string:oklch(45% .15 145);--jv-color-number:oklch(40% .18 265);--jv-color-boolean:oklch(45% .1 188);--jv-color-null:oklch(45% .2 27);--jv-color-done:oklch(45% .15 145)}.dark{--jv-color-string:oklch(70% .12 145);--jv-color-number:oklch(68% .14 265);--jv-color-boolean:oklch(65% .1 188);--jv-color-null:oklch(73% .14 27);--jv-color-done:oklch(70% .12 145)}.jv-wrapper{position:relative}.jv-copy-btn{z-index:10;border-radius:var(--radius-sm);width:22px;height:22px;color:var(--muted-foreground);cursor:pointer;opacity:.35;background:0 0;border:1px solid #0000;justify-content:center;align-items:center;padding:0;transition:opacity .15s,color .15s,background .15s,border-color .15s;display:inline-flex;position:absolute;top:4px;right:4px}.jv-wrapper:hover .jv-copy-btn,.jv-wrapper:focus-within .jv-copy-btn{opacity:1}.jv-copy-btn:hover{color:var(--foreground);background:var(--muted);border-color:var(--border)}.jv-copy-btn--done{opacity:1;color:var(--jv-color-done)}.jv-container{white-space:pre-wrap;word-wrap:break-word;color:var(--foreground);background:0 0;font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,monospace;font-size:12.5px;font-weight:400;line-height:1.55}.jv-basic-child{margin:0;padding:0 0 0 1.1em;display:block;position:relative}.jv-label{color:var(--foreground);margin-right:0;font-weight:400}.jv-clickable-label{color:var(--foreground);cursor:pointer;margin-right:0;font-weight:400}.jv-clickable-label:hover{-webkit-text-decoration:underline dotted;text-decoration:underline dotted}.jv-punctuation{color:var(--foreground);font-weight:400}.jv-expand-icon,.jv-collapse-icon{width:1em;color:var(--muted-foreground);-webkit-user-select:none;user-select:none;cursor:pointer;justify-content:center;align-items:center;font-size:.65em;line-height:1;display:inline-flex;position:absolute;top:.775em;left:0;transform:translateY(-50%)}.jv-expand-icon:after{content:"▶"}.jv-collapse-icon:after{content:"▼"}.jv-collapsed-content{cursor:pointer;color:var(--muted-foreground)}.jv-collapsed-content:after{content:" …"}.jv-child-fields-container{border-left:1px solid var(--border);margin:0 0 0 .3em;padding:0 0 0 1.25em}.jv-null,.jv-undefined{color:var(--jv-color-null)}.jv-string{color:var(--jv-color-string)}.jv-number{color:var(--jv-color-number)}.jv-boolean{color:var(--jv-color-boolean)}.jv-other{color:var(--foreground)}
@@ -0,0 +1 @@
1
+ export * from './keyboardOverlay.types';
@@ -0,0 +1,13 @@
1
+ import { KeyboardOverlayComponent as e } from "./keyboardOverlay.component.js";
2
+ import { KEYBOARD_OVERLAY_ACTION as t, KEYBOARD_OVERLAY_CARD as n, KeyboardOverlay as r, onKeyboardShortcut as i } from "./keyboardOverlay.types.js";
3
+ import { actionTypesToEvents as a, registerCardComponent as o } from "@pihanga2/core";
4
+ //#region src/cards/keyboardOverlay/index.ts
5
+ o({
6
+ name: n,
7
+ component: e,
8
+ events: a(t)
9
+ });
10
+ //#endregion
11
+ export { t as KEYBOARD_OVERLAY_ACTION, n as KEYBOARD_OVERLAY_CARD, r as KeyboardOverlay, i as onKeyboardShortcut };
12
+
13
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","names":[],"sources":["../../../src/cards/keyboardOverlay/index.ts"],"sourcesContent":["import {actionTypesToEvents, registerCardComponent} from \"@pihanga2/core\";\n\nimport {KeyboardOverlayComponent} from \"./keyboardOverlay.component\";\nimport {\n KEYBOARD_OVERLAY_ACTION,\n KEYBOARD_OVERLAY_CARD,\n} from \"./keyboardOverlay.types\";\n\nexport * from \"./keyboardOverlay.types\";\n\nregisterCardComponent({\n name: KEYBOARD_OVERLAY_CARD,\n component: KeyboardOverlayComponent,\n events: actionTypesToEvents(KEYBOARD_OVERLAY_ACTION),\n});\n"],"mappings":";;;;AAUA,EAAsB;CACpB,MAAM;CACN,WAAW;CACX,QAAQ,EAAoB,CAAuB;AACrD,CAAC"}
@@ -0,0 +1,4 @@
1
+ import { PiCardProps } from '@pihanga2/core';
2
+ import { default as React } from 'react';
3
+ import { KeyboardOverlayEvents, KeyboardOverlayProps } from './keyboardOverlay.types';
4
+ export declare const KeyboardOverlayComponent: (props: PiCardProps<KeyboardOverlayProps, KeyboardOverlayEvents>) => React.ReactNode;
@@ -0,0 +1,76 @@
1
+ import { useEffect as e, useRef as t } from "react";
2
+ import { Card as n } from "@pihanga2/core";
3
+ import { jsx as r } from "react/jsx-runtime";
4
+ //#region src/cards/keyboardOverlay/keyboardOverlay.component.tsx
5
+ function i(e, t) {
6
+ let n = e;
7
+ for (; n;) {
8
+ let e = n.getAttribute(`data-${t}`);
9
+ if (e) return e;
10
+ n = n.parentElement;
11
+ }
12
+ }
13
+ function a(e, t) {
14
+ let n = new Set(t);
15
+ return (n.has("ctrl") ? e.ctrlKey : !e.ctrlKey) && (n.has("shift") ? e.shiftKey : !e.shiftKey) && (n.has("alt") ? e.altKey : !e.altKey) && (n.has("meta") ? e.metaKey : !e.metaKey);
16
+ }
17
+ var o = (o) => {
18
+ let { content: s, shortcuts: c, onShortcut: l, cardName: u, className: d, style: f, captureFocus: p = !0, dataKey: m = "pihanga" } = o, h = t({
19
+ x: 0,
20
+ y: 0
21
+ }), g = t(null);
22
+ e(() => {
23
+ let e = (e) => {
24
+ h.current = {
25
+ x: e.clientX,
26
+ y: e.clientY
27
+ };
28
+ }, t = (e) => {
29
+ for (let t of c) {
30
+ let n = t.modifiers ?? [];
31
+ if (!(e.key === t.key || e.code === t.key) || !a(e, n)) continue;
32
+ t.propagate || (e.preventDefault(), e.stopPropagation());
33
+ let { x: r, y: o } = h.current, s = i(document.elementFromPoint(r, o), m);
34
+ l({
35
+ shortcutId: t.id ?? t.key,
36
+ key: t.key,
37
+ modifiers: n,
38
+ dataValue: s,
39
+ cursorX: r,
40
+ cursorY: o
41
+ });
42
+ break;
43
+ }
44
+ };
45
+ return document.addEventListener("mousemove", e, { passive: !0 }), document.addEventListener("keydown", t, { capture: !0 }), () => {
46
+ document.removeEventListener("mousemove", e), document.removeEventListener("keydown", t, { capture: !0 });
47
+ };
48
+ }, [
49
+ c,
50
+ l,
51
+ m
52
+ ]);
53
+ function _() {
54
+ p && g.current?.focus();
55
+ }
56
+ return /* @__PURE__ */ r("div", {
57
+ ref: g,
58
+ tabIndex: p ? -1 : void 0,
59
+ onMouseEnter: _,
60
+ "data-pihanga": u,
61
+ className: d,
62
+ style: {
63
+ ...f,
64
+ position: "relative",
65
+ outline: "none"
66
+ },
67
+ children: /* @__PURE__ */ r(n, {
68
+ cardName: s,
69
+ parentCard: u
70
+ })
71
+ });
72
+ };
73
+ //#endregion
74
+ export { o as KeyboardOverlayComponent };
75
+
76
+ //# sourceMappingURL=keyboardOverlay.component.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"keyboardOverlay.component.js","names":[],"sources":["../../../src/cards/keyboardOverlay/keyboardOverlay.component.tsx"],"sourcesContent":["import {Card, type PiCardProps} from \"@pihanga2/core\";\nimport React, {useEffect, useRef} from \"react\";\n\nimport type {\n KeyboardOverlayEvents,\n KeyboardOverlayProps,\n Modifier,\n} from \"./keyboardOverlay.types\";\n\n/** Walk up the DOM from `el` and return the first value of `data-{dataKey}` found. */\nfunction findDataAttribute(\n el: Element | null,\n dataKey: string,\n): string | undefined {\n let node: Element | null = el;\n while (node) {\n const attr = node.getAttribute(`data-${dataKey}`);\n if (attr) return attr;\n node = node.parentElement;\n }\n return undefined;\n}\n\n/** Returns `true` when the keyboard event's active modifiers exactly match the definition. */\nfunction modifiersMatch(e: KeyboardEvent, required: Modifier[]): boolean {\n const need = new Set(required);\n return (\n (need.has(\"ctrl\") ? e.ctrlKey : !e.ctrlKey) &&\n (need.has(\"shift\") ? e.shiftKey : !e.shiftKey) &&\n (need.has(\"alt\") ? e.altKey : !e.altKey) &&\n (need.has(\"meta\") ? e.metaKey : !e.metaKey)\n );\n}\n\nexport const KeyboardOverlayComponent = (\n props: PiCardProps<KeyboardOverlayProps, KeyboardOverlayEvents>,\n): React.ReactNode => {\n const {\n content,\n shortcuts,\n onShortcut,\n cardName,\n className,\n style,\n captureFocus = true,\n dataKey = \"pihanga\",\n } = props;\n\n // Track cursor position passively — no re-render needed.\n const cursorPos = useRef<{x: number; y: number}>({x: 0, y: 0});\n const divRef = useRef<HTMLDivElement>(null);\n\n useEffect(() => {\n const onMouseMove = (e: MouseEvent) => {\n cursorPos.current = {x: e.clientX, y: e.clientY};\n };\n\n const onKeyDown = (e: KeyboardEvent) => {\n for (const shortcut of shortcuts) {\n const mods: Modifier[] = shortcut.modifiers ?? [];\n const keyMatch = e.key === shortcut.key || e.code === shortcut.key;\n if (!keyMatch || !modifiersMatch(e, mods)) continue;\n\n // Only consume the event when propagate !== true.\n if (!shortcut.propagate) {\n e.preventDefault();\n e.stopPropagation();\n }\n\n const {x, y} = cursorPos.current;\n const el = document.elementFromPoint(x, y);\n const dataValue = findDataAttribute(el, dataKey);\n\n onShortcut({\n shortcutId: shortcut.id ?? shortcut.key,\n key: shortcut.key,\n modifiers: mods,\n dataValue,\n cursorX: x,\n cursorY: y,\n });\n break;\n }\n };\n\n // capture:true ensures we see the keydown before any focused child element.\n document.addEventListener(\"mousemove\", onMouseMove, {passive: true});\n document.addEventListener(\"keydown\", onKeyDown, {capture: true});\n return () => {\n document.removeEventListener(\"mousemove\", onMouseMove);\n document.removeEventListener(\"keydown\", onKeyDown, {capture: true});\n };\n }, [shortcuts, onShortcut, dataKey]);\n\n function onMouseEnter() {\n if (captureFocus) {\n divRef.current?.focus();\n }\n }\n\n return (\n <div\n ref={divRef}\n tabIndex={captureFocus ? -1 : undefined}\n onMouseEnter={onMouseEnter}\n data-pihanga={cardName}\n className={className}\n style={{...style, position: \"relative\", outline: \"none\"}}\n >\n <Card cardName={content} parentCard={cardName} />\n </div>\n );\n};\n"],"mappings":";;;;AAUA,SAAS,EACP,GACA,GACoB;CACpB,IAAI,IAAuB;CAC3B,OAAO,IAAM;EACX,IAAM,IAAO,EAAK,aAAa,QAAQ,GAAS;EAChD,IAAI,GAAM,OAAO;EACjB,IAAO,EAAK;CACd;AAEF;AAGA,SAAS,EAAe,GAAkB,GAA+B;CACvE,IAAM,IAAO,IAAI,IAAI,CAAQ;CAC7B,QACG,EAAK,IAAI,MAAM,IAAI,EAAE,UAAU,CAAC,EAAE,aAClC,EAAK,IAAI,OAAO,IAAI,EAAE,WAAW,CAAC,EAAE,cACpC,EAAK,IAAI,KAAK,IAAI,EAAE,SAAS,CAAC,EAAE,YAChC,EAAK,IAAI,MAAM,IAAI,EAAE,UAAU,CAAC,EAAE;AAEvC;AAEA,IAAa,KACX,MACoB;CACpB,IAAM,EACJ,YACA,cACA,eACA,aACA,cACA,UACA,kBAAe,IACf,aAAU,cACR,GAGE,IAAY,EAA+B;EAAC,GAAG;EAAG,GAAG;CAAC,CAAC,GACvD,IAAS,EAAuB,IAAI;CAE1C,QAAgB;EACd,IAAM,KAAe,MAAkB;GACrC,EAAU,UAAU;IAAC,GAAG,EAAE;IAAS,GAAG,EAAE;GAAO;EACjD,GAEM,KAAa,MAAqB;GACtC,KAAK,IAAM,KAAY,GAAW;IAChC,IAAM,IAAmB,EAAS,aAAa,CAAC;IAEhD,IAAI,EADa,EAAE,QAAQ,EAAS,OAAO,EAAE,SAAS,EAAS,QAC9C,CAAC,EAAe,GAAG,CAAI,GAAG;IAG3C,AAAK,EAAS,cACZ,EAAE,eAAe,GACjB,EAAE,gBAAgB;IAGpB,IAAM,EAAC,MAAG,SAAK,EAAU,SAEnB,IAAY,EADP,SAAS,iBAAiB,GAAG,CACJ,GAAI,CAAO;IAE/C,EAAW;KACT,YAAY,EAAS,MAAM,EAAS;KACpC,KAAK,EAAS;KACd,WAAW;KACX;KACA,SAAS;KACT,SAAS;IACX,CAAC;IACD;GACF;EACF;EAKA,OAFA,SAAS,iBAAiB,aAAa,GAAa,EAAC,SAAS,GAAI,CAAC,GACnE,SAAS,iBAAiB,WAAW,GAAW,EAAC,SAAS,GAAI,CAAC,SAClD;GAEX,AADA,SAAS,oBAAoB,aAAa,CAAW,GACrD,SAAS,oBAAoB,WAAW,GAAW,EAAC,SAAS,GAAI,CAAC;EACpE;CACF,GAAG;EAAC;EAAW;EAAY;CAAO,CAAC;CAEnC,SAAS,IAAe;EACtB,AAAI,KACF,EAAO,SAAS,MAAM;CAE1B;CAEA,OACE,kBAAC,OAAD;EACE,KAAK;EACL,UAAU,IAAe,KAAK,KAAA;EAChB;EACd,gBAAc;EACH;EACX,OAAO;GAAC,GAAG;GAAO,UAAU;GAAY,SAAS;EAAM;YAEvD,kBAAC,GAAD;GAAM,UAAU;GAAS,YAAY;EAAW,CAAA;CAC7C,CAAA;AAET"}
@@ -0,0 +1,92 @@
1
+ import { PiCardRef } from '@pihanga2/core';
2
+ export declare const KEYBOARD_OVERLAY_CARD = "keyboard-overlay";
3
+ export declare const KeyboardOverlay: <S extends import('@pihanga2/core').ReduxState>(p: import('@pihanga2/core').PiMapProps<KeyboardOverlayProps, S, KeyboardOverlayEvents>) => import('@pihanga2/core').PiCardDef;
4
+ export declare const KEYBOARD_OVERLAY_ACTION: {
5
+ SHORTCUT: string;
6
+ };
7
+ export declare const onKeyboardShortcut: <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
+ } & KeyboardOverlayShortcutEvent>) => void;
10
+ /** Modifier keys that can be part of a shortcut definition. */
11
+ export type Modifier = "ctrl" | "shift" | "alt" | "meta";
12
+ /**
13
+ * A single keyboard shortcut to intercept.
14
+ *
15
+ * `key` matches `KeyboardEvent.key` (e.g. `"k"`, `"Escape"`, `"ArrowUp"`) or
16
+ * `KeyboardEvent.code` (e.g. `"KeyK"`).
17
+ *
18
+ * `modifiers` lists the modifier keys that **must** be active; any modifier
19
+ * not listed is expected to be **inactive** (strict matching).
20
+ *
21
+ * `id` is an optional caller-defined label that is forwarded in the event so
22
+ * you can distinguish shortcuts without pattern-matching on key+modifiers.
23
+ *
24
+ * `propagate` controls whether the key event continues down the DOM tree after
25
+ * the overlay handles it. Defaults to `false` (the event is consumed here).
26
+ * Set to `true` to fire the pihanga event **and** still let the browser /
27
+ * focused child element see the keystroke.
28
+ */
29
+ export type ShortcutDef = {
30
+ key: string;
31
+ modifiers?: Modifier[];
32
+ id?: string;
33
+ /** When `true` the key event is NOT stopped — it propagates normally after
34
+ * the pihanga `onShortcut` event fires. Default: `false`. */
35
+ propagate?: boolean;
36
+ };
37
+ export type KeyboardOverlayProps = {
38
+ /** Child card to render beneath the overlay. */
39
+ content: PiCardRef;
40
+ /** Shortcuts to intercept; all other key events pass through. */
41
+ shortcuts: ShortcutDef[];
42
+ /**
43
+ * When `true` (default) the wrapper `<div>` is made focusable (`tabIndex -1`)
44
+ * and automatically focused on mount, giving it the keyboard context so that
45
+ * shortcuts are reliably delivered even inside iframes or when an outer element
46
+ * would otherwise hold focus.
47
+ *
48
+ * Set to `false` only if you need focus to stay on a specific child element
49
+ * (e.g. a text input) right from the start; the document-level capture listener
50
+ * will still handle shortcuts, but without focus ownership.
51
+ */
52
+ captureFocus?: boolean;
53
+ /**
54
+ * When set, the overlay walks up the DOM from the cursor position looking for
55
+ * `data-{dataKey}` instead of the default `data-pihanga` attribute.
56
+ * The found value is forwarded as `dataValue` in the shortcut event.
57
+ */
58
+ dataKey?: string;
59
+ /**
60
+ * Extra CSS classes applied to the wrapper `<div>`.
61
+ *
62
+ * Useful for layout constraints, e.g. `"h-full w-full flex"`.
63
+ * `position: relative` is always applied and cannot be overridden.
64
+ */
65
+ className?: string;
66
+ /**
67
+ * Inline styles merged onto the wrapper `<div>`.
68
+ *
69
+ * `position: relative` is always applied and takes precedence.
70
+ */
71
+ style?: React.CSSProperties;
72
+ };
73
+ /**
74
+ * Payload emitted by `onShortcut` whenever a registered shortcut fires.
75
+ *
76
+ * `dataValue` is the value of the `data-{dataKey}` attribute (defaulting to
77
+ * `data-pihanga`) of the **lowest** DOM element under the cursor at the moment
78
+ * the key was pressed, walking up the tree until a match is found.
79
+ * `undefined` when no matching ancestor exists.
80
+ */
81
+ export type KeyboardOverlayShortcutEvent = {
82
+ /** `ShortcutDef.id` when set, otherwise the matched `key`. */
83
+ shortcutId: string;
84
+ key: string;
85
+ modifiers: Modifier[];
86
+ dataValue?: string;
87
+ cursorX: number;
88
+ cursorY: number;
89
+ };
90
+ export type KeyboardOverlayEvents = {
91
+ onShortcut: KeyboardOverlayShortcutEvent;
92
+ };
@@ -0,0 +1,7 @@
1
+ import { createCardDeclaration as e, createOnAction as t, registerActions as n } from "@pihanga2/core";
2
+ //#region src/cards/keyboardOverlay/keyboardOverlay.types.ts
3
+ var r = "keyboard-overlay", i = e(r), a = n(r, ["shortcut"]), o = t(a.SHORTCUT);
4
+ //#endregion
5
+ export { a as KEYBOARD_OVERLAY_ACTION, r as KEYBOARD_OVERLAY_CARD, i as KeyboardOverlay, o as onKeyboardShortcut };
6
+
7
+ //# sourceMappingURL=keyboardOverlay.types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"keyboardOverlay.types.js","names":[],"sources":["../../../src/cards/keyboardOverlay/keyboardOverlay.types.ts"],"sourcesContent":["import {\n type PiCardRef,\n createCardDeclaration,\n createOnAction,\n registerActions,\n} from \"@pihanga2/core\";\n\nexport const KEYBOARD_OVERLAY_CARD = \"keyboard-overlay\";\n\nexport const KeyboardOverlay = createCardDeclaration<\n KeyboardOverlayProps,\n KeyboardOverlayEvents\n>(KEYBOARD_OVERLAY_CARD);\n\nexport const KEYBOARD_OVERLAY_ACTION = registerActions(KEYBOARD_OVERLAY_CARD, [\n \"shortcut\",\n]);\n\nexport const onKeyboardShortcut = createOnAction<KeyboardOverlayShortcutEvent>(\n KEYBOARD_OVERLAY_ACTION.SHORTCUT,\n);\n\n/** Modifier keys that can be part of a shortcut definition. */\nexport type Modifier = \"ctrl\" | \"shift\" | \"alt\" | \"meta\";\n\n/**\n * A single keyboard shortcut to intercept.\n *\n * `key` matches `KeyboardEvent.key` (e.g. `\"k\"`, `\"Escape\"`, `\"ArrowUp\"`) or\n * `KeyboardEvent.code` (e.g. `\"KeyK\"`).\n *\n * `modifiers` lists the modifier keys that **must** be active; any modifier\n * not listed is expected to be **inactive** (strict matching).\n *\n * `id` is an optional caller-defined label that is forwarded in the event so\n * you can distinguish shortcuts without pattern-matching on key+modifiers.\n *\n * `propagate` controls whether the key event continues down the DOM tree after\n * the overlay handles it. Defaults to `false` (the event is consumed here).\n * Set to `true` to fire the pihanga event **and** still let the browser /\n * focused child element see the keystroke.\n */\nexport type ShortcutDef = {\n key: string;\n modifiers?: Modifier[];\n id?: string;\n /** When `true` the key event is NOT stopped — it propagates normally after\n * the pihanga `onShortcut` event fires. Default: `false`. */\n propagate?: boolean;\n};\n\nexport type KeyboardOverlayProps = {\n /** Child card to render beneath the overlay. */\n content: PiCardRef;\n /** Shortcuts to intercept; all other key events pass through. */\n shortcuts: ShortcutDef[];\n /**\n * When `true` (default) the wrapper `<div>` is made focusable (`tabIndex -1`)\n * and automatically focused on mount, giving it the keyboard context so that\n * shortcuts are reliably delivered even inside iframes or when an outer element\n * would otherwise hold focus.\n *\n * Set to `false` only if you need focus to stay on a specific child element\n * (e.g. a text input) right from the start; the document-level capture listener\n * will still handle shortcuts, but without focus ownership.\n */\n captureFocus?: boolean;\n /**\n * When set, the overlay walks up the DOM from the cursor position looking for\n * `data-{dataKey}` instead of the default `data-pihanga` attribute.\n * The found value is forwarded as `dataValue` in the shortcut event.\n */\n dataKey?: string;\n /**\n * Extra CSS classes applied to the wrapper `<div>`.\n *\n * Useful for layout constraints, e.g. `\"h-full w-full flex\"`.\n * `position: relative` is always applied and cannot be overridden.\n */\n className?: string;\n /**\n * Inline styles merged onto the wrapper `<div>`.\n *\n * `position: relative` is always applied and takes precedence.\n */\n style?: React.CSSProperties;\n};\n\n/**\n * Payload emitted by `onShortcut` whenever a registered shortcut fires.\n *\n * `dataValue` is the value of the `data-{dataKey}` attribute (defaulting to\n * `data-pihanga`) of the **lowest** DOM element under the cursor at the moment\n * the key was pressed, walking up the tree until a match is found.\n * `undefined` when no matching ancestor exists.\n */\nexport type KeyboardOverlayShortcutEvent = {\n /** `ShortcutDef.id` when set, otherwise the matched `key`. */\n shortcutId: string;\n key: string;\n modifiers: Modifier[];\n dataValue?: string;\n cursorX: number;\n cursorY: number;\n};\n\nexport type KeyboardOverlayEvents = {\n onShortcut: KeyboardOverlayShortcutEvent;\n};\n"],"mappings":";;AAOA,IAAa,IAAwB,oBAExB,IAAkB,EAG7B,CAAqB,GAEV,IAA0B,EAAgB,GAAuB,CAC5E,UACF,CAAC,GAEY,IAAqB,EAChC,EAAwB,QAC1B"}
@@ -1 +1 @@
1
- .overlay-wrapper{flex-direction:column;width:100%;min-width:0;display:flex;position:relative}.overlay-wrapper.is-loading.fill-parent{min-height:12rem}.overlay-content{flex-direction:column;flex-grow:1;width:100%;min-width:0;display:flex;position:relative;overflow-y:hidden}.overlay-wrapper.fill-parent .overlay-content{min-height:inherit}.loading-overlay{opacity:0;pointer-events:none;z-index:2;background:oklch(100% 0 0/.7);justify-content:center;align-items:center;transition:opacity .15s;display:flex;position:absolute;inset:0}.overlay-wrapper.viewport-centered .loading-overlay{z-index:50;position:fixed;inset:0}.dark .loading-overlay{background:oklch(14.5% 0 0/.5)}.overlay-wrapper.is-loading .loading-overlay{opacity:1;pointer-events:auto}.loading-overlay__content{color:var(--muted-foreground);align-items:center;gap:.5rem;font-size:.875rem;display:flex}
1
+ .overlay-wrapper{flex-direction:column;width:100%;min-width:0;display:flex;position:relative}.overlay-wrapper.is-loading.fill-parent{min-height:12rem}.overlay-content{flex-direction:column;flex-grow:1;width:100%;min-width:0;display:flex;position:relative;overflow-y:hidden}.overlay-wrapper.fill-parent .overlay-content{min-height:inherit}.loading-overlay{background:color-mix(in srgb, var(--background) 70%, transparent);opacity:0;pointer-events:none;z-index:2;justify-content:center;align-items:center;transition:opacity .15s;display:flex;position:absolute;inset:0}.overlay-wrapper.viewport-centered .loading-overlay{z-index:50;position:fixed;inset:0}.overlay-wrapper.is-loading .loading-overlay{opacity:1;pointer-events:auto}.loading-overlay__content{color:var(--muted-foreground);align-items:center;gap:.5rem;font-size:.875rem;display:flex}
@@ -11,7 +11,7 @@ var o = (o) => {
11
11
  s(e), d({ mode: e });
12
12
  }
13
13
  return /* @__PURE__ */ r(e, {
14
- variant: l ?? "outline",
14
+ variant: l ?? "ghost",
15
15
  size: "icon",
16
16
  onClick: p,
17
17
  className: u,
@@ -1 +1 @@
1
- {"version":3,"file":"mode-toggle.component.js","names":[],"sources":["../../../src/cards/modeToggle/mode-toggle.component.tsx"],"sourcesContent":["import React from \"react\";\nimport {Moon, Sun} from \"lucide-react\";\nimport type {PiCardProps} from \"@pihanga2/core\";\nimport {Button} from \"@/components/ui/button\";\nimport {useTheme} from \"@/components/theme-provider\";\nimport type {ModeToggleEvents, ModeToggleProps} from \"./mode-toggle.types\";\n\nexport const ModeToggleComponent = (\n props: PiCardProps<ModeToggleProps, ModeToggleEvents>,\n): React.ReactNode => {\n const {setTheme, theme} = useTheme();\n const {variant, className, onModeChanged, cardName} = props;\n\n function onClick() {\n const mode = theme === \"dark\" ? \"light\" : \"dark\";\n setTheme(mode);\n onModeChanged({mode});\n }\n\n return (\n <Button\n variant={variant ?? \"outline\"}\n size=\"icon\"\n onClick={onClick}\n className={className}\n data-pihanga={cardName}\n >\n <Sun className=\"h-[1.2rem] w-[1.2rem] rotate-0 scale-100 transition-all dark:-rotate-90 dark:scale-0\" />\n <Moon className=\"absolute h-[1.2rem] w-[1.2rem] rotate-90 scale-0 transition-all dark:rotate-0 dark:scale-100\" />\n <span className=\"sr-only\">Toggle theme</span>\n </Button>\n );\n};\n"],"mappings":";;;;;;AAOA,IAAa,KACX,MACoB;CACpB,IAAM,EAAC,aAAU,aAAS,EAAS,GAC7B,EAAC,YAAS,cAAW,kBAAe,gBAAY;CAEtD,SAAS,IAAU;EACjB,IAAM,IAAO,MAAU,SAAS,UAAU;EAE1C,AADA,EAAS,CAAI,GACb,EAAc,EAAC,QAAI,CAAC;CACtB;CAEA,OACE,kBAAC,GAAD;EACE,SAAS,KAAW;EACpB,MAAK;EACI;EACE;EACX,gBAAc;YALhB;GAOE,kBAAC,GAAD,EAAK,WAAU,uFAAwF,CAAA;GACvG,kBAAC,GAAD,EAAM,WAAU,+FAAgG,CAAA;GAChH,kBAAC,QAAD;IAAM,WAAU;cAAU;GAAkB,CAAA;EACtC;;AAEZ"}
1
+ {"version":3,"file":"mode-toggle.component.js","names":[],"sources":["../../../src/cards/modeToggle/mode-toggle.component.tsx"],"sourcesContent":["import React from \"react\";\nimport {Moon, Sun} from \"lucide-react\";\nimport type {PiCardProps} from \"@pihanga2/core\";\nimport {Button} from \"@/components/ui/button\";\nimport {useTheme} from \"@/components/theme-provider\";\nimport type {ModeToggleEvents, ModeToggleProps} from \"./mode-toggle.types\";\n\nexport const ModeToggleComponent = (\n props: PiCardProps<ModeToggleProps, ModeToggleEvents>,\n): React.ReactNode => {\n const {setTheme, theme} = useTheme();\n const {variant, className, onModeChanged, cardName} = props;\n\n function onClick() {\n const mode = theme === \"dark\" ? \"light\" : \"dark\";\n setTheme(mode);\n onModeChanged({mode});\n }\n\n return (\n <Button\n variant={variant ?? \"ghost\"}\n size=\"icon\"\n onClick={onClick}\n className={className}\n data-pihanga={cardName}\n >\n <Sun className=\"h-[1.2rem] w-[1.2rem] rotate-0 scale-100 transition-all dark:-rotate-90 dark:scale-0\" />\n <Moon className=\"absolute h-[1.2rem] w-[1.2rem] rotate-90 scale-0 transition-all dark:rotate-0 dark:scale-100\" />\n <span className=\"sr-only\">Toggle theme</span>\n </Button>\n );\n};\n"],"mappings":";;;;;;AAOA,IAAa,KACX,MACoB;CACpB,IAAM,EAAC,aAAU,aAAS,EAAS,GAC7B,EAAC,YAAS,cAAW,kBAAe,gBAAY;CAEtD,SAAS,IAAU;EACjB,IAAM,IAAO,MAAU,SAAS,UAAU;EAE1C,AADA,EAAS,CAAI,GACb,EAAc,EAAC,QAAI,CAAC;CACtB;CAEA,OACE,kBAAC,GAAD;EACE,SAAS,KAAW;EACpB,MAAK;EACI;EACE;EACX,gBAAc;YALhB;GAOE,kBAAC,GAAD,EAAK,WAAU,uFAAwF,CAAA;GACvG,kBAAC,GAAD,EAAM,WAAU,+FAAgG,CAAA;GAChH,kBAAC,QAAD;IAAM,WAAU;cAAU;GAAkB,CAAA;EACtC;;AAEZ"}
@@ -1 +1 @@
1
- .window{justify-content:center;width:100%;height:100%;display:flex}.page{flex-direction:column;flex:1;width:100%;height:100%;display:flex}.header{border-bottom:1px solid var(--border);background-color:var(--background);flex-shrink:0;align-items:center;gap:1rem;padding-left:1rem;padding-right:1rem;display:flex}@media (width>=768px){.header{padding-left:1.5rem;padding-right:1.5rem}}.title-section{align-items:center;gap:.5rem;display:inline-flex}.title-section .title-icon{flex-shrink:0;width:1.5rem;height:1.5rem}.title-section .title-text{color:var(--foreground);font-size:1.125rem;font-weight:600;line-height:1.75rem}.nav-md{display:none}@media (width>=768px){.nav-md{flex-direction:row;align-items:center;gap:1.25rem;display:flex}}@media (width>=1024px){.nav-md{gap:1.5rem}}.nav-md .nav-link{color:var(--muted-foreground);padding-left:0;padding-right:0;transition-property:color,background-color,border-color,text-decoration-color,fill,stroke;transition-duration:.15s;transition-timing-function:cubic-bezier(.4,0,.2,1)}.nav-md .nav-link:hover{color:var(--foreground)}.nav-sm{gap:1.5rem;padding:1rem;font-size:1.125rem;font-weight:500;line-height:1.75rem;display:grid}.nav-sm .nav-link{color:var(--muted-foreground);justify-content:flex-start;padding-left:0;padding-right:0;font-size:1.125rem;line-height:1.75rem}.nav-sm .nav-link:hover{color:var(--foreground)}.header-actions{align-items:center;gap:.5rem;margin-left:auto;display:flex}@media (width>=1024px){.header-actions{gap:1rem}}.main{background-color:var(--background);flex:1;min-height:0;padding:.5rem 1rem;overflow-y:auto}@media (width>=768px){.main{padding:1rem 2rem}}.footer{background-color:var(--background);color:var(--muted-foreground);flex-shrink:0;padding:.5rem 1rem}@media (width>=768px){.footer{padding:1rem 2rem}}
1
+ .window{justify-content:center;width:100%;height:100%;display:flex}.page{flex-direction:column;flex:1;width:100%;height:100%;display:flex}.header{border-bottom:1px solid var(--border);background-color:var(--background);flex-shrink:0;align-items:center;gap:1rem;padding:.75rem 1rem;display:flex}@media (width>=768px){.header{padding-left:1.5rem;padding-right:1.5rem}}.title-section{align-items:center;gap:.5rem;display:inline-flex}.title-section .title-icon{flex-shrink:0;width:1.5rem;height:1.5rem}.title-section .title-text{color:var(--foreground);font-size:1.125rem;font-weight:600;line-height:1.75rem}.nav-md{display:none}@media (width>=768px){.nav-md{flex-direction:row;align-items:center;gap:1.25rem;display:flex}}@media (width>=1024px){.nav-md{gap:1.5rem}}.nav-md .nav-link{color:var(--muted-foreground);padding-left:0;padding-right:0;transition-property:color,background-color,border-color,text-decoration-color,fill,stroke;transition-duration:.15s;transition-timing-function:cubic-bezier(.4,0,.2,1)}.nav-md .nav-link:hover{color:var(--foreground)}.nav-sm{gap:1.5rem;padding:1rem;font-size:1.125rem;font-weight:500;line-height:1.75rem;display:grid}.nav-sm .nav-link{color:var(--muted-foreground);justify-content:flex-start;padding-left:0;padding-right:0;font-size:1.125rem;line-height:1.75rem}.nav-sm .nav-link:hover{color:var(--foreground)}.header-actions{align-items:center;gap:.5rem;margin-left:auto;display:flex}@media (width>=1024px){.header-actions{gap:1rem}}.main{background-color:var(--background);flex:1;min-height:0;padding:.5rem 1rem;overflow-y:auto}@media (width>=768px){.main{padding:1rem 2rem}}.footer{background-color:var(--background);color:var(--muted-foreground);flex-shrink:0;padding:.5rem 1rem}@media (width>=768px){.footer{padding:1rem 2rem}}
@@ -1 +1 @@
1
- .pihanga-paste-target{border:2px dashed #999}.pihanga-paste-target .paste-target-msg-title{font-size:1.2em}.pihanga-paste-target textarea:focus{outline:none}.pihanga-paste-target-focused{border-width:3px;border-color:#007bff}.pihanga-paste-target-focused .paste-target-focus-reminder{display:none}
1
+ .pihanga-paste-target{border:dashed 2px var(--border)}.pihanga-paste-target .paste-target-msg-title{font-size:1.2em}.pihanga-paste-target textarea:focus{outline:none}.pihanga-paste-target-focused{border-color:var(--ring);border-width:3px}.pihanga-paste-target-focused .paste-target-focus-reminder{display:none}
@@ -0,0 +1 @@
1
+ export * from './resizableColumns.types';
@@ -0,0 +1,4 @@
1
+ import { PiCardProps } from '@pihanga2/core';
2
+ import { ResizableColumnsProps } from './resizableColumns.types';
3
+ import * as React from "react";
4
+ export declare const ResizableColumnsComponent: (props: PiCardProps<ResizableColumnsProps>) => React.ReactNode;
@@ -0,0 +1,22 @@
1
+ import { PiCardRef } from '@pihanga2/core';
2
+ export declare const RESIZABLE_COLUMNS_CARD = "pi/resizable-columns";
3
+ export type ResizableColumnsProps = {
4
+ columnCards: PiCardRef[];
5
+ /**
6
+ * Initial column widths. Supports CSS-like values:
7
+ * - '200px' — fixed initial width
8
+ * - '30%' — explicit percentage
9
+ * - '1fr' — fraction of remaining space (after px/% columns)
10
+ *
11
+ * Defaults to equal split when omitted or when length doesn't match columnCards.
12
+ * @example ['200px', '1fr', '1fr'] — fixed sidebar, two equal remaining columns
13
+ * @example ['30%', '70%']
14
+ * @example ['1fr', '2fr', '1fr']
15
+ */
16
+ columnWidths?: string[];
17
+ /** Minimum percentage any column can shrink to during drag. Default: 10 */
18
+ minColumnPercent?: number;
19
+ className?: string;
20
+ dividerClassName?: string;
21
+ };
22
+ export declare const ResizableColumns: <S extends import('@pihanga2/core').ReduxState>(p: import('@pihanga2/core').PiMapProps<ResizableColumnsProps, S, {}>) => import('@pihanga2/core').PiCardDef;
@@ -0,0 +1 @@
1
+ export * from './resizableGrid.types';
@@ -0,0 +1,4 @@
1
+ import { PiCardProps } from '@pihanga2/core';
2
+ import { ResizableGridProps } from './resizableGrid.types';
3
+ import * as React from "react";
4
+ export declare const ResizableGridComponent: (props: PiCardProps<ResizableGridProps>) => React.ReactNode;
@@ -0,0 +1,33 @@
1
+ import { PiCardRef } from '@pihanga2/core';
2
+ export declare const RESIZABLE_GRID_CARD = "pi/resizable-grid";
3
+ export type ResizableGridProps = {
4
+ /**
5
+ * 2D array of cards — cells[row][col]. Must be rectangular (all rows same length).
6
+ */
7
+ cells: PiCardRef[][];
8
+ /**
9
+ * Initial column widths using CSS-like values:
10
+ * - '200px' — fixed initial width (resolved against container width at mount)
11
+ * - '30%' — explicit percentage of container
12
+ * - '1fr' — fraction of remaining space after px/% columns
13
+ * - 'auto' — natural content width; no drag handle rendered for adjacent boundaries
14
+ *
15
+ * Defaults to equal `1fr` split when omitted.
16
+ */
17
+ columnWidths?: string[];
18
+ /**
19
+ * Initial row heights using the same syntax as columnWidths.
20
+ * 'auto' rows follow content height (max across all cells in that row).
21
+ * No drag handle is rendered between two rows unless both are explicit (non-auto).
22
+ *
23
+ * Defaults to equal `1fr` split when omitted.
24
+ */
25
+ rowHeights?: string[];
26
+ /** Minimum percentage any explicit column can shrink to during drag. Default: 10 */
27
+ minColumnPercent?: number;
28
+ /** Minimum percentage any explicit row can shrink to during drag. Default: 10 */
29
+ minRowPercent?: number;
30
+ className?: string;
31
+ dividerClassName?: string;
32
+ };
33
+ export declare const ResizableGrid: <S extends import('@pihanga2/core').ReduxState>(p: import('@pihanga2/core').PiMapProps<ResizableGridProps, S, {}>) => import('@pihanga2/core').PiCardDef;
@@ -1 +1 @@
1
- .swa-root{flex-direction:column;min-height:0;display:flex}.swa-content-wrapper{flex-direction:column;flex:1;min-height:0;display:flex;position:relative}.swa-scroll-container{flex:1;min-height:0;overflow:auto}.swa-scroll-container::-webkit-scrollbar{width:14px}.swa-scroll-container::-webkit-scrollbar-track{background:var(--color-background-tertiary,#f5f5f5)}.swa-scroll-container::-webkit-scrollbar-thumb{background:var(--color-border-secondary,#ccc);border:2px solid var(--color-background-primary,#fff);border-radius:6px}.swa-overlay{pointer-events:none;z-index:1;width:12px;height:100%;position:absolute;top:0;right:2px}.swa-marker{cursor:pointer;pointer-events:auto;transition:opacity .15s,transform .15s,box-shadow .15s;position:absolute;left:0}:root{--swa-point-size:10px}.swa-marker-point{width:var(--swa-point-size);height:var(--swa-point-size);border:2px solid #fff;border-radius:50%;left:2px;transform:translateY(-50%);box-shadow:0 0 4px #0000004d}.swa-marker-point.swa-marker--hover{transform:translateY(-50%)scale(1.5)}:root{--swa-bar-width:10px;--swa-bar-height:10px}.swa-marker-bar{width:var(--swa-bar-width);height:var(--swa-bar-height);opacity:.8;border-radius:1px;transform:translateY(-50%)}.swa-marker-bar.swa-marker--hover{opacity:1;box-shadow:0 0 4px #0006}:root{--swa-range-width:8px;--swa-range-min-height:8px}.swa-marker-range{width:var(--swa-range-width);min-height:var(--swa-range-min-height);opacity:.4;border-left:2px solid #fff;border-radius:2px;box-shadow:inset 0 0 2px #0003}.swa-marker-range.swa-marker--hover{opacity:.65;box-shadow:0 0 6px #0006}:root{--swa-color-error:#e24b4a;--swa-color-warning:#f39c12;--swa-color-todo:#378add;--swa-color-comment:#95a5a6;--swa-color-review:#888780;--swa-color-fixme:#ba7517}.swa-type-error{background:var(--swa-color-error)}.swa-type-warning{background:var(--swa-color-warning)}.swa-type-todo{background:var(--swa-color-todo)}.swa-type-comment{background:var(--swa-color-comment)}.swa-type-review{background:var(--swa-color-review)}.swa-type-fixme{background:var(--swa-color-fixme)}
1
+ .swa-root{flex-direction:column;min-height:0;display:flex}.swa-content-wrapper{flex-direction:column;flex:1;min-height:0;display:flex;position:relative}.swa-scroll-container{flex:1;min-height:0;overflow:auto}.swa-scroll-container::-webkit-scrollbar{width:14px}.swa-scroll-container::-webkit-scrollbar-track{background:var(--muted)}.swa-scroll-container::-webkit-scrollbar-thumb{background:var(--border);border-radius:var(--radius-sm);border:2px solid var(--background)}.swa-overlay{pointer-events:none;z-index:1;width:12px;height:100%;position:absolute;top:0;right:2px}.swa-marker{cursor:pointer;pointer-events:auto;transition:opacity .15s,transform .15s,box-shadow .15s;position:absolute;left:0}:root{--swa-point-size:10px}.swa-marker-point{width:var(--swa-point-size);height:var(--swa-point-size);border:2px solid var(--background);box-shadow:0 0 4px color-mix(in srgb, var(--foreground) 30%, transparent);border-radius:50%;left:2px;transform:translateY(-50%)}.swa-marker-point.swa-marker--hover{transform:translateY(-50%)scale(1.5)}:root{--swa-bar-width:10px;--swa-bar-height:10px}.swa-marker-bar{width:var(--swa-bar-width);height:var(--swa-bar-height);opacity:.8;border-radius:1px;transform:translateY(-50%)}.swa-marker-bar.swa-marker--hover{opacity:1;box-shadow:0 0 4px color-mix(in srgb, var(--foreground) 40%, transparent)}:root{--swa-range-width:8px;--swa-range-min-height:8px}.swa-marker-range{width:var(--swa-range-width);min-height:var(--swa-range-min-height);opacity:.4;border-left:2px solid var(--background);box-shadow:inset 0 0 2px color-mix(in srgb, var(--foreground) 20%, transparent);border-radius:2px}.swa-marker-range.swa-marker--hover{opacity:.65;box-shadow:0 0 6px color-mix(in srgb, var(--foreground) 40%, transparent)}:root{--swa-color-error:var(--destructive);--swa-color-warning:oklch(75% .15 70);--swa-color-todo:oklch(55% .15 250);--swa-color-comment:oklch(65% .04 220);--swa-color-review:oklch(60% .04 100);--swa-color-fixme:oklch(60% .12 55)}.swa-type-error{background:var(--swa-color-error)}.swa-type-warning{background:var(--swa-color-warning)}.swa-type-todo{background:var(--swa-color-todo)}.swa-type-comment{background:var(--swa-color-comment)}.swa-type-review{background:var(--swa-color-review)}.swa-type-fixme{background:var(--swa-color-fixme)}
@@ -0,0 +1 @@
1
+ export * from './suspense.types';
@@ -0,0 +1,12 @@
1
+ import { SUSPENSE_CARD as e, Suspense as t } from "./suspense.types.js";
2
+ import { SuspenseComponent as n } from "./suspense.component.js";
3
+ import { registerCardComponent as r } from "@pihanga2/core";
4
+ //#region src/cards/suspense/index.ts
5
+ r({
6
+ name: e,
7
+ component: n
8
+ });
9
+ //#endregion
10
+ export { e as SUSPENSE_CARD, t as Suspense };
11
+
12
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","names":[],"sources":["../../../src/cards/suspense/index.ts"],"sourcesContent":["import {registerCardComponent} from \"@pihanga2/core\";\nimport {SUSPENSE_CARD} from \"./suspense.types\";\nimport {SuspenseComponent} from \"./suspense.component\";\n\nexport * from \"./suspense.types\";\n\n// No events — structural card.\nregisterCardComponent({\n name: SUSPENSE_CARD,\n component: SuspenseComponent,\n});\n"],"mappings":";;;;AAOA,EAAsB;CACpB,MAAM;CACN,WAAW;AACb,CAAC"}
@@ -0,0 +1,15 @@
1
+ import { PiCardProps } from '@pihanga2/core';
2
+ import { SuspenseProps } from './suspense.types';
3
+ import * as React from "react";
4
+ /**
5
+ * SuspenseComponent
6
+ *
7
+ * Wraps `content` in a React `<Suspense>` boundary so it commits and paints
8
+ * independently of the rest of the tree.
9
+ *
10
+ * When `content`'s registered component is code-split with `React.lazy`, this
11
+ * boundary catches the thrown promise and shows either:
12
+ * • a custom `fallback` card (when the `fallback` prop is set), or
13
+ * • an animated shimmer skeleton (default).
14
+ */
15
+ export declare const SuspenseComponent: (props: PiCardProps<SuspenseProps>) => React.ReactNode;
@@ -0,0 +1,45 @@
1
+ import { Suspense as e } from "react";
2
+ import { Card as t } from "@pihanga2/core";
3
+ import { jsx as n } from "react/jsx-runtime";
4
+ //#region src/cards/suspense/suspense.component.tsx
5
+ var r = {
6
+ xs: "h-3",
7
+ sm: "h-5",
8
+ md: "h-10",
9
+ lg: "h-14",
10
+ xl: "h-20"
11
+ }, i = {
12
+ sm: "gap-2",
13
+ md: "gap-3",
14
+ lg: "gap-4"
15
+ };
16
+ function a({ rows: e = 3, rowSize: t = "md", spacing: a = "md", rowClassName: o, className: s }) {
17
+ let c = o ?? `rounded-md bg-foreground/15 ${r[t ?? "md"]}`;
18
+ return /* @__PURE__ */ n("div", {
19
+ className: s ?? `flex w-full flex-col ${i[a ?? "md"]}`,
20
+ children: Array.from({ length: e }).map((e, t) => /* @__PURE__ */ n("div", { className: `animate-pulse ${c}` }, t))
21
+ });
22
+ }
23
+ var o = (r) => {
24
+ let { cardName: i, content: o, fallback: s, rows: c, rowSize: l, spacing: u, rowClassName: d, className: f } = r;
25
+ return /* @__PURE__ */ n(e, {
26
+ fallback: s ? /* @__PURE__ */ n(t, {
27
+ cardName: s,
28
+ parentCard: i
29
+ }) : /* @__PURE__ */ n(a, {
30
+ rows: c,
31
+ rowSize: l,
32
+ spacing: u,
33
+ rowClassName: d,
34
+ className: f
35
+ }),
36
+ children: /* @__PURE__ */ n(t, {
37
+ cardName: o,
38
+ parentCard: i
39
+ })
40
+ });
41
+ };
42
+ //#endregion
43
+ export { o as SuspenseComponent };
44
+
45
+ //# sourceMappingURL=suspense.component.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"suspense.component.js","names":[],"sources":["../../../src/cards/suspense/suspense.component.tsx"],"sourcesContent":["import * as React from \"react\";\nimport {Suspense} from \"react\";\nimport {Card, type PiCardProps} from \"@pihanga2/core\";\nimport type {SuspenseProps} from \"./suspense.types\";\n\n// ── Preset maps (mirrors loading-skeleton) ─────────────────────────────────────\n\nconst ROW_SIZE_CLASS: Record<string, string> = {\n xs: \"h-3\",\n sm: \"h-5\",\n md: \"h-10\",\n lg: \"h-14\",\n xl: \"h-20\",\n};\n\nconst SPACING_CLASS: Record<string, string> = {\n sm: \"gap-2\",\n md: \"gap-3\",\n lg: \"gap-4\",\n};\n\n// ── Inline skeleton (used when no `fallback` card is provided) ─────────────────\n\nfunction InlineSkeleton({\n rows = 3,\n rowSize = \"md\",\n spacing = \"md\",\n rowClassName,\n className,\n}: Pick<\n SuspenseProps,\n \"rows\" | \"rowSize\" | \"spacing\" | \"rowClassName\" | \"className\"\n>) {\n const rowCls =\n rowClassName ??\n `rounded-md bg-foreground/15 ${ROW_SIZE_CLASS[rowSize ?? \"md\"]}`;\n const wrapperCls =\n className ?? `flex w-full flex-col ${SPACING_CLASS[spacing ?? \"md\"]}`;\n\n return (\n <div className={wrapperCls}>\n {Array.from({length: rows}).map((_, i) => (\n <div key={i} className={`animate-pulse ${rowCls}`} />\n ))}\n </div>\n );\n}\n\n// ── Component ──────────────────────────────────────────────────────────────────\n\n/**\n * SuspenseComponent\n *\n * Wraps `content` in a React `<Suspense>` boundary so it commits and paints\n * independently of the rest of the tree.\n *\n * When `content`'s registered component is code-split with `React.lazy`, this\n * boundary catches the thrown promise and shows either:\n * • a custom `fallback` card (when the `fallback` prop is set), or\n * • an animated shimmer skeleton (default).\n */\nexport const SuspenseComponent = (\n props: PiCardProps<SuspenseProps>,\n): React.ReactNode => {\n const {\n cardName,\n content,\n fallback,\n rows,\n rowSize,\n spacing,\n rowClassName,\n className,\n } = props;\n\n const fallbackNode = fallback ? (\n <Card cardName={fallback} parentCard={cardName} />\n ) : (\n <InlineSkeleton\n rows={rows}\n rowSize={rowSize}\n spacing={spacing}\n rowClassName={rowClassName}\n className={className}\n />\n );\n\n return (\n <Suspense fallback={fallbackNode}>\n <Card cardName={content} parentCard={cardName} />\n </Suspense>\n );\n};\n"],"mappings":";;;;AAOA,IAAM,IAAyC;CAC7C,IAAI;CACJ,IAAI;CACJ,IAAI;CACJ,IAAI;CACJ,IAAI;AACN,GAEM,IAAwC;CAC5C,IAAI;CACJ,IAAI;CACJ,IAAI;AACN;AAIA,SAAS,EAAe,EACtB,UAAO,GACP,aAAU,MACV,aAAU,MACV,iBACA,gBAIC;CACD,IAAM,IACJ,KACA,+BAA+B,EAAe,KAAW;CAI3D,OACE,kBAAC,OAAD;EAAK,WAHL,KAAa,wBAAwB,EAAc,KAAW;YAI3D,MAAM,KAAK,EAAC,QAAQ,EAAI,CAAC,EAAE,KAAK,GAAG,MAClC,kBAAC,OAAD,EAAa,WAAW,iBAAiB,IAAW,GAA1C,CAA0C,CACrD;CACE,CAAA;AAET;AAeA,IAAa,KACX,MACoB;CACpB,IAAM,EACJ,aACA,YACA,aACA,SACA,YACA,YACA,iBACA,iBACE;CAcJ,OACE,kBAAC,GAAD;EAAU,UAbS,IACnB,kBAAC,GAAD;GAAM,UAAU;GAAU,YAAY;EAAW,CAAA,IAEjD,kBAAC,GAAD;GACQ;GACG;GACA;GACK;GACH;EACZ,CAAA;YAKC,kBAAC,GAAD;GAAM,UAAU;GAAS,YAAY;EAAW,CAAA;CACxC,CAAA;AAEd"}
@@ -0,0 +1,67 @@
1
+ import { PiCardRef } from '@pihanga2/core';
2
+ import { SkeletonRowSize, SkeletonSpacing } from '../loadingSkeleton';
3
+ export declare const SUSPENSE_CARD = "shad/suspense";
4
+ export declare const Suspense: <S extends import('@pihanga2/core').ReduxState>(p: import('@pihanga2/core').PiMapProps<SuspenseProps, S, {}>) => import('@pihanga2/core').PiCardDef;
5
+ /**
6
+ * Props for the `shad/suspense` card.
7
+ *
8
+ * Wraps `content` in a React `<Suspense>` boundary so it can suspend
9
+ * independently of its siblings (e.g. when its component is code-split via
10
+ * `React.lazy`).
11
+ *
12
+ * **Default fallback** — animated shimmer skeleton rows (same as
13
+ * `shad/loading-skeleton`); customise with `rows` / `rowSize` / `spacing`:
14
+ * ```ts
15
+ * registerCard("myApp/editorSection", PiSuspense({
16
+ * content: "myApp/codeEditor",
17
+ * rows: 5,
18
+ * rowSize: "lg",
19
+ * }));
20
+ * ```
21
+ *
22
+ * **Custom fallback card** — pass any registered card name via `fallback` to
23
+ * replace the built-in skeleton entirely:
24
+ * ```ts
25
+ * registerCard("myApp/graphSection", PiSuspense({
26
+ * content: "myApp/graphView",
27
+ * fallback: "myApp/graphPlaceholder",
28
+ * }));
29
+ * ```
30
+ */
31
+ export type SuspenseProps = {
32
+ /**
33
+ * The card to render inside the Suspense boundary.
34
+ * Typically registered with a `React.lazy` component so it can suspend.
35
+ */
36
+ content: PiCardRef;
37
+ /**
38
+ * Optional card to use as the Suspense fallback.
39
+ * When provided, the built-in skeleton props below are ignored.
40
+ */
41
+ fallback?: PiCardRef;
42
+ /**
43
+ * Number of placeholder rows shown while suspended.
44
+ * @default 3
45
+ */
46
+ rows?: number;
47
+ /**
48
+ * Named height preset for each shimmer row.
49
+ * @default "md"
50
+ */
51
+ rowSize?: SkeletonRowSize;
52
+ /**
53
+ * Named gap preset between shimmer rows.
54
+ * @default "md"
55
+ */
56
+ spacing?: SkeletonSpacing;
57
+ /**
58
+ * Raw Tailwind classes for every shimmer row.
59
+ * When set, takes precedence over `rowSize`.
60
+ */
61
+ rowClassName?: string;
62
+ /**
63
+ * Raw Tailwind classes for the outer wrapper `<div>`.
64
+ * When set, takes precedence over `spacing`.
65
+ */
66
+ className?: string;
67
+ };
@@ -0,0 +1,7 @@
1
+ import { createCardDeclaration as e } from "@pihanga2/core";
2
+ //#region src/cards/suspense/suspense.types.ts
3
+ var t = "shad/suspense", n = e(t);
4
+ //#endregion
5
+ export { t as SUSPENSE_CARD, n as Suspense };
6
+
7
+ //# sourceMappingURL=suspense.types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"suspense.types.js","names":[],"sources":["../../../src/cards/suspense/suspense.types.ts"],"sourcesContent":["import {createCardDeclaration} from \"@pihanga2/core\";\nimport type {PiCardRef} from \"@pihanga2/core\";\nimport type {SkeletonRowSize, SkeletonSpacing} from \"@/cards/loadingSkeleton\";\n\n// ── Card id ───────────────────────────────────────────────────────────────────\n\nexport const SUSPENSE_CARD = \"shad/suspense\";\n\n// ── Card declaration factory ──────────────────────────────────────────────────\n\nexport const Suspense = createCardDeclaration<SuspenseProps>(SUSPENSE_CARD);\n\n// ── Props ─────────────────────────────────────────────────────────────────────\n\n/**\n * Props for the `shad/suspense` card.\n *\n * Wraps `content` in a React `<Suspense>` boundary so it can suspend\n * independently of its siblings (e.g. when its component is code-split via\n * `React.lazy`).\n *\n * **Default fallback** — animated shimmer skeleton rows (same as\n * `shad/loading-skeleton`); customise with `rows` / `rowSize` / `spacing`:\n * ```ts\n * registerCard(\"myApp/editorSection\", PiSuspense({\n * content: \"myApp/codeEditor\",\n * rows: 5,\n * rowSize: \"lg\",\n * }));\n * ```\n *\n * **Custom fallback card** — pass any registered card name via `fallback` to\n * replace the built-in skeleton entirely:\n * ```ts\n * registerCard(\"myApp/graphSection\", PiSuspense({\n * content: \"myApp/graphView\",\n * fallback: \"myApp/graphPlaceholder\",\n * }));\n * ```\n */\nexport type SuspenseProps = {\n /**\n * The card to render inside the Suspense boundary.\n * Typically registered with a `React.lazy` component so it can suspend.\n */\n content: PiCardRef;\n\n /**\n * Optional card to use as the Suspense fallback.\n * When provided, the built-in skeleton props below are ignored.\n */\n fallback?: PiCardRef;\n\n // ── Built-in skeleton fallback options (ignored when `fallback` is set) ────\n\n /**\n * Number of placeholder rows shown while suspended.\n * @default 3\n */\n rows?: number;\n\n /**\n * Named height preset for each shimmer row.\n * @default \"md\"\n */\n rowSize?: SkeletonRowSize;\n\n /**\n * Named gap preset between shimmer rows.\n * @default \"md\"\n */\n spacing?: SkeletonSpacing;\n\n /**\n * Raw Tailwind classes for every shimmer row.\n * When set, takes precedence over `rowSize`.\n */\n rowClassName?: string;\n\n /**\n * Raw Tailwind classes for the outer wrapper `<div>`.\n * When set, takes precedence over `spacing`.\n */\n className?: string;\n};\n// No events — display-only structural card.\n"],"mappings":";;AAMA,IAAa,IAAgB,iBAIhB,IAAW,EAAqC,CAAa"}