@janbox/storefront-builder 1.0.4 → 1.0.6

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 (41) hide show
  1. package/README.md +480 -0
  2. package/dist/editor/hooks/use-editor-state.d.ts +13 -1
  3. package/dist/editor/lib/index.d.ts +4 -1
  4. package/dist/editor/ui/dialog/dialog/helpers.d.ts +1 -1
  5. package/dist/editor/ui/dialog/dialog-close/dialog-close.d.ts +1 -1
  6. package/dist/editor.js +356 -351
  7. package/dist/{index-CBDllFpx.js → index-BU9pvpw_.js} +263 -256
  8. package/dist/index.js +428 -419
  9. package/dist/lib/accordion/README.md +39 -0
  10. package/dist/lib/accordion-content/README.md +36 -0
  11. package/dist/lib/accordion-group/README.md +106 -0
  12. package/dist/lib/accordion-summary/README.md +37 -0
  13. package/dist/lib/box/README.md +107 -0
  14. package/dist/lib/button/README.md +94 -0
  15. package/dist/lib/cell/README.md +119 -0
  16. package/dist/lib/countdown-timer/README.md +157 -0
  17. package/dist/lib/flex-item/README.md +111 -0
  18. package/dist/lib/flexbox/README.md +120 -0
  19. package/dist/lib/grid/README.md +133 -0
  20. package/dist/lib/heading/README.md +53 -0
  21. package/dist/lib/icon/README.md +98 -0
  22. package/dist/lib/image/README.md +107 -0
  23. package/dist/lib/link/README.md +111 -0
  24. package/dist/lib/list-item/README.md +31 -0
  25. package/dist/lib/marquee/README.md +111 -0
  26. package/dist/lib/marquee-item/README.md +31 -0
  27. package/dist/lib/paragraph/README.md +82 -0
  28. package/dist/lib/root/README.md +33 -0
  29. package/dist/lib/swiper/README.md +108 -0
  30. package/dist/lib/swiper-slide/README.md +32 -0
  31. package/dist/lib/tab/README.md +35 -0
  32. package/dist/lib/tab-content/README.md +35 -0
  33. package/dist/lib/tab-list/README.md +35 -0
  34. package/dist/lib/tab-panel/README.md +35 -0
  35. package/dist/lib/tabs/README.md +90 -0
  36. package/dist/lib/text/README.md +90 -0
  37. package/dist/lib/unknown/README.md +48 -0
  38. package/dist/lib/unordered-list/README.md +82 -0
  39. package/dist/lib/video/README.md +91 -0
  40. package/package.json +4 -10
  41. package/dist/editor/lib/events.d.ts +0 -5
