@zappie/foundations 0.0.0-stage → 0.1.1
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/CHANGELOG.md +27 -0
- package/LICENSE +21 -0
- package/README.md +50 -2
- package/dist/chunk-BY2WQGLO.js +97 -0
- package/dist/chunk-BY2WQGLO.js.map +1 -0
- package/dist/chunk-D3J7S33L.js +81 -0
- package/dist/chunk-D3J7S33L.js.map +1 -0
- package/dist/core-DdojsUzR.d.ts +23 -0
- package/dist/engine-XTsgCwwe.d.ts +60 -0
- package/dist/history-Bqu12Vkz.d.ts +8 -0
- package/dist/index.d.ts +808 -0
- package/dist/index.js +2119 -0
- package/dist/index.js.map +1 -0
- package/dist/next.d.ts +11 -0
- package/dist/next.js +56 -0
- package/dist/next.js.map +1 -0
- package/dist/overlay/engine.d.ts +4 -0
- package/dist/overlay/engine.js +172 -0
- package/dist/overlay/engine.js.map +1 -0
- package/dist/rich-text/browser.d.ts +9 -0
- package/dist/rich-text/browser.js +24 -0
- package/dist/rich-text/browser.js.map +1 -0
- package/dist/rich-text/index.d.ts +10 -0
- package/dist/rich-text/index.js +80 -0
- package/dist/rich-text/index.js.map +1 -0
- package/package.json +55 -3
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { partId, part, composeRefs } from '../chunk-D3J7S33L.js';
|
|
3
|
+
import { Dialog } from '@base-ui/react/dialog';
|
|
4
|
+
import { Drawer } from '@base-ui/react/drawer';
|
|
5
|
+
import { createContext, forwardRef, useContext } from 'react';
|
|
6
|
+
import { jsx, jsxs } from 'react/jsx-runtime';
|
|
7
|
+
|
|
8
|
+
var PartsContext = createContext(null);
|
|
9
|
+
function useEngineParts() {
|
|
10
|
+
const parts = useContext(PartsContext);
|
|
11
|
+
if (!parts) {
|
|
12
|
+
throw new Error(
|
|
13
|
+
"Overlay.Title and Overlay.Close must be inside Overlay.Content"
|
|
14
|
+
);
|
|
15
|
+
}
|
|
16
|
+
return parts;
|
|
17
|
+
}
|
|
18
|
+
function changeHandler(props) {
|
|
19
|
+
return (open, details) => {
|
|
20
|
+
if (open) {
|
|
21
|
+
props.onOpen();
|
|
22
|
+
return;
|
|
23
|
+
}
|
|
24
|
+
const callback = details.reason === "escape-key" ? props.onEscape : details.reason === "outside-press" || details.reason === "focus-out" ? props.onInteractOutside : void 0;
|
|
25
|
+
callback?.(details.event);
|
|
26
|
+
if (details.event?.defaultPrevented && callback) {
|
|
27
|
+
details.cancel();
|
|
28
|
+
return;
|
|
29
|
+
}
|
|
30
|
+
props.onClose();
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
function Popup({
|
|
34
|
+
props,
|
|
35
|
+
Component,
|
|
36
|
+
children
|
|
37
|
+
}) {
|
|
38
|
+
const { contentProps, contentRef, presentation, state } = props;
|
|
39
|
+
const { id: _ownId, ...rest } = contentProps;
|
|
40
|
+
const id = partId(contentProps);
|
|
41
|
+
return /* @__PURE__ */ jsx(
|
|
42
|
+
Component,
|
|
43
|
+
{
|
|
44
|
+
id: id ?? props.contentId,
|
|
45
|
+
initialFocus: () => document.getElementById(id ?? props.contentId)?.querySelector("[data-autofocus]") ?? true,
|
|
46
|
+
finalFocus: () => props.trigger.current ?? true,
|
|
47
|
+
render: (baseProps) => {
|
|
48
|
+
const { ref: baseRef, ...base } = baseProps;
|
|
49
|
+
return part(
|
|
50
|
+
"div",
|
|
51
|
+
children === void 0 ? rest : { ...rest, children },
|
|
52
|
+
state,
|
|
53
|
+
{
|
|
54
|
+
...base,
|
|
55
|
+
"aria-modal": true,
|
|
56
|
+
"data-presentation": presentation,
|
|
57
|
+
"data-state": props.open ? "open" : "closed"
|
|
58
|
+
},
|
|
59
|
+
composeRefs(baseRef, contentRef)
|
|
60
|
+
);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
);
|
|
64
|
+
}
|
|
65
|
+
function Surface(props) {
|
|
66
|
+
const { children, forceMount, container, presentation } = props;
|
|
67
|
+
const portal = {
|
|
68
|
+
...forceMount ? { keepMounted: true } : {},
|
|
69
|
+
...container !== void 0 ? { container } : {}
|
|
70
|
+
};
|
|
71
|
+
if (presentation === "dialog") {
|
|
72
|
+
return /* @__PURE__ */ jsx(
|
|
73
|
+
Dialog.Root,
|
|
74
|
+
{
|
|
75
|
+
open: props.open,
|
|
76
|
+
onOpenChange: changeHandler(props),
|
|
77
|
+
children: /* @__PURE__ */ jsx(
|
|
78
|
+
PartsContext.Provider,
|
|
79
|
+
{
|
|
80
|
+
value: { Title: Dialog.Title, Close: Dialog.Close },
|
|
81
|
+
children: /* @__PURE__ */ jsxs(Dialog.Portal, { ...portal, children: [
|
|
82
|
+
/* @__PURE__ */ jsx(
|
|
83
|
+
Dialog.Backdrop,
|
|
84
|
+
{
|
|
85
|
+
"data-zappie-backdrop": "",
|
|
86
|
+
"data-presentation": presentation,
|
|
87
|
+
"data-state": props.open ? "open" : "closed"
|
|
88
|
+
}
|
|
89
|
+
),
|
|
90
|
+
/* @__PURE__ */ jsx(Popup, { props, Component: Dialog.Popup, children })
|
|
91
|
+
] })
|
|
92
|
+
}
|
|
93
|
+
)
|
|
94
|
+
}
|
|
95
|
+
);
|
|
96
|
+
}
|
|
97
|
+
return /* @__PURE__ */ jsx(
|
|
98
|
+
Drawer.Root,
|
|
99
|
+
{
|
|
100
|
+
open: props.open,
|
|
101
|
+
onOpenChange: changeHandler(props),
|
|
102
|
+
swipeDirection: props.swipeDirection,
|
|
103
|
+
children: /* @__PURE__ */ jsx(
|
|
104
|
+
PartsContext.Provider,
|
|
105
|
+
{
|
|
106
|
+
value: {
|
|
107
|
+
Title: Drawer.Title,
|
|
108
|
+
Close: Drawer.Close
|
|
109
|
+
},
|
|
110
|
+
children: /* @__PURE__ */ jsxs(Drawer.Portal, { ...portal, children: [
|
|
111
|
+
/* @__PURE__ */ jsx(
|
|
112
|
+
Drawer.Backdrop,
|
|
113
|
+
{
|
|
114
|
+
"data-zappie-backdrop": "",
|
|
115
|
+
"data-presentation": presentation,
|
|
116
|
+
"data-state": props.open ? "open" : "closed"
|
|
117
|
+
}
|
|
118
|
+
),
|
|
119
|
+
/* @__PURE__ */ jsx(Drawer.VirtualKeyboardProvider, { children: /* @__PURE__ */ jsx(Drawer.Viewport, { children: /* @__PURE__ */ jsx(
|
|
120
|
+
Popup,
|
|
121
|
+
{
|
|
122
|
+
props,
|
|
123
|
+
Component: Drawer.Popup,
|
|
124
|
+
children
|
|
125
|
+
}
|
|
126
|
+
) }) })
|
|
127
|
+
] })
|
|
128
|
+
}
|
|
129
|
+
)
|
|
130
|
+
}
|
|
131
|
+
);
|
|
132
|
+
}
|
|
133
|
+
var EngineTitle = forwardRef(
|
|
134
|
+
function EngineTitle2({ state, ...props }, ref) {
|
|
135
|
+
const { Title } = useEngineParts();
|
|
136
|
+
const id = partId(props);
|
|
137
|
+
return /* @__PURE__ */ jsx(
|
|
138
|
+
Title,
|
|
139
|
+
{
|
|
140
|
+
...id ? { id } : {},
|
|
141
|
+
render: (base) => {
|
|
142
|
+
const { ref: baseRef, ...rest } = base;
|
|
143
|
+
return part("h2", props, state, rest, composeRefs(baseRef, ref));
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
);
|
|
147
|
+
}
|
|
148
|
+
);
|
|
149
|
+
var EngineClose = forwardRef(
|
|
150
|
+
function EngineClose2({ state, ...props }, ref) {
|
|
151
|
+
const { Close } = useEngineParts();
|
|
152
|
+
return /* @__PURE__ */ jsx(
|
|
153
|
+
Close,
|
|
154
|
+
{
|
|
155
|
+
render: (base) => {
|
|
156
|
+
const { ref: baseRef, ...rest } = base;
|
|
157
|
+
return part(
|
|
158
|
+
"button",
|
|
159
|
+
props,
|
|
160
|
+
state,
|
|
161
|
+
{ type: "button", ...rest },
|
|
162
|
+
composeRefs(baseRef, ref)
|
|
163
|
+
);
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
);
|
|
167
|
+
}
|
|
168
|
+
);
|
|
169
|
+
|
|
170
|
+
export { EngineClose, EngineTitle, Surface, useEngineParts };
|
|
171
|
+
//# sourceMappingURL=engine.js.map
|
|
172
|
+
//# sourceMappingURL=engine.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/overlay/engine.tsx"],"names":["EngineTitle","EngineClose"],"mappings":"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","file":"engine.js","sourcesContent":["\"use client\";\n// The overlay engine: the only module that imports Base UI. It loads on first open (or an\n// idle prefetch) so dialog and drawer mechanics stay out of the initial bundle.\nimport { Dialog } from \"@base-ui/react/dialog\";\nimport { Drawer } from \"@base-ui/react/drawer\";\nimport {\n createContext,\n forwardRef,\n useContext,\n type ReactNode,\n type Ref,\n} from \"react\";\nimport { composeRefs, part, partId, type PartProps } from \"../internal/slot\";\n\nexport type Presentation = \"dialog\" | \"drawer\" | \"sheet\";\nexport type SwipeDirection = \"up\" | \"down\" | \"left\" | \"right\";\ntype ChangeDetails = {\n reason: string;\n event: Event;\n cancel: () => void;\n};\nexport type SurfaceProps = {\n open: boolean;\n presentation: Presentation;\n swipeDirection: SwipeDirection;\n /** Ids the trigger points at; caller-supplied ids win. */\n contentId: string;\n titleId?: string | undefined;\n trigger: { current: HTMLElement | null };\n onOpen: () => void;\n onClose: () => void;\n onEscape?: ((event: KeyboardEvent) => void) | undefined;\n onInteractOutside?: ((event: Event) => void) | undefined;\n forceMount?: true | undefined;\n container?: HTMLElement | null | undefined;\n state: unknown;\n contentProps: PartProps<never>;\n contentRef: Ref<HTMLElement>;\n};\ntype Parts = { Title: typeof Dialog.Title; Close: typeof Dialog.Close };\nconst PartsContext = createContext<Parts | null>(null);\nexport function useEngineParts() {\n const parts = useContext(PartsContext);\n if (!parts) {\n throw new Error(\n \"Overlay.Title and Overlay.Close must be inside Overlay.Content\",\n );\n }\n return parts;\n}\n/** Route Base UI's open changes through the URL, letting callbacks cancel a dismissal. */\nfunction changeHandler(props: SurfaceProps) {\n return (open: boolean, details: ChangeDetails) => {\n if (open) {\n props.onOpen();\n return;\n }\n const callback =\n details.reason === \"escape-key\"\n ? (props.onEscape as ((event: Event) => void) | undefined)\n : details.reason === \"outside-press\" || details.reason === \"focus-out\"\n ? props.onInteractOutside\n : undefined;\n callback?.(details.event);\n if (details.event?.defaultPrevented && callback) {\n details.cancel();\n return;\n }\n props.onClose();\n };\n}\nfunction Popup({\n props,\n Component,\n children,\n}: {\n props: SurfaceProps;\n Component: typeof Dialog.Popup;\n children?: ReactNode;\n}) {\n const { contentProps, contentRef, presentation, state } = props;\n const { id: _ownId, ...rest } = contentProps as PartProps<never> & {\n id?: string;\n };\n const id = partId(contentProps as Parameters<typeof partId>[0]);\n return (\n <Component\n id={id ?? props.contentId}\n // Prefer an explicit [data-autofocus] target, else Base UI's first tabbable.\n initialFocus={() =>\n document\n .getElementById(id ?? props.contentId)\n ?.querySelector<HTMLElement>(\"[data-autofocus]\") ?? true\n }\n finalFocus={() => props.trigger.current ?? true}\n render={(baseProps) => {\n const { ref: baseRef, ...base } = baseProps as unknown as Record<\n string,\n unknown\n > & {\n ref?: Ref<HTMLElement>;\n };\n return part(\n \"div\",\n (children === undefined\n ? rest\n : { ...rest, children }) as PartProps<unknown>,\n state,\n {\n ...base,\n \"aria-modal\": true,\n \"data-presentation\": presentation,\n \"data-state\": props.open ? \"open\" : \"closed\",\n },\n composeRefs(baseRef, contentRef),\n );\n }}\n />\n );\n}\nexport function Surface(props: SurfaceProps & { children?: ReactNode }) {\n const { children, forceMount, container, presentation } = props;\n const portal = {\n ...(forceMount ? { keepMounted: true } : {}),\n ...(container !== undefined ? { container } : {}),\n };\n if (presentation === \"dialog\") {\n return (\n <Dialog.Root\n open={props.open}\n onOpenChange={changeHandler(props) as never}\n >\n <PartsContext.Provider\n value={{ Title: Dialog.Title, Close: Dialog.Close }}\n >\n <Dialog.Portal {...portal}>\n {/* Unstyled; templates style [data-zappie-backdrop] with data-state/data-presentation. */}\n <Dialog.Backdrop\n data-zappie-backdrop=\"\"\n data-presentation={presentation}\n data-state={props.open ? \"open\" : \"closed\"}\n />\n <Popup props={props} Component={Dialog.Popup}>\n {children}\n </Popup>\n </Dialog.Portal>\n </PartsContext.Provider>\n </Dialog.Root>\n );\n }\n return (\n <Drawer.Root\n open={props.open}\n onOpenChange={changeHandler(props) as never}\n swipeDirection={props.swipeDirection}\n >\n <PartsContext.Provider\n value={{\n Title: Drawer.Title as typeof Dialog.Title,\n Close: Drawer.Close as typeof Dialog.Close,\n }}\n >\n <Drawer.Portal {...portal}>\n <Drawer.Backdrop\n data-zappie-backdrop=\"\"\n data-presentation={presentation}\n data-state={props.open ? \"open\" : \"closed\"}\n />\n {/* Keeps focused fields visible above the on-screen keyboard in bottom sheets. */}\n <Drawer.VirtualKeyboardProvider>\n <Drawer.Viewport>\n <Popup\n props={props}\n Component={Drawer.Popup as typeof Dialog.Popup}\n >\n {children}\n </Popup>\n </Drawer.Viewport>\n </Drawer.VirtualKeyboardProvider>\n </Drawer.Portal>\n </PartsContext.Provider>\n </Drawer.Root>\n );\n}\n/** Title and Close rendered with whichever engine (dialog or drawer) owns the content. */\ntype EnginePartProps = PartProps<unknown> & {\n id?: string | undefined;\n state: unknown;\n};\nexport const EngineTitle = forwardRef<HTMLElement, EnginePartProps>(\n function EngineTitle({ state, ...props }, ref) {\n const { Title } = useEngineParts();\n const id = partId(props as Parameters<typeof partId>[0]);\n return (\n <Title\n {...(id ? { id } : {})}\n render={(base) => {\n const { ref: baseRef, ...rest } = base as unknown as Record<\n string,\n unknown\n > & {\n ref?: Ref<HTMLElement>;\n };\n return part(\"h2\", props, state, rest, composeRefs(baseRef, ref));\n }}\n />\n );\n },\n);\nexport const EngineClose = forwardRef<HTMLElement, EnginePartProps>(\n function EngineClose({ state, ...props }, ref) {\n const { Close } = useEngineParts();\n return (\n <Close\n render={(base) => {\n const { ref: baseRef, ...rest } = base as unknown as Record<\n string,\n unknown\n > & {\n ref?: Ref<HTMLElement>;\n };\n return part(\n \"button\",\n props,\n state,\n { type: \"button\", ...rest },\n composeRefs(baseRef, ref),\n );\n }}\n />\n );\n },\n);\n"]}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { P as ParsedRichTextNode } from '../core-DdojsUzR.js';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Parse HTML with the browser DOMParser and convert body children to the shared rich-text node format.
|
|
5
|
+
* Requires a DOM environment. Pair with createSanitizeRichText to remove unsupported content.
|
|
6
|
+
*/
|
|
7
|
+
declare const parseRichText: (html: string) => ParsedRichTextNode[];
|
|
8
|
+
|
|
9
|
+
export { parseRichText };
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
// src/rich-text/browser.ts
|
|
2
|
+
var TEXT_NODE = 3;
|
|
3
|
+
var COMMENT_NODE = 8;
|
|
4
|
+
var convertNode = (node) => {
|
|
5
|
+
if (node.nodeType === TEXT_NODE) {
|
|
6
|
+
return { type: "text", data: node.nodeValue || "" };
|
|
7
|
+
}
|
|
8
|
+
if (node.nodeType === COMMENT_NODE) {
|
|
9
|
+
return { type: "comment" };
|
|
10
|
+
}
|
|
11
|
+
return {
|
|
12
|
+
type: "tag",
|
|
13
|
+
name: node.nodeName.toLowerCase(),
|
|
14
|
+
children: Array.from(node.childNodes).map(convertNode)
|
|
15
|
+
};
|
|
16
|
+
};
|
|
17
|
+
var parseRichText = (html) => {
|
|
18
|
+
const document = new DOMParser().parseFromString(html, "text/html");
|
|
19
|
+
return Array.from(document.body.childNodes).map(convertNode);
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
export { parseRichText };
|
|
23
|
+
//# sourceMappingURL=browser.js.map
|
|
24
|
+
//# sourceMappingURL=browser.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/rich-text/browser.ts"],"names":[],"mappings":";AAEA,IAAM,SAAA,GAAY,CAAA;AAClB,IAAM,YAAA,GAAe,CAAA;AAErB,IAAM,WAAA,GAAc,CAAC,IAAA,KAAmC;AACtD,EAAA,IAAI,IAAA,CAAK,aAAa,SAAA,EAAW;AAC/B,IAAA,OAAO,EAAE,IAAA,EAAM,MAAA,EAAQ,IAAA,EAAM,IAAA,CAAK,aAAa,EAAA,EAAG;AAAA,EACpD;AAEA,EAAA,IAAI,IAAA,CAAK,aAAa,YAAA,EAAc;AAClC,IAAA,OAAO,EAAE,MAAM,SAAA,EAAU;AAAA,EAC3B;AAEA,EAAA,OAAO;AAAA,IACL,IAAA,EAAM,KAAA;AAAA,IACN,IAAA,EAAM,IAAA,CAAK,QAAA,CAAS,WAAA,EAAY;AAAA,IAChC,UAAU,KAAA,CAAM,IAAA,CAAK,KAAK,UAAU,CAAA,CAAE,IAAI,WAAW;AAAA,GACvD;AACF,CAAA;AAMO,IAAM,aAAA,GAAgB,CAAC,IAAA,KAAuC;AACnE,EAAA,MAAM,WAAW,IAAI,SAAA,EAAU,CAAE,eAAA,CAAgB,MAAM,WAAW,CAAA;AAClE,EAAA,OAAO,MAAM,IAAA,CAAK,QAAA,CAAS,KAAK,UAAU,CAAA,CAAE,IAAI,WAAW,CAAA;AAC7D","file":"browser.js","sourcesContent":["import type { ParsedRichTextNode } from \"./core\";\n\nconst TEXT_NODE = 3;\nconst COMMENT_NODE = 8;\n\nconst convertNode = (node: Node): ParsedRichTextNode => {\n if (node.nodeType === TEXT_NODE) {\n return { type: \"text\", data: node.nodeValue || \"\" };\n }\n\n if (node.nodeType === COMMENT_NODE) {\n return { type: \"comment\" };\n }\n\n return {\n type: \"tag\",\n name: node.nodeName.toLowerCase(),\n children: Array.from(node.childNodes).map(convertNode),\n };\n};\n\n/**\n * Parse HTML with the browser DOMParser and convert body children to the shared rich-text node format.\n * Requires a DOM environment. Pair with createSanitizeRichText to remove unsupported content.\n */\nexport const parseRichText = (html: string): ParsedRichTextNode[] => {\n const document = new DOMParser().parseFromString(html, \"text/html\");\n return Array.from(document.body.childNodes).map(convertNode);\n};\n"]}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export { a as ParseRichText, P as ParsedRichTextNode, c as createSanitizeRichText } from '../core-DdojsUzR.js';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* The separator used to store multiple menu info banners in a single string.
|
|
5
|
+
*/
|
|
6
|
+
declare const BANNER_DELIMITER = "<::>";
|
|
7
|
+
/** Split a stored info-banner value into individual banner HTML fragments. */
|
|
8
|
+
declare const splitInfoBanner: (infoBanner?: string | null) => string[];
|
|
9
|
+
|
|
10
|
+
export { BANNER_DELIMITER, splitInfoBanner };
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
// src/rich-text/banners.ts
|
|
2
|
+
var BANNER_DELIMITER = "<::>";
|
|
3
|
+
var normalizeBannerDelimiters = (html) => html.replace(/<p>\s*(?:<::>|<::>)\s*<\/p>/gi, BANNER_DELIMITER).replaceAll("<::>", BANNER_DELIMITER);
|
|
4
|
+
var splitInfoBanner = (infoBanner) => normalizeBannerDelimiters(infoBanner ?? "").split(BANNER_DELIMITER);
|
|
5
|
+
|
|
6
|
+
// src/rich-text/core.ts
|
|
7
|
+
var ALLOWED_TAGS = /* @__PURE__ */ new Set([
|
|
8
|
+
"b",
|
|
9
|
+
"blockquote",
|
|
10
|
+
"br",
|
|
11
|
+
"code",
|
|
12
|
+
"del",
|
|
13
|
+
"em",
|
|
14
|
+
"h1",
|
|
15
|
+
"h2",
|
|
16
|
+
"h3",
|
|
17
|
+
"h4",
|
|
18
|
+
"h5",
|
|
19
|
+
"h6",
|
|
20
|
+
"hr",
|
|
21
|
+
"i",
|
|
22
|
+
"li",
|
|
23
|
+
"ol",
|
|
24
|
+
"p",
|
|
25
|
+
"pre",
|
|
26
|
+
"s",
|
|
27
|
+
"span",
|
|
28
|
+
"strike",
|
|
29
|
+
"strong",
|
|
30
|
+
"sub",
|
|
31
|
+
"sup",
|
|
32
|
+
"u",
|
|
33
|
+
"ul"
|
|
34
|
+
]);
|
|
35
|
+
var DROP_CONTENT_TAGS = /* @__PURE__ */ new Set([
|
|
36
|
+
"audio",
|
|
37
|
+
"canvas",
|
|
38
|
+
"embed",
|
|
39
|
+
"iframe",
|
|
40
|
+
"math",
|
|
41
|
+
"noscript",
|
|
42
|
+
"object",
|
|
43
|
+
"script",
|
|
44
|
+
"style",
|
|
45
|
+
"svg",
|
|
46
|
+
"template",
|
|
47
|
+
"video"
|
|
48
|
+
]);
|
|
49
|
+
var VOID_TAGS = /* @__PURE__ */ new Set(["br", "hr"]);
|
|
50
|
+
var escapeText = (value) => value.replaceAll("\r\n", "\n").replaceAll("\r", "\n").replaceAll("&", "&").replaceAll("\xA0", " ").replaceAll("<", "<").replaceAll(">", ">");
|
|
51
|
+
var serializeNode = (node) => {
|
|
52
|
+
if (node.type === "text") {
|
|
53
|
+
return escapeText(node.data || "");
|
|
54
|
+
}
|
|
55
|
+
if (node.type === "comment" || node.type === "directive") {
|
|
56
|
+
return "";
|
|
57
|
+
}
|
|
58
|
+
const tagName = node.name?.toLowerCase();
|
|
59
|
+
if (tagName && DROP_CONTENT_TAGS.has(tagName)) {
|
|
60
|
+
return "";
|
|
61
|
+
}
|
|
62
|
+
const children = (node.children || []).map(serializeNode).join("");
|
|
63
|
+
if (!tagName || !ALLOWED_TAGS.has(tagName)) {
|
|
64
|
+
return children;
|
|
65
|
+
}
|
|
66
|
+
if (VOID_TAGS.has(tagName)) {
|
|
67
|
+
return `<${tagName}>`;
|
|
68
|
+
}
|
|
69
|
+
return `<${tagName}>${children}</${tagName}>`;
|
|
70
|
+
};
|
|
71
|
+
var createSanitizeRichText = (parseRichText) => (html) => {
|
|
72
|
+
if (!html) {
|
|
73
|
+
return "";
|
|
74
|
+
}
|
|
75
|
+
return parseRichText(html).map(serializeNode).join("");
|
|
76
|
+
};
|
|
77
|
+
|
|
78
|
+
export { BANNER_DELIMITER, createSanitizeRichText, splitInfoBanner };
|
|
79
|
+
//# sourceMappingURL=index.js.map
|
|
80
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/rich-text/banners.ts","../../src/rich-text/core.ts"],"names":[],"mappings":"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","file":"index.js","sourcesContent":["// Multiple menu info banners are stored as one string joined by this delimiter\n// (the dashboard editor joins on it; here we split it back out for display).\n/**\n * The separator used to store multiple menu info banners in a single string.\n */\nexport const BANNER_DELIMITER = \"<::>\";\n\n// The delimiter looks like an HTML tag, so once it lands inside the dashboard's\n// rich-text editor as content it gets serialised as escaped text — `<::>`,\n// usually on its own `<p>` line. Splitting on the literal `<::>` then finds\n// nothing and every banner collapses into one. Convert the escaped form (and its\n// `<p>` wrapper) back to the real delimiter before splitting.\nconst normalizeBannerDelimiters = (html: string): string =>\n html\n .replace(/<p>\\s*(?:<::>|<::>)\\s*<\\/p>/gi, BANNER_DELIMITER)\n .replaceAll(\"<::>\", BANNER_DELIMITER);\n\n/** Split a stored info-banner value into individual banner HTML fragments. */\nexport const splitInfoBanner = (infoBanner?: string | null): string[] =>\n normalizeBannerDelimiters(infoBanner ?? \"\").split(BANNER_DELIMITER);\n","/**\n * A parser-neutral rich-text node containing its type, optional tag name, text and children.\n */\nexport type ParsedRichTextNode = {\n type: string;\n name?: string;\n data?: string;\n children?: ParsedRichTextNode[];\n};\n\n/**\n * A parser adapter that converts HTML into the node tree consumed by the rich-text sanitizer.\n */\nexport type ParseRichText = (html: string) => ParsedRichTextNode[];\n\nconst ALLOWED_TAGS = new Set([\n \"b\",\n \"blockquote\",\n \"br\",\n \"code\",\n \"del\",\n \"em\",\n \"h1\",\n \"h2\",\n \"h3\",\n \"h4\",\n \"h5\",\n \"h6\",\n \"hr\",\n \"i\",\n \"li\",\n \"ol\",\n \"p\",\n \"pre\",\n \"s\",\n \"span\",\n \"strike\",\n \"strong\",\n \"sub\",\n \"sup\",\n \"u\",\n \"ul\",\n]);\n\nconst DROP_CONTENT_TAGS = new Set([\n \"audio\",\n \"canvas\",\n \"embed\",\n \"iframe\",\n \"math\",\n \"noscript\",\n \"object\",\n \"script\",\n \"style\",\n \"svg\",\n \"template\",\n \"video\",\n]);\n\nconst VOID_TAGS = new Set([\"br\", \"hr\"]);\n\nconst escapeText = (value: string): string =>\n value\n .replaceAll(\"\\r\\n\", \"\\n\")\n .replaceAll(\"\\r\", \"\\n\")\n .replaceAll(\"&\", \"&\")\n .replaceAll(\"\\u00a0\", \" \")\n .replaceAll(\"<\", \"<\")\n .replaceAll(\">\", \">\");\n\nconst serializeNode = (node: ParsedRichTextNode): string => {\n if (node.type === \"text\") {\n return escapeText(node.data || \"\");\n }\n\n if (node.type === \"comment\" || node.type === \"directive\") {\n return \"\";\n }\n\n const tagName = node.name?.toLowerCase();\n if (tagName && DROP_CONTENT_TAGS.has(tagName)) {\n return \"\";\n }\n\n const children = (node.children || []).map(serializeNode).join(\"\");\n if (!tagName || !ALLOWED_TAGS.has(tagName)) {\n return children;\n }\n\n if (VOID_TAGS.has(tagName)) {\n return `<${tagName}>`;\n }\n\n return `<${tagName}>${children}</${tagName}>`;\n};\n\n/**\n * Create a sanitizer using your HTML parser adapter.\n * Keeps supported formatting tags without attributes, escapes text and removes unsafe element content.\n *\n * @param parseRichText - A parser that returns the shared rich-text node format.\n * @returns A sanitizer that accepts HTML and returns sanitized HTML, or an empty string for empty input.\n */\nexport const createSanitizeRichText =\n (parseRichText: ParseRichText) =>\n (html?: string | null): string => {\n if (!html) {\n return \"\";\n }\n\n return parseRichText(html).map(serializeNode).join(\"\");\n };\n"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,58 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@zappie/foundations",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"
|
|
5
|
-
"
|
|
3
|
+
"version": "0.1.1",
|
|
4
|
+
"description": "UI-less React primitives for Zappie storefronts: behaviour, state and accessibility without styles.",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"sideEffects": false,
|
|
7
|
+
"exports": {
|
|
8
|
+
".": {
|
|
9
|
+
"types": "./dist/index.d.ts",
|
|
10
|
+
"import": "./dist/index.js"
|
|
11
|
+
},
|
|
12
|
+
"./next": {
|
|
13
|
+
"types": "./dist/next.d.ts",
|
|
14
|
+
"import": "./dist/next.js"
|
|
15
|
+
},
|
|
16
|
+
"./rich-text": {
|
|
17
|
+
"types": "./dist/rich-text/index.d.ts",
|
|
18
|
+
"import": "./dist/rich-text/index.js"
|
|
19
|
+
},
|
|
20
|
+
"./rich-text/browser": {
|
|
21
|
+
"types": "./dist/rich-text/browser.d.ts",
|
|
22
|
+
"import": "./dist/rich-text/browser.js"
|
|
23
|
+
}
|
|
24
|
+
},
|
|
25
|
+
"publishConfig": {
|
|
26
|
+
"access": "public",
|
|
27
|
+
"provenance": false
|
|
28
|
+
},
|
|
29
|
+
"files": [
|
|
30
|
+
"dist",
|
|
31
|
+
"CHANGELOG.md"
|
|
32
|
+
],
|
|
33
|
+
"dependencies": {
|
|
34
|
+
"@base-ui/react": "^1.8.0",
|
|
35
|
+
"@zappie/sdk": "^0.1.1"
|
|
36
|
+
},
|
|
37
|
+
"peerDependencies": {
|
|
38
|
+
"react": "^18.2.0 || ^19.0.0",
|
|
39
|
+
"react-dom": "^18.2.0 || ^19.0.0"
|
|
40
|
+
},
|
|
41
|
+
"repository": {
|
|
42
|
+
"type": "git",
|
|
43
|
+
"url": "git+https://github.com/zappie/storefront.git",
|
|
44
|
+
"directory": "packages/foundations"
|
|
45
|
+
},
|
|
46
|
+
"devDependencies": {
|
|
47
|
+
"esbuild": "^0.27.7"
|
|
48
|
+
},
|
|
49
|
+
"license": "MIT",
|
|
50
|
+
"homepage": "https://github.com/zappie/storefront#readme",
|
|
51
|
+
"bugs": {
|
|
52
|
+
"url": "https://github.com/zappie/storefront/issues"
|
|
53
|
+
},
|
|
54
|
+
"scripts": {
|
|
55
|
+
"build": "tsup",
|
|
56
|
+
"size": "node scripts/check-size.mjs"
|
|
57
|
+
}
|
|
6
58
|
}
|