@pihanga2/shadcn 0.2.13 → 0.2.15
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -1
- package/cards/fileDrop/fileDrop.component.js +64 -49
- package/cards/fileDrop/fileDrop.component.js.map +1 -1
- package/cards/fileDrop/fileDrop.types.d.ts +49 -0
- package/cards/fileDrop/fileDrop.types.js +9 -2
- package/cards/fileDrop/fileDrop.types.js.map +1 -1
- package/cards/fileDrop/index.js +14 -8
- package/cards/fileDrop/index.js.map +1 -1
- package/cards/keyboardOverlay/index.d.ts +1 -0
- package/cards/keyboardOverlay/index.js +13 -0
- package/cards/keyboardOverlay/index.js.map +1 -0
- package/cards/keyboardOverlay/keyboardOverlay.component.d.ts +4 -0
- package/cards/keyboardOverlay/keyboardOverlay.component.js +64 -0
- package/cards/keyboardOverlay/keyboardOverlay.component.js.map +1 -0
- package/cards/keyboardOverlay/keyboardOverlay.types.d.ts +74 -0
- package/cards/keyboardOverlay/keyboardOverlay.types.js +7 -0
- package/cards/keyboardOverlay/keyboardOverlay.types.js.map +1 -0
- package/cards/textField/textField.component.js +2 -1
- package/cards/textField/textField.component.js.map +1 -1
- package/package.json +6 -1
package/README.md
CHANGED
|
@@ -62,7 +62,7 @@ import "@pihanga2/shadcn/cards/dataTable";
|
|
|
62
62
|
|
|
63
63
|
---
|
|
64
64
|
|
|
65
|
-
## Included cards (
|
|
65
|
+
## Included cards (44)
|
|
66
66
|
|
|
67
67
|
- `avatar`
|
|
68
68
|
- `badge`
|
|
@@ -84,6 +84,7 @@ import "@pihanga2/shadcn/cards/dataTable";
|
|
|
84
84
|
- `infoCard`
|
|
85
85
|
- `input`
|
|
86
86
|
- `jsonViewer`
|
|
87
|
+
- `keyboardOverlay`
|
|
87
88
|
- `list`
|
|
88
89
|
- `loadingOverlay`
|
|
89
90
|
- `loadingSkeleton`
|
|
@@ -1,90 +1,105 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { getIcon as e } from "../icons.js";
|
|
2
|
+
import { DEF_FILE_DROP_FILE_TYPES as t, getFileDropTheme as n } from "./fileDrop.types.js";
|
|
2
3
|
import './fileDrop.css';/* empty css */
|
|
3
4
|
import "react";
|
|
4
|
-
import { jsx as
|
|
5
|
-
import { FileUploader as
|
|
5
|
+
import { jsx as r, jsxs as i } from "react/jsx-runtime";
|
|
6
|
+
import { FileUploader as a } from "react-drag-drop-files";
|
|
6
7
|
//#region src/cards/fileDrop/fileDrop.component.tsx
|
|
7
|
-
var
|
|
8
|
-
function
|
|
9
|
-
let t =
|
|
8
|
+
var o = Symbol.for("pihanga.card.FileDrop.LastDropped"), s = globalThis;
|
|
9
|
+
function c(e) {
|
|
10
|
+
let t = s[o] ??= null;
|
|
10
11
|
return t?.name === e ? t.file : null;
|
|
11
12
|
}
|
|
12
|
-
function
|
|
13
|
-
|
|
13
|
+
function l(e) {
|
|
14
|
+
s[o] = e;
|
|
14
15
|
}
|
|
15
|
-
function
|
|
16
|
-
|
|
16
|
+
function u() {
|
|
17
|
+
s[o] = null;
|
|
17
18
|
}
|
|
18
|
-
var
|
|
19
|
-
let { fileTypes:
|
|
20
|
-
|
|
19
|
+
var d = (o) => {
|
|
20
|
+
let { fileTypes: s = t, title: c = "Click or drop a file right here", description: d, showProgress: f = !1, progress: p = 0, progressStyle: m = {}, dropStyle: h = {}, icon: g, iconProps: _, browseLabel: v, onFileDropped: y, onError: b, cardName: x, theme: S, classNames: C, className: w, _cls: T } = o, E = {
|
|
21
|
+
...S ? n(S) : {},
|
|
22
|
+
...C
|
|
23
|
+
};
|
|
24
|
+
function D(e) {
|
|
21
25
|
let t = Array.isArray(e) ? e[0] : e;
|
|
22
26
|
if (!t) return;
|
|
23
27
|
let { name: n, size: r, type: i } = t;
|
|
24
|
-
|
|
28
|
+
l({
|
|
25
29
|
name: n,
|
|
26
30
|
file: t
|
|
27
|
-
}),
|
|
31
|
+
}), y({
|
|
28
32
|
name: n,
|
|
29
33
|
size: r,
|
|
30
34
|
type: i
|
|
31
|
-
}), setTimeout(
|
|
35
|
+
}), setTimeout(u, 2e3);
|
|
32
36
|
}
|
|
33
|
-
function
|
|
34
|
-
|
|
37
|
+
function O(e) {
|
|
38
|
+
b({ error: String(e) });
|
|
35
39
|
}
|
|
36
|
-
function
|
|
37
|
-
let e = `${
|
|
40
|
+
function k() {
|
|
41
|
+
let e = `${p}%`, t = `${e} Complete`, n = {
|
|
38
42
|
width: "50%",
|
|
39
|
-
...
|
|
43
|
+
...m
|
|
40
44
|
};
|
|
41
|
-
return /* @__PURE__ */
|
|
45
|
+
return /* @__PURE__ */ i("div", {
|
|
42
46
|
className: "pi-progress",
|
|
43
|
-
children: [/* @__PURE__ */
|
|
47
|
+
children: [/* @__PURE__ */ r("div", {
|
|
44
48
|
className: "pi-progress-label",
|
|
45
49
|
children: e
|
|
46
|
-
}), /* @__PURE__ */
|
|
50
|
+
}), /* @__PURE__ */ r("div", {
|
|
47
51
|
className: "pi-progress-container",
|
|
48
|
-
style:
|
|
49
|
-
children: /* @__PURE__ */
|
|
52
|
+
style: n,
|
|
53
|
+
children: /* @__PURE__ */ r("div", {
|
|
50
54
|
className: "pi-progress-bar",
|
|
51
55
|
style: { width: e },
|
|
52
56
|
role: "progressbar",
|
|
53
|
-
"aria-label":
|
|
57
|
+
"aria-label": t
|
|
54
58
|
})
|
|
55
59
|
})]
|
|
56
60
|
});
|
|
57
61
|
}
|
|
58
|
-
function
|
|
59
|
-
return /* @__PURE__ */
|
|
60
|
-
className: "dropzone-msg",
|
|
61
|
-
style:
|
|
62
|
-
children: [
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
62
|
+
function A() {
|
|
63
|
+
return /* @__PURE__ */ i("div", {
|
|
64
|
+
className: ["dropzone-msg", E.dropZone].filter(Boolean).join(" "),
|
|
65
|
+
style: h,
|
|
66
|
+
children: [
|
|
67
|
+
g && /* @__PURE__ */ r("div", {
|
|
68
|
+
className: E.icon,
|
|
69
|
+
children: e(g, _)
|
|
70
|
+
}),
|
|
71
|
+
c && /* @__PURE__ */ r("h3", {
|
|
72
|
+
className: ["dropzone-msg-title", E.title].filter(Boolean).join(" "),
|
|
73
|
+
children: c
|
|
74
|
+
}),
|
|
75
|
+
d && /* @__PURE__ */ r("span", {
|
|
76
|
+
className: ["dropzone-msg-desc", E.description].filter(Boolean).join(" "),
|
|
77
|
+
children: d
|
|
78
|
+
}),
|
|
79
|
+
v && /* @__PURE__ */ r("div", {
|
|
80
|
+
className: E.browseButton,
|
|
81
|
+
children: v
|
|
82
|
+
})
|
|
83
|
+
]
|
|
69
84
|
});
|
|
70
85
|
}
|
|
71
|
-
function
|
|
72
|
-
return /* @__PURE__ */
|
|
73
|
-
handleChange:
|
|
74
|
-
onTypeError:
|
|
86
|
+
function j() {
|
|
87
|
+
return /* @__PURE__ */ r(a, {
|
|
88
|
+
handleChange: D,
|
|
89
|
+
onTypeError: O,
|
|
75
90
|
name: "file",
|
|
76
|
-
types:
|
|
91
|
+
types: s,
|
|
77
92
|
hoverTitle: " ",
|
|
78
|
-
children:
|
|
93
|
+
children: A()
|
|
79
94
|
});
|
|
80
95
|
}
|
|
81
|
-
return /* @__PURE__ */
|
|
82
|
-
className:
|
|
83
|
-
"data-pihanga":
|
|
84
|
-
children:
|
|
96
|
+
return /* @__PURE__ */ r("div", {
|
|
97
|
+
className: T("root", E.root ?? w),
|
|
98
|
+
"data-pihanga": x,
|
|
99
|
+
children: f ? k() : j()
|
|
85
100
|
});
|
|
86
101
|
};
|
|
87
102
|
//#endregion
|
|
88
|
-
export {
|
|
103
|
+
export { d as FileDropComponent, c as get_last_dropped };
|
|
89
104
|
|
|
90
105
|
//# sourceMappingURL=fileDrop.component.js.map
|
|
@@ -1 +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
|
|
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 getFileDropTheme,\n type FileDropEvents,\n type FileDropProps,\n} from \"./fileDrop.types\";\nimport {getIcon} from \"../icons\";\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\n// eslint-disable-next-line react-refresh/only-export-components\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 icon,\n iconProps,\n browseLabel,\n onFileDropped,\n onError,\n cardName,\n theme,\n classNames,\n className,\n _cls,\n } = props;\n\n // Merge theme (base) with per-card classNames (overrides)\n const cn = {...(theme ? getFileDropTheme(theme) : {}), ...classNames};\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 const dzCn = [\"dropzone-msg\", cn.dropZone].filter(Boolean).join(\" \");\n return (\n <div className={dzCn} style={dropStyle as React.CSSProperties}>\n {icon && <div className={cn.icon}>{getIcon(icon, iconProps)}</div>}\n {title && (\n <h3\n className={[\"dropzone-msg-title\", cn.title]\n .filter(Boolean)\n .join(\" \")}\n >\n {title}\n </h3>\n )}\n {description && (\n <span\n className={[\"dropzone-msg-desc\", cn.description]\n .filter(Boolean)\n .join(\" \")}\n >\n {description}\n </span>\n )}\n {browseLabel && <div className={cn.browseButton}>{browseLabel}</div>}\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 // classNames.root takes precedence over the legacy className prop\n const rootCn = _cls(\"root\", cn.root ?? className);\n return (\n <div className={rootCn} data-pihanga={cardName}>\n {showProgress ? renderProgress() : renderFileUploader()}\n </div>\n );\n};\n"],"mappings":";;;;;;;AAcA,IAAM,IAAM,OAAO,IAAI,mCAAmC,GAEpD,IAAiB;AAMvB,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,SACA,cACA,gBACA,kBACA,YACA,aACA,UACA,eACA,cACA,YACE,GAGE,IAAK;EAAC,GAAI,IAAQ,EAAiB,CAAK,IAAI,CAAC;EAAI,GAAG;CAAU;CAEpE,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;EAE5C,OACE,kBAAC,OAAD;GAAK,WAFM,CAAC,gBAAgB,EAAG,QAAQ,EAAE,OAAO,OAAO,EAAE,KAAK,GAE9C;GAAM,OAAO;aAA7B;IACG,KAAQ,kBAAC,OAAD;KAAK,WAAW,EAAG;eAAO,EAAQ,GAAM,CAAS;IAAO,CAAA;IAChE,KACC,kBAAC,MAAD;KACE,WAAW,CAAC,sBAAsB,EAAG,KAAK,EACvC,OAAO,OAAO,EACd,KAAK,GAAG;eAEV;IACC,CAAA;IAEL,KACC,kBAAC,QAAD;KACE,WAAW,CAAC,qBAAqB,EAAG,WAAW,EAC5C,OAAO,OAAO,EACd,KAAK,GAAG;eAEV;IACG,CAAA;IAEP,KAAe,kBAAC,OAAD;KAAK,WAAW,EAAG;eAAe;IAAiB,CAAA;GAChE;;CAET;CAEA,SAAS,IAAsC;EAC7C,OACE,kBAAC,GAAD;GACgB;GACd,aAAa;GACb,MAAK;GACL,OAAO;GACP,YAAW;aAEV,EAAe;EACJ,CAAA;CAElB;CAIA,OACE,kBAAC,OAAD;EAAK,WAFQ,EAAK,QAAQ,EAAG,QAAQ,CAErB;EAAQ,gBAAc;YACnC,IAAe,EAAe,IAAI,EAAmB;CACnD,CAAA;AAET"}
|
|
@@ -10,6 +10,38 @@ export declare const onFileDropped: <S extends import('@pihanga2/core').ReduxSta
|
|
|
10
10
|
export declare const onFileDropError: <S extends import('@pihanga2/core').ReduxState>(register: import('@pihanga2/core').PiRegister, f: import('@pihanga2/core').ReduceF<S, import('@pihanga2/core').ReduxAction & {
|
|
11
11
|
cardID: string;
|
|
12
12
|
} & FileDropErrorEvent>) => void;
|
|
13
|
+
/** Per-element Tailwind / CSS class overrides for the FileDrop card. */
|
|
14
|
+
export type FileDropClassNames = {
|
|
15
|
+
/** Outermost wrapper `<div>`. Overrides the legacy `className` prop. */
|
|
16
|
+
root?: string;
|
|
17
|
+
/** Inner drop-zone content wrapper `<div>`. */
|
|
18
|
+
dropZone?: string;
|
|
19
|
+
/** Icon wrapper `<div>`. */
|
|
20
|
+
icon?: string;
|
|
21
|
+
/** `<h3>` title element. */
|
|
22
|
+
title?: string;
|
|
23
|
+
/** Description `<span>`. */
|
|
24
|
+
description?: string;
|
|
25
|
+
/** Browse-button `<div>`. */
|
|
26
|
+
browseButton?: string;
|
|
27
|
+
};
|
|
28
|
+
/**
|
|
29
|
+
* Register a named FileDrop theme.
|
|
30
|
+
* Use the returned name string as the `theme` prop on a `FileDrop` card.
|
|
31
|
+
* Per-card `classNames` always override the theme.
|
|
32
|
+
*
|
|
33
|
+
* @example
|
|
34
|
+
* ```ts
|
|
35
|
+
* export const UPLOAD_THEME = registerFileDropTheme("upload-card", {
|
|
36
|
+
* root: "flex items-center justify-center p-8 rounded-2xl border border-dashed ...",
|
|
37
|
+
* dropZone: "flex flex-col items-center gap-3 text-center",
|
|
38
|
+
* ...
|
|
39
|
+
* });
|
|
40
|
+
* ```
|
|
41
|
+
*/
|
|
42
|
+
export declare function registerFileDropTheme(name: string, classNames: FileDropClassNames): string;
|
|
43
|
+
/** Resolve a registered theme by name. Returns `undefined` for unknown names. */
|
|
44
|
+
export declare function getFileDropTheme(name: string): FileDropClassNames | undefined;
|
|
13
45
|
export type FileDropProps<S = Record<string, unknown>> = {
|
|
14
46
|
/** Accepted file extensions, e.g. ["JPG", "PNG", "PDF"]. Defaults to ["JPG", "PNG", "GIF"]. */
|
|
15
47
|
fileTypes?: string[];
|
|
@@ -25,7 +57,24 @@ export type FileDropProps<S = Record<string, unknown>> = {
|
|
|
25
57
|
progressStyle?: Record<string, unknown>;
|
|
26
58
|
/** Current upload progress (0–100). Only used when `showProgress` is true. */
|
|
27
59
|
progress?: number;
|
|
60
|
+
/** Registered icon name (see src/pihanga/icons.ts) rendered above the title. */
|
|
61
|
+
icon?: string;
|
|
62
|
+
/** Extra props forwarded to the icon element (e.g. `{ className: "size-6" }`). */
|
|
63
|
+
iconProps?: Record<string, any>;
|
|
64
|
+
/** Label for the browse-files button. When omitted the button is not rendered. */
|
|
65
|
+
browseLabel?: string;
|
|
28
66
|
style?: S;
|
|
67
|
+
/**
|
|
68
|
+
* Name of a pre-registered theme (see `registerFileDropTheme`).
|
|
69
|
+
* Per-card `classNames` always override the theme on a key-by-key basis.
|
|
70
|
+
*/
|
|
71
|
+
theme?: string;
|
|
72
|
+
/**
|
|
73
|
+
* Per-element class overrides. Merged on top of any resolved `theme`.
|
|
74
|
+
* `classNames.root` takes precedence over the legacy `className` prop.
|
|
75
|
+
*/
|
|
76
|
+
classNames?: FileDropClassNames;
|
|
77
|
+
/** @deprecated Prefer `classNames.root`. Kept for backward compatibility. */
|
|
29
78
|
className?: string;
|
|
30
79
|
};
|
|
31
80
|
export declare const DEF_FILE_DROP_FILE_TYPES: string[];
|
|
@@ -1,11 +1,18 @@
|
|
|
1
1
|
import { createCardDeclaration as e, createOnAction as t, registerActions as n } from "@pihanga2/core";
|
|
2
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 =
|
|
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
|
+
function l(e, t) {
|
|
5
|
+
return c[e] !== void 0 && console.warn(`FileDrop theme '${e}' is already registered — overwriting`), c[e] = t, e;
|
|
6
|
+
}
|
|
7
|
+
function u(e) {
|
|
8
|
+
return c[e];
|
|
9
|
+
}
|
|
10
|
+
var d = [
|
|
4
11
|
"JPG",
|
|
5
12
|
"PNG",
|
|
6
13
|
"GIF"
|
|
7
14
|
];
|
|
8
15
|
//#endregion
|
|
9
|
-
export {
|
|
16
|
+
export { d as DEF_FILE_DROP_FILE_TYPES, a as FILE_DROP_ACTION, r as FILE_DROP_CARD, i as FileDrop, u as getFileDropTheme, s as onFileDropError, o as onFileDropped, l as registerFileDropTheme };
|
|
10
17
|
|
|
11
18
|
//# sourceMappingURL=fileDrop.types.js.map
|
|
@@ -1 +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,
|
|
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\n/** Per-element Tailwind / CSS class overrides for the FileDrop card. */\nexport type FileDropClassNames = {\n /** Outermost wrapper `<div>`. Overrides the legacy `className` prop. */\n root?: string;\n /** Inner drop-zone content wrapper `<div>`. */\n dropZone?: string;\n /** Icon wrapper `<div>`. */\n icon?: string;\n /** `<h3>` title element. */\n title?: string;\n /** Description `<span>`. */\n description?: string;\n /** Browse-button `<div>`. */\n browseButton?: string;\n};\n\n// ── Theme registry ───────────────────────────────────────────────────────────\n\nconst _fileDropThemes: Record<string, FileDropClassNames> = {};\n\n/**\n * Register a named FileDrop theme.\n * Use the returned name string as the `theme` prop on a `FileDrop` card.\n * Per-card `classNames` always override the theme.\n *\n * @example\n * ```ts\n * export const UPLOAD_THEME = registerFileDropTheme(\"upload-card\", {\n * root: \"flex items-center justify-center p-8 rounded-2xl border border-dashed ...\",\n * dropZone: \"flex flex-col items-center gap-3 text-center\",\n * ...\n * });\n * ```\n */\nexport function registerFileDropTheme(\n name: string,\n classNames: FileDropClassNames,\n): string {\n if (_fileDropThemes[name] !== undefined) {\n console.warn(\n `FileDrop theme '${name}' is already registered — overwriting`,\n );\n }\n _fileDropThemes[name] = classNames;\n return name;\n}\n\n/** Resolve a registered theme by name. Returns `undefined` for unknown names. */\nexport function getFileDropTheme(name: string): FileDropClassNames | undefined {\n return _fileDropThemes[name];\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 /** Registered icon name (see src/pihanga/icons.ts) rendered above the title. */\n icon?: string;\n /** Extra props forwarded to the icon element (e.g. `{ className: \"size-6\" }`). */\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n iconProps?: Record<string, any>;\n /** Label for the browse-files button. When omitted the button is not rendered. */\n browseLabel?: string;\n\n style?: S;\n /**\n * Name of a pre-registered theme (see `registerFileDropTheme`).\n * Per-card `classNames` always override the theme on a key-by-key basis.\n */\n theme?: string;\n /**\n * Per-element class overrides. Merged on top of any resolved `theme`.\n * `classNames.root` takes precedence over the legacy `className` prop.\n */\n classNames?: FileDropClassNames;\n /** @deprecated Prefer `classNames.root`. Kept for backward compatibility. */\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,GAoBM,IAAsD,CAAC;AAgB7D,SAAgB,EACd,GACA,GACQ;CAOR,OANI,EAAgB,OAAU,KAAA,KAC5B,QAAQ,KACN,mBAAmB,EAAK,sCAC1B,GAEF,EAAgB,KAAQ,GACjB;AACT;AAGA,SAAgB,EAAiB,GAA8C;CAC7E,OAAO,EAAgB;AACzB;AAyCA,IAAa,IAA2B;CAAC;CAAO;CAAO;AAAK"}
|
package/cards/fileDrop/index.js
CHANGED
|
@@ -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,
|
|
2
|
-
import { FileDropComponent as
|
|
3
|
-
import { actionTypesToEvents as
|
|
4
|
-
|
|
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:
|
|
8
|
-
events:
|
|
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,
|
|
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
|
|
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"}
|
|
@@ -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,64 @@
|
|
|
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) {
|
|
6
|
+
let t = e;
|
|
7
|
+
for (; t;) {
|
|
8
|
+
let e = t.getAttribute("data-pihanga");
|
|
9
|
+
if (e) return e;
|
|
10
|
+
t = t.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 } = o, p = t({
|
|
19
|
+
x: 0,
|
|
20
|
+
y: 0
|
|
21
|
+
});
|
|
22
|
+
return e(() => {
|
|
23
|
+
let e = (e) => {
|
|
24
|
+
p.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 } = p.current, s = i(document.elementFromPoint(r, o));
|
|
34
|
+
l({
|
|
35
|
+
shortcutId: t.id ?? t.key,
|
|
36
|
+
key: t.key,
|
|
37
|
+
modifiers: n,
|
|
38
|
+
dataPihanga: 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
|
+
}, [c, l]), /* @__PURE__ */ r("div", {
|
|
49
|
+
"data-pihanga": u,
|
|
50
|
+
className: d,
|
|
51
|
+
style: {
|
|
52
|
+
...f,
|
|
53
|
+
position: "relative"
|
|
54
|
+
},
|
|
55
|
+
children: /* @__PURE__ */ r(n, {
|
|
56
|
+
cardName: s,
|
|
57
|
+
parentCard: u
|
|
58
|
+
})
|
|
59
|
+
});
|
|
60
|
+
};
|
|
61
|
+
//#endregion
|
|
62
|
+
export { o as KeyboardOverlayComponent };
|
|
63
|
+
|
|
64
|
+
//# 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 `data-pihanga` value found. */\nfunction findDataPihanga(el: Element | null): string | undefined {\n let node: Element | null = el;\n while (node) {\n const attr = node.getAttribute(\"data-pihanga\");\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 {content, shortcuts, onShortcut, cardName, className, style} = props;\n\n // Track cursor position passively — no re-render needed.\n const cursorPos = useRef<{x: number; y: number}>({x: 0, y: 0});\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 dataPihanga = findDataPihanga(el);\n\n onShortcut({\n shortcutId: shortcut.id ?? shortcut.key,\n key: shortcut.key,\n modifiers: mods,\n dataPihanga,\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]);\n\n return (\n <div\n data-pihanga={cardName}\n className={className}\n style={{...style, position: \"relative\"}}\n >\n <Card cardName={content} parentCard={cardName} />\n </div>\n );\n};\n"],"mappings":";;;;AAUA,SAAS,EAAgB,GAAwC;CAC/D,IAAI,IAAuB;CAC3B,OAAO,IAAM;EACX,IAAM,IAAO,EAAK,aAAa,cAAc;EAC7C,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,EAAC,YAAS,cAAW,eAAY,aAAU,cAAW,aAAS,GAG/D,IAAY,EAA+B;EAAC,GAAG;EAAG,GAAG;CAAC,CAAC;CA4C7D,OA1CA,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,IAAc,EADT,SAAS,iBAAiB,GAAG,CACJ,CAAE;IAEtC,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,CAAC,GAAW,CAAU,CAAC,GAGxB,kBAAC,OAAD;EACE,gBAAc;EACH;EACX,OAAO;GAAC,GAAG;GAAO,UAAU;EAAU;YAEtC,kBAAC,GAAD;GAAM,UAAU;GAAS,YAAY;EAAW,CAAA;CAC7C,CAAA;AAET"}
|
|
@@ -0,0 +1,74 @@
|
|
|
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
|
+
* Extra CSS classes applied to the wrapper `<div>`.
|
|
44
|
+
*
|
|
45
|
+
* Useful for layout constraints, e.g. `"h-full w-full flex"`.
|
|
46
|
+
* `position: relative` is always applied and cannot be overridden.
|
|
47
|
+
*/
|
|
48
|
+
className?: string;
|
|
49
|
+
/**
|
|
50
|
+
* Inline styles merged onto the wrapper `<div>`.
|
|
51
|
+
*
|
|
52
|
+
* `position: relative` is always applied and takes precedence.
|
|
53
|
+
*/
|
|
54
|
+
style?: React.CSSProperties;
|
|
55
|
+
};
|
|
56
|
+
/**
|
|
57
|
+
* Payload emitted by `onShortcut` whenever a registered shortcut fires.
|
|
58
|
+
*
|
|
59
|
+
* `dataPihanga` is the `data-pihanga` attribute of the **lowest** DOM element
|
|
60
|
+
* under the cursor at the moment the key was pressed, walking up the tree
|
|
61
|
+
* until a match is found. `undefined` when no `data-pihanga` ancestor exists.
|
|
62
|
+
*/
|
|
63
|
+
export type KeyboardOverlayShortcutEvent = {
|
|
64
|
+
/** `ShortcutDef.id` when set, otherwise the matched `key`. */
|
|
65
|
+
shortcutId: string;
|
|
66
|
+
key: string;
|
|
67
|
+
modifiers: Modifier[];
|
|
68
|
+
dataPihanga?: string;
|
|
69
|
+
cursorX: number;
|
|
70
|
+
cursorY: number;
|
|
71
|
+
};
|
|
72
|
+
export type KeyboardOverlayEvents = {
|
|
73
|
+
onShortcut: KeyboardOverlayShortcutEvent;
|
|
74
|
+
};
|
|
@@ -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 * 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 * `dataPihanga` is the `data-pihanga` attribute of the **lowest** DOM element\n * under the cursor at the moment the key was pressed, walking up the tree\n * until a match is found. `undefined` when no `data-pihanga` 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 dataPihanga?: 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
|
-
{"version":3,"file":"textField.component.js","names":[],"sources":["../../../src/cards/textField/textField.component.tsx"],"sourcesContent":["import React from \"react\";\nimport {type PiCardProps} from \"@pihanga2/core\";\nimport {Input} from \"@/components/ui/input\";\nimport {useFormContext} from \"@/cards/form/form.context\";\nimport type {PiTextFieldEvents, PiTextFieldProps} from \"./textField.types\";\n\nexport const TextFieldComponent = (\n props: PiCardProps<PiTextFieldProps, PiTextFieldEvents>,\n): React.ReactNode => {\n const {\n name,\n value: propValue = \"\",\n type = \"text\",\n placeholder,\n disabled,\n cardName,\n onChanged,\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 const value = useFormData\n ? ((form.formData[name!] as string | undefined) ?? \"\")\n : propValue;\n\n function handleChange(e: React.ChangeEvent<HTMLInputElement>) {\n const newValue = e.target.value;\n if (useFormData) {\n form.handleChange(name!, newValue);\n } else {\n onChanged({name, value: newValue});\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 ?? \"field\"}`;\n const fieldId = injectedId ?? selfId;\n\n return (\n <Input\n id={fieldId}\n type={type}\n value={value}\n onChange={handleChange}\n placeholder={placeholder}\n disabled={disabled}\n aria-invalid={invalid || undefined}\n />\n );\n};\n"],"mappings":";;;;;;AAMA,IAAa,KACX,MACoB;CACpB,IAAM,EACJ,SACA,OAAO,IAAY,IACnB,UAAO,QACP,gBACA,aACA,aACA,iBACE,GAKE,IAAc,EAAwB,IACtC,IAAU,EAAS,EAA8B,SAGjD,IAAO,EAAe,GACtB,IAAc,EAAK,YAAY,EAAQ,GAEvC,IAAQ,IACR,EAAK,SAAS,MAAiC,KACjD;CAEJ,SAAS,EAAa,GAAwC;EAC5D,IAAM,IAAW,EAAE,OAAO;EAC1B,AAAI,IACF,EAAK,aAAa,GAAO,CAAQ,IAEjC,EAAU;GAAC;GAAM,OAAO;EAAQ,CAAC;CAErC;CAIA,IAAM,IAAS,GAAG,EAAS,GAAG,KAAQ;CAGtC,OACE,kBAAC,GAAD;EACE,IAJY,KAAc;EAKpB;EACC;EACP,UAAU;EACG;EACH;EACV,gBAAc,KAAW,KAAA;
|
|
1
|
+
{"version":3,"file":"textField.component.js","names":[],"sources":["../../../src/cards/textField/textField.component.tsx"],"sourcesContent":["import React from \"react\";\nimport {type PiCardProps} from \"@pihanga2/core\";\nimport {Input} from \"@/components/ui/input\";\nimport {useFormContext} from \"@/cards/form/form.context\";\nimport type {PiTextFieldEvents, PiTextFieldProps} from \"./textField.types\";\n\nexport const TextFieldComponent = (\n props: PiCardProps<PiTextFieldProps, PiTextFieldEvents>,\n): React.ReactNode => {\n const {\n name,\n value: propValue = \"\",\n type = \"text\",\n placeholder,\n disabled,\n cardName,\n onChanged,\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 const value = useFormData\n ? ((form.formData[name!] as string | undefined) ?? \"\")\n : propValue;\n\n function handleChange(e: React.ChangeEvent<HTMLInputElement>) {\n const newValue = e.target.value;\n if (useFormData) {\n form.handleChange(name!, newValue);\n } else {\n onChanged({name, value: newValue});\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 ?? \"field\"}`;\n const fieldId = injectedId ?? selfId;\n\n return (\n <Input\n id={fieldId}\n type={type}\n value={value}\n onChange={handleChange}\n placeholder={placeholder}\n disabled={disabled}\n aria-invalid={invalid || undefined}\n data-pihanga={cardName}\n />\n );\n};\n"],"mappings":";;;;;;AAMA,IAAa,KACX,MACoB;CACpB,IAAM,EACJ,SACA,OAAO,IAAY,IACnB,UAAO,QACP,gBACA,aACA,aACA,iBACE,GAKE,IAAc,EAAwB,IACtC,IAAU,EAAS,EAA8B,SAGjD,IAAO,EAAe,GACtB,IAAc,EAAK,YAAY,EAAQ,GAEvC,IAAQ,IACR,EAAK,SAAS,MAAiC,KACjD;CAEJ,SAAS,EAAa,GAAwC;EAC5D,IAAM,IAAW,EAAE,OAAO;EAC1B,AAAI,IACF,EAAK,aAAa,GAAO,CAAQ,IAEjC,EAAU;GAAC;GAAM,OAAO;EAAQ,CAAC;CAErC;CAIA,IAAM,IAAS,GAAG,EAAS,GAAG,KAAQ;CAGtC,OACE,kBAAC,GAAD;EACE,IAJY,KAAc;EAKpB;EACC;EACP,UAAU;EACG;EACH;EACV,gBAAc,KAAW,KAAA;EACzB,gBAAc;CACf,CAAA;AAEL"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pihanga2/shadcn",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.15",
|
|
4
4
|
"description": "Pihanga core card components built on shadcn/ui and Radix UI — the npm distribution of pihanga-shadcn.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"exports": {
|
|
@@ -84,6 +84,10 @@
|
|
|
84
84
|
"import": "./cards/jsonViewer/index.js",
|
|
85
85
|
"types": "./cards/jsonViewer/index.d.ts"
|
|
86
86
|
},
|
|
87
|
+
"./cards/keyboardOverlay": {
|
|
88
|
+
"import": "./cards/keyboardOverlay/index.js",
|
|
89
|
+
"types": "./cards/keyboardOverlay/index.d.ts"
|
|
90
|
+
},
|
|
87
91
|
"./cards/list": {
|
|
88
92
|
"import": "./cards/list/index.js",
|
|
89
93
|
"types": "./cards/list/index.d.ts"
|
|
@@ -218,6 +222,7 @@
|
|
|
218
222
|
"./cards/infoCard/index.js",
|
|
219
223
|
"./cards/input/index.js",
|
|
220
224
|
"./cards/jsonViewer/index.js",
|
|
225
|
+
"./cards/keyboardOverlay/index.js",
|
|
221
226
|
"./cards/list/index.js",
|
|
222
227
|
"./cards/loadingOverlay/index.js",
|
|
223
228
|
"./cards/loadingSkeleton/index.js",
|