package/README.md ADDED
@@ -0,0 +1,480 @@
1
+ # @janbox/storefront-builder
2
+
3
+ A standalone visual page builder library built on [CraftJS](https://craft.js.org/). Provides drag-and-drop storefront creation with a full editor UI, 33 pre-built node components, and responsive design support.
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ npm install @janbox/storefront-builder
9
+ # or
10
+ pnpm add @janbox/storefront-builder
11
+ ```
12
+
13
+ ### Peer dependencies
14
+
15
+ ```bash
16
+ npm install react@^19 react-dom@^19 @janbox/storefront-ui@>=2
17
+ ```
18
+
19
+ ## Package Exports
20
+
21
+ | Import path | Contents |
22
+ | --- | --- |
23
+ | `@janbox/storefront-builder` | Node components (all 33 nodes) |
24
+ | `@janbox/storefront-builder/editor` | Full editor UI (`Editor` component + toolbars) |
25
+ | `@janbox/storefront-builder/templates` | Pre-built element groups and section templates |
26
+ | `@janbox/storefront-builder/style.css` | Required CSS bundle |
27
+
28
+ ---
29
+
30
+ ## Quick Start — Full Editor
31
+
32
+ The `Editor` component from `/editor` renders the complete visual builder UI (header toolbar, sidebar with component library, drag-and-drop canvas).
33
+
34
+ ```tsx
35
+ import '@janbox/storefront-builder/style.css';
36
+ import { Editor } from '@janbox/storefront-builder/editor';
37
+ import {
38
+ LAYOUT_ELEMENT_GROUP,
39
+ TEXT_ELEMENT_GROUP,
40
+ MEDIA_ELEMENT_GROUP,
41
+ INTERACTIVE_ELEMENT_GROUP,
42
+ HERO_SECTION_GROUP,
43
+ } from '@janbox/storefront-builder/templates';
44
+ import type { Theme } from '@janbox/storefront-ui/theme';
45
+
46
+ const myTheme: Theme = {
47
+ palette: {
48
+ primary: {
49
+ 500: '#fa8c16',
50
+ 600: '#d46b08',
51
+ },
52
+ // ... full theme
53
+ },
54
+ typography: {
55
+ base: { fontSize: '1rem', lineHeight: '1.5rem' },
56
+ // ...
57
+ },
58
+ };
59
+
60
+ export default function BuilderPage() {
61
+ return (
62
+ <Editor
63
+ theme={myTheme}
64
+ handlers={{
65
+ upload: async () => '',
66
+ submit: (_data) => {},
67
+ close: () => {},
68
+ }}
69
+ insert={{
70
+ elements: [LAYOUT_ELEMENT_GROUP, TEXT_ELEMENT_GROUP, MEDIA_ELEMENT_GROUP, INTERACTIVE_ELEMENT_GROUP],
71
+ sections: [HERO_SECTION_GROUP],
72
+ }}
73
+ />
74
+ );
75
+ }
76
+ ```
77
+
78
+ `Editor` occupies the full viewport (`100vw × 100vh`). It should be the root of a dedicated page/route.
79
+
80
+ ---
81
+
82
+ ## Quick Start — Render Only (No Editor UI)
83
+
84
+ Use `Composer` + `Canvas` to render a saved page without the editor chrome. This is for the storefront-facing page.
85
+
86
+ ```tsx
87
+ import '@janbox/storefront-builder/style.css';
88
+ import { Composer, Canvas } from '@janbox/storefront-builder';
89
+ import { RootNode } from '@janbox/storefront-builder';
90
+ import type { SerializedNodes } from '@janbox/storefront-builder';
91
+
92
+ const savedData: SerializedNodes = { /* JSON from editor */ };
93
+
94
+ export default function StorefrontPage() {
95
+ return (
96
+ <Composer>
97
+ <Canvas data={savedData}>
98
+ <RootNode />
99
+ </Canvas>
100
+ </Composer>
101
+ );
102
+ }
103
+ ```
104
+
105
+ ---
106
+
107
+ ## Core Concepts
108
+
109
+ ### Nodes
110
+
111
+ Every element on the canvas is a **node** — a React component registered with CraftJS. The library ships 33 built-in nodes:
112
+
113
+ | Category | Nodes |
114
+ | --- | --- |
115
+ | Layout | `RootNode`, `Box`, `Flexbox`, `FlexItem`, `Grid`, `Cell` |
116
+ | Typography | `Text`, `Paragraph`, `Heading` |
117
+ | Media | `Image`, `Video`, `Icon` |
118
+ | Navigation | `Link`, `Button` |
119
+ | Lists | `UnorderedList`, `ListItem` |
120
+ | Tabs | `Tabs`, `TabList`, `Tab`, `TabContent`, `TabPanel` |
121
+ | Accordion | `Accordion`, `AccordionGroup`, `AccordionSummary`, `AccordionContent` |
122
+ | Carousel | `Swiper`, `SwiperSlide` |
123
+ | Marquee | `Marquee`, `MarqueeItem` |
124
+ | Utilities | `CountdownTimer` |
125
+ | Internal | `UnknownNode` (fallback for unresolved nodes) |
126
+
127
+ All nodes are exported from `@janbox/storefront-builder`.
128
+
129
+ ### Responsive Props
130
+
131
+ All style props accept either a plain value or a responsive object keyed by breakpoint:
132
+
133
+ ```ts
134
+ // Breakpoints: xs (390px), sm (768px), md (1280px), lg (1680px)
135
+
136
+ // Plain value (applies at all breakpoints)
137
+ { fontSize: '16px' }
138
+
139
+ // Responsive object (xs is the base; other keys override upward)
140
+ { fontSize: { xs: '14px', md: '18px' } }
141
+ ```
142
+
143
+ The current breakpoint is controlled via the `?screen=xs|sm|md|lg` query param.
144
+
145
+ ### Serialized State
146
+
147
+ The canvas state is plain JSON (`SerializedNodes` from `@craftjs/core`). Use `useComposer()` to read and serialize it:
148
+
149
+ ```tsx
150
+ import { useComposer } from '@janbox/storefront-builder';
151
+
152
+ function SaveButton() {
153
+ const { query } = useComposer();
154
+
155
+ const handleSave = () => {
156
+ const json = query.serialize(); // SerializedNodes as JSON string
157
+ // save to your backend
158
+ };
159
+
160
+ return <button onClick={handleSave}>Save</button>;
161
+ }
162
+ ```
163
+
164
+ ---
165
+
166
+ ## Templates
167
+
168
+ Templates are pre-configured drag-and-drop groups shown in the editor sidebar.
169
+
170
+ ### Element Groups (sidebar "Elements" tab)
171
+
172
+ ```ts
173
+ import {
174
+ LAYOUT_ELEMENT_GROUP, // Box, Flexbox, Grid
175
+ TEXT_ELEMENT_GROUP, // Text, Heading, Paragraph
176
+ MEDIA_ELEMENT_GROUP, // Image, Video, Icon
177
+ INTERACTIVE_ELEMENT_GROUP, // Button, Link, Accordion, Tabs
178
+ CONTENT_LIST_ELEMENT_GROUP, // UnorderedList, Marquee, Swiper
179
+ UTILITIES_ELEMENT_GROUP, // CountdownTimer
180
+ } from '@janbox/storefront-builder/templates';
181
+ ```
182
+
183
+ ### Section Groups (sidebar "Sections" tab)
184
+
185
+ ```ts
186
+ import {
187
+ HERO_SECTION_GROUP,
188
+ FAQS_SECTION_GROUP,
189
+ GUARANTEE_SECTION_GROUP,
190
+ } from '@janbox/storefront-builder/templates';
191
+ ```
192
+
193
+ Pass them to `Editor` via the `insert` prop:
194
+
195
+ ```tsx
196
+ <Editor
197
+ theme={myTheme}
198
+ handlers={{
199
+ upload: async () => '',
200
+ submit: () => {},
201
+ close: () => {},
202
+ }}
203
+ insert={{
204
+ elements: [LAYOUT_ELEMENT_GROUP, TEXT_ELEMENT_GROUP],
205
+ sections: [HERO_SECTION_GROUP],
206
+ }}
207
+ />
208
+ ```
209
+
210
+ ---
211
+
212
+ ## Custom Nodes
213
+
214
+ ### 1. Create a node component
215
+
216
+ ```tsx
217
+ // my-badge.node.tsx
218
+ import { defineNode } from '@janbox/storefront-builder';
219
+ import { useNode } from '@janbox/storefront-builder';
220
+
221
+ type MyBadgeProps = { label: string; color?: string };
222
+
223
+ export const MyBadge = ({ label, color = '#fa8c16' }: MyBadgeProps) => {
224
+ const { connectors } = useNode();
225
+
226
+ return (
227
+ <span
228
+ ref={(el) => el && connectors.connect(el)}
229
+ style={{ background: color, padding: '2px 8px', borderRadius: 4 }}
230
+ >
231
+ {label}
232
+ </span>
233
+ );
234
+ };
235
+
236
+ defineNode(MyBadge, {
237
+ resolved: 'MyBadge',
238
+ isCanvas: false,
239
+ info: { displayName: 'Badge' },
240
+ defaultProps: { label: 'Badge', color: '#fa8c16' },
241
+ });
242
+ ```
243
+
244
+ ### 2. Pass a resolver to the editor
245
+
246
+ ```tsx
247
+ import { MyBadge } from './my-badge.node';
248
+
249
+ <Editor
250
+ theme={myTheme}
251
+ handlers={{
252
+ upload: async () => '',
253
+ submit: () => {},
254
+ close: () => {},
255
+ }}
256
+ resolver={{ MyBadge }}
257
+ insert={{ elements: [/* ... */], sections: [] }}
258
+ />
259
+ ```
260
+
261
+ ### 3. Add a toolbar (optional)
262
+
263
+ ```tsx
264
+ // my-badge.toolbar.tsx
265
+ import { useNodeProps } from '@janbox/storefront-builder';
266
+
267
+ export function MyBadgeToolbar() {
268
+ const { nodeProps, setNodeProps } = useNodeProps<MyBadgeProps>();
269
+
270
+ return (
271
+ <div>
272
+ <label>Label</label>
273
+ <input
274
+ value={nodeProps.label}
275
+ onChange={(e) => setNodeProps({ label: e.target.value })}
276
+ />
277
+ </div>
278
+ );
279
+ }
280
+ ```
281
+
282
+ Register the toolbar in `defineNode`:
283
+
284
+ ```ts
285
+ defineNode(MyBadge, {
286
+ resolved: 'MyBadge',
287
+ info: { displayName: 'Badge' },
288
+ related: { inspector: MyBadgeToolbar },
289
+ });
290
+ ```
291
+
292
+ ---
293
+
294
+ ## Hooks
295
+
296
+ All hooks must be called inside a `<Composer>` tree.
297
+
298
+ ### `useComposer(collector?)`
299
+
300
+ Enhanced wrapper around CraftJS `useEditor`. Adds `actions.duplicate()` and `actions.move()`.
301
+
302
+ ```ts
303
+ const { query, actions } = useComposer();
304
+
305
+ // Serialize current canvas state
306
+ const json = query.serialize();
307
+
308
+ // Duplicate a node
309
+ actions.duplicate(nodeId);
310
+
311
+ // Move a node
312
+ actions.move({ nodeId, sourceIndex: 0, destinationIndex: 2 });
313
+ ```
314
+
315
+ ### `useNodeProps<P>(options?)`
316
+
317
+ Read and write props for the current node (or a specific node via `options.nodeId`). Supports responsive updates.
318
+
319
+ ```ts
320
+ const { nodeProps, setNodeProps, setNodeResponsiveProps } = useNodeProps<MyProps>();
321
+
322
+ // Set a flat prop
323
+ setNodeProps({ color: '#ff0000' });
324
+
325
+ // Set a responsive prop for a specific breakpoint
326
+ setNodeResponsiveProps({ fontSize: '18px' }, 'md');
327
+ ```
328
+
329
+ ### `useNode()`
330
+
331
+ Direct access to the CraftJS node context (re-exported from `@craftjs/core`).
332
+
333
+ ```ts
334
+ const { id, connectors, actions } = useNode();
335
+ ```
336
+
337
+ ---
338
+
339
+ ## TailwindCSS Integration
340
+
341
+ The library uses TailwindCSS v4 internally with a `tw:` prefix. Import the stylesheet once at your app entry:
342
+
343
+ ```ts
344
+ import '@janbox/storefront-builder/style.css';
345
+ ```
346
+
347
+ ---
348
+
349
+ ## Theme Shape
350
+
351
+ The `theme` prop on `Editor` follows the `@janbox/storefront-ui` `Theme` type:
352
+
353
+ ```ts
354
+ type Theme = {
355
+ palette: {
356
+ primary: Record<100 | 200 | 300 | 400 | 500 | 600 | 700 | 800, string>;
357
+ secondary: Record<...>;
358
+ red, orange, yellow, green, blue, violet, neutral: Record<...>;
359
+ black: string;
360
+ white: string;
361
+ background: { subtle, default, emphasis: string };
362
+ surface: { default: string };
363
+ border: { default: string };
364
+ inherit: string;
365
+ current: string;
366
+ transparent: string;
367
+ };
368
+ typography: Record<'xs' | 'sm' | 'base' | 'lg' | 'xl' | '2xl' | '3xl' | '4xl' | '5xl' | '6xl', {
369
+ fontSize: string;
370
+ lineHeight: string;
371
+ }>;
372
+ };
373
+ ```
374
+
375
+ ---
376
+
377
+ ## TypeScript
378
+
379
+ The package ships full `.d.ts` declarations. Key types:
380
+
381
+ ```ts
382
+ import type {
383
+ SerializedNodes, // canvas JSON state
384
+ NodeId, // string node identifier
385
+ NodeTree, // tree of nodes
386
+ Resolver, // map of resolvedName → component
387
+ NodeConfig, // config passed to defineNode()
388
+ DisplayOnValue, // responsive visibility: Partial<Record<Screen, boolean>>
389
+ } from '@janbox/storefront-builder';
390
+ ```
391
+
392
+ ---
393
+
394
+ ## Node Reference
395
+
396
+ Chi tiết props, usage và rules cho từng node:
397
+
398
+ ### Layout
399
+
400
+ - [RootNode](dist/lib/root/README.md) — top-level canvas container
401
+ - [BoxNode](dist/lib/box/README.md) — block container
402
+ - [FlexboxNode](dist/lib/flexbox/README.md) — flex container
403
+ - [FlexItemNode](dist/lib/flex-item/README.md) — flex item
404
+ - [GridNode](dist/lib/grid/README.md) — CSS grid container
405
+ - [CellNode](dist/lib/cell/README.md) — grid cell
406
+
407
+ ### Typography
408
+
409
+ - [TextNode](dist/lib/text/README.md) — inline/block text
410
+ - [HeadingNode](dist/lib/heading/README.md) — h1–h6 heading
411
+ - [ParagraphNode](dist/lib/paragraph/README.md) — paragraph
412
+
413
+ ### Media
414
+
415
+ - [ImageNode](dist/lib/image/README.md) — image
416
+ - [VideoNode](dist/lib/video/README.md) — video player
417
+ - [IconNode](dist/lib/icon/README.md) — inline SVG icon
418
+
419
+ ### Navigation
420
+
421
+ - [LinkNode](dist/lib/link/README.md) — anchor link
422
+ - [ButtonNode](dist/lib/button/README.md) — CTA button
423
+
424
+ ### Lists
425
+
426
+ - [UnorderedListNode](dist/lib/unordered-list/README.md) — list container
427
+ - [ListItemNode](dist/lib/list-item/README.md) — list item
428
+
429
+ ### Accordion
430
+
431
+ - [AccordionGroupNode](dist/lib/accordion-group/README.md) — accordion container
432
+ - [AccordionNode](dist/lib/accordion/README.md) — accordion item
433
+ - [AccordionSummaryNode](dist/lib/accordion-summary/README.md) — accordion header
434
+ - [AccordionContentNode](dist/lib/accordion-content/README.md) — accordion content
435
+
436
+ ### Tabs
437
+
438
+ - [TabsNode](dist/lib/tabs/README.md) — tabs container
439
+ - [TabListNode](dist/lib/tab-list/README.md) — tab headers container
440
+ - [TabNode](dist/lib/tab/README.md) — tab header button
441
+ - [TabContentNode](dist/lib/tab-content/README.md) — tab panels container
442
+ - [TabPanelNode](dist/lib/tab-panel/README.md) — tab panel content
443
+
444
+ ### Carousel
445
+
446
+ - [SwiperNode](dist/lib/swiper/README.md) — carousel container
447
+ - [SwiperSlideNode](dist/lib/swiper-slide/README.md) — carousel slide
448
+
449
+ ### Marquee
450
+
451
+ - [MarqueeNode](dist/lib/marquee/README.md) — auto-scrolling ticker
452
+ - [MarqueeItemNode](dist/lib/marquee-item/README.md) — ticker item
453
+
454
+ ### Utilities
455
+
456
+ - [CountdownTimerNode](dist/lib/countdown-timer/README.md) — countdown timer
457
+
458
+ ### Internal
459
+
460
+ - [UnknownNode](dist/lib/unknown/README.md) — fallback for unresolved nodes
461
+
462
+ ---
463
+
464
+ ## Monorepo Development
465
+
466
+ ```bash
467
+ # Install dependencies
468
+ pnpm install
469
+
470
+ # Start demo app (React Router 7)
471
+ pnpm dev
472
+
473
+ # Build the library
474
+ pnpm build
475
+
476
+ # Type check
477
+ pnpm typecheck
478
+ ```
479
+
480
+ Requires Node.js >= 20 and pnpm.
@@ -1,5 +1,17 @@
1
1
  import { Theme } from '@janbox/storefront-ui/theme';
2
+ import { SerializedNodes } from '../../types';
3
+ export type EditorHandlers = {
4
+ close: () => void;
5
+ submit: (abc: SerializedNodes) => void | Promise<void>;
6
+ upload: (file: File) => string | Promise<string>;
7
+ };
2
8
  export type EditorState = {
3
9
  theme: Theme;
10
+ handlers: Readonly<EditorHandlers>;
11
+ };
12
+ type MutableEditorState = Pick<EditorState, 'theme'>;
13
+ type EditorStateValue = EditorState & {
14
+ setState: (partial: Partial<MutableEditorState>) => void;
4
15
  };
5
- export declare const useEditorState: (initialState?: EditorState) => [EditorState, (partial: Partial<EditorState>) => void];
16
+ export declare const useEditor: (initialState?: EditorState) => EditorStateValue;
17
+ export {};
@@ -1,10 +1,13 @@
1
1
  import { Theme } from '@janbox/storefront-ui/theme';
2
2
  import { SidebarProps } from './sidebar';
3
3
  import { Resolver } from '../../types';
4
+ import { EditorHandlers } from '../hooks/use-editor-state';
4
5
  export type EditorProps = {
5
6
  theme: Theme;
6
7
  insert?: SidebarProps['insert'];
7
8
  resolver?: Resolver;
9
+ handlers: EditorHandlers;
8
10
  };
11
+ export type { EditorHandlers } from '../hooks/use-editor-state';
9
12
  export type { InsertElementGroup, InsertSectionGroup } from './sidebar/insert-action';
10
- export declare const Editor: ({ theme, insert, resolver }: EditorProps) => import("@emotion/react/jsx-runtime").JSX.Element;
13
+ export declare const Editor: ({ theme, insert, resolver, handlers }: EditorProps) => import("@emotion/react/jsx-runtime").JSX.Element;
@@ -1 +1 @@
1
- export declare const useDialogContext: () => import('../../floating').WbFloatingContextValue;
1
+ export declare const useDialogContext: () => import('../..').WbFloatingContextValue;
@@ -1 +1 @@
1
- export declare const WbDialogClose: import('react').ForwardRefExoticComponent<Omit<import('../../floating').WbFloatingCloseProps, "ref"> & import('react').RefAttributes<HTMLDivElement>>;
1
+ export declare const WbDialogClose: import('react').ForwardRefExoticComponent<Omit<import('../..').WbFloatingCloseProps, "ref"> & import('react').RefAttributes<HTMLDivElement>>;