@fayz-ai/ui 0.15.0 → 0.17.0
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/dist/analytics/context.d.ts +15 -1
- package/dist/analytics/context.d.ts.map +1 -1
- package/dist/analytics/index.d.ts +1 -1
- package/dist/analytics/index.d.ts.map +1 -1
- package/dist/{chunk-J7F3Z52K.js → chunk-DFOAKWYB.js} +4 -4
- package/dist/chunk-DFOAKWYB.js.map +1 -0
- package/dist/{chunk-KD2V3TAX.js → chunk-E44WOPCR.js} +61 -49
- package/dist/chunk-E44WOPCR.js.map +1 -0
- package/dist/{chunk-FFJ2C5YH.js → chunk-HKGTF2YA.js} +41 -16
- package/dist/chunk-HKGTF2YA.js.map +1 -0
- package/dist/{chunk-NA3GE4H3.js → chunk-OBIU37AX.js} +365 -252
- package/dist/chunk-OBIU37AX.js.map +1 -0
- package/dist/{chunk-SLWTKHNY.js → chunk-Z2ITFPGL.js} +380 -83
- package/dist/chunk-Z2ITFPGL.js.map +1 -0
- package/dist/{chunk-5KMAEQA3.js → chunk-ZLPYNHXH.js} +56 -21
- package/dist/chunk-ZLPYNHXH.js.map +1 -0
- package/dist/dashboard/OnboardingWidget.d.ts +7 -1
- package/dist/dashboard/OnboardingWidget.d.ts.map +1 -1
- package/dist/dashboard/SidebarOnboarding.d.ts +7 -1
- package/dist/dashboard/SidebarOnboarding.d.ts.map +1 -1
- package/dist/dashboard/index.js +3 -3
- package/dist/hooks/useIsWide.d.ts +16 -0
- package/dist/hooks/useIsWide.d.ts.map +1 -0
- package/dist/index.d.ts +5 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +278 -10
- package/dist/index.js.map +1 -1
- package/dist/layout/AppShell.d.ts +25 -2
- package/dist/layout/AppShell.d.ts.map +1 -1
- package/dist/layout/ModulePage.d.ts +4 -0
- package/dist/layout/ModulePage.d.ts.map +1 -1
- package/dist/layout/SaveBar.d.ts.map +1 -1
- package/dist/layout/Sidebar.d.ts.map +1 -1
- package/dist/layout/Topbar.d.ts +5 -1
- package/dist/layout/Topbar.d.ts.map +1 -1
- package/dist/layout/index.d.ts +1 -1
- package/dist/layout/index.d.ts.map +1 -1
- package/dist/layout/index.js +3 -3
- package/dist/primitives/checkbox.d.ts +4 -0
- package/dist/primitives/checkbox.d.ts.map +1 -1
- package/dist/primitives/index.d.ts +3 -0
- package/dist/primitives/index.d.ts.map +1 -1
- package/dist/primitives/index.js +4 -4
- package/dist/primitives/list-view.d.ts +5 -1
- package/dist/primitives/list-view.d.ts.map +1 -1
- package/dist/primitives/modal.d.ts.map +1 -1
- package/dist/primitives/page-actions.d.ts +55 -0
- package/dist/primitives/page-actions.d.ts.map +1 -0
- package/dist/primitives/page-header.d.ts +5 -2
- package/dist/primitives/page-header.d.ts.map +1 -1
- package/dist/primitives/page-toolbar.d.ts.map +1 -1
- package/dist/primitives/refresh-button.d.ts.map +1 -1
- package/dist/primitives/row-actions.d.ts +11 -0
- package/dist/primitives/row-actions.d.ts.map +1 -0
- package/dist/primitives/whatsapp-icon.d.ts +7 -0
- package/dist/primitives/whatsapp-icon.d.ts.map +1 -0
- package/dist/stores/index.d.ts +1 -1
- package/dist/stores/index.d.ts.map +1 -1
- package/dist/stores/layout.store.d.ts +1 -0
- package/dist/stores/layout.store.d.ts.map +1 -1
- package/dist/stores/page-header.store.d.ts +39 -5
- package/dist/stores/page-header.store.d.ts.map +1 -1
- package/dist/styles.css +188 -3
- package/dist/tour/TourSpotlight.d.ts +29 -0
- package/dist/tour/TourSpotlight.d.ts.map +1 -0
- package/dist/tour/index.d.ts +4 -0
- package/dist/tour/index.d.ts.map +1 -0
- package/dist/tour/tour.store.d.ts +22 -0
- package/dist/tour/tour.store.d.ts.map +1 -0
- package/dist/tour/types.d.ts +28 -0
- package/dist/tour/types.d.ts.map +1 -0
- package/package.json +2 -2
- package/dist/chunk-5KMAEQA3.js.map +0 -1
- package/dist/chunk-FFJ2C5YH.js.map +0 -1
- package/dist/chunk-J7F3Z52K.js.map +0 -1
- package/dist/chunk-KD2V3TAX.js.map +0 -1
- package/dist/chunk-NA3GE4H3.js.map +0 -1
- package/dist/chunk-SLWTKHNY.js.map +0 -1
|
@@ -1,17 +1,22 @@
|
|
|
1
1
|
import * as React from 'react';
|
|
2
|
+
import { createPortal } from 'react-dom';
|
|
2
3
|
import { create } from 'zustand';
|
|
3
4
|
|
|
4
5
|
// src/stores/page-header.store.ts
|
|
5
6
|
var usePageHeaderStore = create((set, get) => ({
|
|
6
|
-
|
|
7
|
+
shellSlot: null,
|
|
8
|
+
setShellSlot: (element) => set({ shellSlot: element }),
|
|
9
|
+
outletSlot: null,
|
|
10
|
+
setOutletSlot: (element) => set({ outletSlot: element }),
|
|
11
|
+
claimed: false,
|
|
7
12
|
owner: null,
|
|
8
13
|
hasBack: false,
|
|
9
14
|
hosts: 0,
|
|
10
15
|
outlets: 0,
|
|
11
16
|
actions: 0,
|
|
12
|
-
claim: (owner,
|
|
17
|
+
claim: (owner, hasBack = false) => set({ owner, claimed: true, hasBack }),
|
|
13
18
|
release: (owner) => {
|
|
14
|
-
if (get().owner === owner) set({ owner: null,
|
|
19
|
+
if (get().owner === owner) set({ owner: null, claimed: false, hasBack: false });
|
|
15
20
|
},
|
|
16
21
|
addHost: () => set((s) => ({ hosts: s.hosts + 1 })),
|
|
17
22
|
removeHost: () => set((s) => ({ hosts: Math.max(0, s.hosts - 1) })),
|
|
@@ -31,8 +36,14 @@ function useRegisterPageActions() {
|
|
|
31
36
|
function useHasPageActions() {
|
|
32
37
|
return usePageHeaderStore((s) => s.actions > 0);
|
|
33
38
|
}
|
|
34
|
-
function
|
|
35
|
-
return usePageHeaderStore((s) => s.
|
|
39
|
+
function usePageHeaderClaimed() {
|
|
40
|
+
return usePageHeaderStore((s) => s.claimed);
|
|
41
|
+
}
|
|
42
|
+
function usePageTitleSlot() {
|
|
43
|
+
return usePageHeaderStore((s) => s.outletSlot ?? s.shellSlot);
|
|
44
|
+
}
|
|
45
|
+
function usePageTitleSlotHost() {
|
|
46
|
+
return usePageHeaderStore((s) => s.setShellSlot);
|
|
36
47
|
}
|
|
37
48
|
function usePageHeaderHost() {
|
|
38
49
|
const addHost = usePageHeaderStore((s) => s.addHost);
|
|
@@ -49,33 +60,41 @@ function useHasPageHeaderOutlet() {
|
|
|
49
60
|
return usePageHeaderStore((s) => s.outlets > 0);
|
|
50
61
|
}
|
|
51
62
|
function PageHeaderOutlet({ className }) {
|
|
52
|
-
const
|
|
63
|
+
const setOutletSlot = usePageHeaderStore((s) => s.setOutletSlot);
|
|
64
|
+
const claimed = usePageHeaderStore((s) => s.claimed);
|
|
53
65
|
const addOutlet = usePageHeaderStore((s) => s.addOutlet);
|
|
54
66
|
const removeOutlet = usePageHeaderStore((s) => s.removeOutlet);
|
|
55
67
|
React.useEffect(() => {
|
|
56
68
|
addOutlet();
|
|
57
69
|
return removeOutlet;
|
|
58
70
|
}, [addOutlet, removeOutlet]);
|
|
59
|
-
|
|
60
|
-
|
|
71
|
+
return React.createElement("div", {
|
|
72
|
+
ref: setOutletSlot,
|
|
73
|
+
className: `${className ?? "mb-4"}${claimed ? "" : " hidden"}`
|
|
74
|
+
});
|
|
61
75
|
}
|
|
62
|
-
function usePageHeader(
|
|
76
|
+
function usePageHeader(active, options) {
|
|
63
77
|
const id = React.useId();
|
|
64
78
|
const claim = usePageHeaderStore((s) => s.claim);
|
|
65
79
|
const release = usePageHeaderStore((s) => s.release);
|
|
66
80
|
const back = options?.back ?? false;
|
|
67
81
|
React.useEffect(() => {
|
|
68
|
-
if (
|
|
82
|
+
if (!active) {
|
|
69
83
|
release(id);
|
|
70
84
|
return;
|
|
71
85
|
}
|
|
72
|
-
claim(id,
|
|
86
|
+
claim(id, back);
|
|
73
87
|
return () => release(id);
|
|
74
|
-
}, [id,
|
|
88
|
+
}, [id, active, back, claim, release]);
|
|
75
89
|
}
|
|
76
90
|
function useHasPageHeaderBack() {
|
|
77
91
|
return usePageHeaderStore((s) => s.hasBack);
|
|
78
92
|
}
|
|
93
|
+
function PageTitlePortal({ children }) {
|
|
94
|
+
const slot = usePageTitleSlot();
|
|
95
|
+
if (!slot) return React.createElement(React.Fragment, null, children);
|
|
96
|
+
return createPortal(children, slot);
|
|
97
|
+
}
|
|
79
98
|
var useLayoutStore = create((set) => ({
|
|
80
99
|
sidebarOpen: true,
|
|
81
100
|
sidebarCollapsed: false,
|
|
@@ -84,9 +103,15 @@ var useLayoutStore = create((set) => ({
|
|
|
84
103
|
toggleSidebar: () => set((state) => ({ sidebarOpen: !state.sidebarOpen })),
|
|
85
104
|
setSidebarCollapsed: (collapsed) => set({ sidebarCollapsed: collapsed }),
|
|
86
105
|
toggleMobileDrawer: () => set((state) => ({ mobileDrawerOpen: !state.mobileDrawerOpen })),
|
|
87
|
-
|
|
106
|
+
setMobileDrawerOpen: (open) => set({ mobileDrawerOpen: open }),
|
|
107
|
+
// Opening search closes the mobile drawer. The palette is a full-screen
|
|
108
|
+
// surface on a phone, and leaving the drawer mounted behind it means the user
|
|
109
|
+
// dismisses the palette onto a menu they had already navigated away from.
|
|
110
|
+
// Handled here so every entry point behaves the same — the rail's search
|
|
111
|
+
// field, the topbar button and the ⌘K shortcut.
|
|
112
|
+
setCommandPaletteOpen: (open) => set(open ? { commandPaletteOpen: true, mobileDrawerOpen: false } : { commandPaletteOpen: false })
|
|
88
113
|
}));
|
|
89
114
|
|
|
90
|
-
export { PageHeaderOutlet, useHasPageActions, useHasPageHeaderBack, useHasPageHeaderHost, useHasPageHeaderOutlet, useLayoutStore, usePageHeader, usePageHeaderHost,
|
|
91
|
-
//# sourceMappingURL=chunk-
|
|
92
|
-
//# sourceMappingURL=chunk-
|
|
115
|
+
export { PageHeaderOutlet, PageTitlePortal, useHasPageActions, useHasPageHeaderBack, useHasPageHeaderHost, useHasPageHeaderOutlet, useLayoutStore, usePageHeader, usePageHeaderClaimed, usePageHeaderHost, usePageTitleSlotHost, useRegisterPageActions };
|
|
116
|
+
//# sourceMappingURL=chunk-HKGTF2YA.js.map
|
|
117
|
+
//# sourceMappingURL=chunk-HKGTF2YA.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/stores/page-header.store.ts","../src/stores/layout.store.ts"],"names":["create"],"mappings":"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","file":"chunk-HKGTF2YA.js","sourcesContent":["import * as React from 'react'\nimport { createPortal } from 'react-dom'\nimport { create } from 'zustand'\n\ninterface PageHeaderState {\n /**\n * Where a page's own header DRAWS, so it can stay in the tree that wrote it.\n *\n * This used to be the node itself, handed over through this store — and a\n * store is not a React tree. The shell rendered it under ITS providers, so a\n * title carrying anything plugin-shaped (a quote's status dropdown) threw\n * \"useStore must be used within CrmPage\" and took the page with it. Same\n * lesson the right rail already learned: the page stays where it was written\n * and portals its body in.\n */\n shellSlot: HTMLElement | null\n setShellSlot: (element: HTMLElement | null) => void\n /** A container deeper than the shell — a module's own content column — that is\n * better placed to draw the header. It wins while it is mounted. */\n outletSlot: HTMLElement | null\n setOutletSlot: (element: HTMLElement | null) => void\n /** A page is drawing its own header, so the shell must not draw one too. */\n claimed: boolean\n /** Identity of whoever owns the slot, so a late unmount can't clear a newer\n * owner's content — the exact race you get navigating form → form. */\n owner: string | null\n /** The claimed header carries a back action, i.e. what the bar is showing is\n * a SUBPAGE (\"← Contas a Pagar / Nova\"), not the module's own screen. The\n * module's tab band reads this to step aside: a form reached from a tab is\n * not one of the tabs, and leaving the strip up marks a place the user is no\n * longer at, one line under a back link that says so. */\n hasBack: boolean\n /** How many shells are currently rendering the slot. A page needs to know:\n * with no host (minimal layout, an embedded form, a modal) its header would\n * be claimed into the void, and the page must draw it inline instead. */\n hosts: number\n /**\n * Quantos OUTLETS internos estão mostrando o slot. Um outlet é um container\n * mais fundo que a shell — a coluna de conteúdo de um módulo com trilha\n * lateral — que fica em melhor posição para desenhar o cabeçalho.\n *\n * Quando existe um, a barra da shell se cala: as duas juntas desenhariam a\n * mesma migalha duas vezes, e a da shell atravessa a largura inteira,\n * empurrando a trilha do módulo para baixo do topo.\n */\n outlets: number\n /**\n * Quantos blocos de ações de página estão montados agora. A barra precisa\n * saber ANTES de decidir se existe: ela some no celular de alguns produtos, e\n * um \"Novo\" entregue a uma barra escondida simplesmente não aparecia.\n */\n actions: number\n claim: (owner: string, hasBack?: boolean) => void\n release: (owner: string) => void\n addHost: () => void\n removeHost: () => void\n addOutlet: () => void\n removeOutlet: () => void\n addActions: () => void\n removeActions: () => void\n}\n\nconst usePageHeaderStore = create<PageHeaderState>((set, get) => ({\n shellSlot: null,\n setShellSlot: (element) => set({ shellSlot: element }),\n outletSlot: null,\n setOutletSlot: (element) => set({ outletSlot: element }),\n claimed: false,\n owner: null,\n hasBack: false,\n hosts: 0,\n outlets: 0,\n actions: 0,\n claim: (owner, hasBack = false) => set({ owner, claimed: true, hasBack }),\n release: (owner) => {\n if (get().owner === owner) set({ owner: null, claimed: false, hasBack: false })\n },\n addHost: () => set((s) => ({ hosts: s.hosts + 1 })),\n removeHost: () => set((s) => ({ hosts: Math.max(0, s.hosts - 1) })),\n addOutlet: () => set((s) => ({ outlets: s.outlets + 1 })),\n removeOutlet: () => set((s) => ({ outlets: Math.max(0, s.outlets - 1) })),\n addActions: () => set((s) => ({ actions: s.actions + 1 })),\n removeActions: () => set((s) => ({ actions: Math.max(0, s.actions - 1) })),\n}))\n\n/** Declara que esta página tem ações no topo. Chamado por PageHeaderActions. */\nexport function useRegisterPageActions(): void {\n const add = usePageHeaderStore((s) => s.addActions)\n const remove = usePageHeaderStore((s) => s.removeActions)\n React.useEffect(() => {\n add()\n return remove\n }, [add, remove])\n}\n\n/** Se alguma página tem ações no topo agora. A barra usa para não sumir com\n * elas no celular, e para não existir vazia. */\nexport function useHasPageActions(): boolean {\n return usePageHeaderStore((s) => s.actions > 0)\n}\n\n/** Read the current override. The shell calls this; pages should not. */\n/** True while a page is drawing its own header into the bar's slot. */\nexport function usePageHeaderClaimed(): boolean {\n return usePageHeaderStore((s) => s.claimed)\n}\n\n/** The bar's portal target, for the page that draws into it. */\nexport function usePageTitleSlot(): HTMLElement | null {\n return usePageHeaderStore((s) => s.outletSlot ?? s.shellSlot)\n}\n\n/** Registers the bar's portal target. Called by the shell. */\nexport function usePageTitleSlotHost(): (element: HTMLElement | null) => void {\n return usePageHeaderStore((s) => s.setShellSlot)\n}\n\n/**\n * Declare that this shell renders the page-header slot. Called by the layout,\n * never by a page. Without it `PageHeader` cannot tell \"nobody claimed the bar\"\n * from \"there is no bar\", and a header would vanish instead of falling back.\n */\nexport function usePageHeaderHost(): void {\n const addHost = usePageHeaderStore((s) => s.addHost)\n const removeHost = usePageHeaderStore((s) => s.removeHost)\n React.useEffect(() => {\n addHost()\n return removeHost\n }, [addHost, removeHost])\n}\n\n/** Whether any shell OR inner outlet is showing the slot right now. */\nexport function useHasPageHeaderHost(): boolean {\n return usePageHeaderStore((s) => s.hosts > 0 || s.outlets > 0)\n}\n\n/** True when a deeper container took over drawing the header. The shell bar\n * uses this to stand down. */\nexport function useHasPageHeaderOutlet(): boolean {\n return usePageHeaderStore((s) => s.outlets > 0)\n}\n\n/**\n * Desenha o cabeçalho AQUI em vez de na barra da shell.\n *\n * Para quem tem uma coluna própria de conteúdo — um módulo com trilha lateral —,\n * onde a barra da shell, larga demais, passaria por cima da trilha.\n */\nexport function PageHeaderOutlet({ className }: { className?: string }): React.ReactElement | null {\n const setOutletSlot = usePageHeaderStore((s) => s.setOutletSlot)\n const claimed = usePageHeaderStore((s) => s.claimed)\n const addOutlet = usePageHeaderStore((s) => s.addOutlet)\n const removeOutlet = usePageHeaderStore((s) => s.removeOutlet)\n\n React.useEffect(() => {\n addOutlet()\n return removeOutlet\n }, [addOutlet, removeOutlet])\n\n // A target, not a renderer: the page portals its own header in here, keeping\n // the tree that wrote it. `empty:hidden` keeps the margin off an unused slot.\n return React.createElement('div', {\n ref: setOutletSlot,\n className: `${className ?? 'mb-4'}${claimed ? '' : ' hidden'}`,\n })\n}\n\n/**\n * Put this page's own header — a back link, a breadcrumb — into the shell's top\n * bar, replacing the page title for as long as the page is mounted.\n *\n * Prefer the `PageHeader` component: it is this hook plus the one agreed layout\n * (← + title), so every screen in every app reads the same. Reach for the hook\n * directly only for a header that genuinely is not \"back + title\".\n *\n * A form or detail screen already says where it is (\"← Novo produto\") and the\n * bar above it was saying \"Produtos\" again, one line up. Two headers stacked is\n * a whole row of vertical space spent repeating a word.\n *\n * The node is re-claimed on every change so a breadcrumb whose label depends on\n * loaded data (a record's name) updates once it arrives.\n */\n/**\n * Tells the bar that this page is drawing its own header, and whether that\n * header carries a back action. The header ITSELF goes through `PageTitlePortal`\n * — flags travel as data, JSX stays in the tree that wrote it.\n */\nexport function usePageHeader(active: boolean, options?: { back?: boolean }): void {\n const id = React.useId()\n const claim = usePageHeaderStore((s) => s.claim)\n const release = usePageHeaderStore((s) => s.release)\n const back = options?.back ?? false\n\n React.useEffect(() => {\n if (!active) { release(id); return }\n claim(id, back)\n return () => release(id)\n }, [id, active, back, claim, release])\n}\n\n/** True while the bar is showing a subpage's \"← parent / title\". See\n * `PageHeaderState.hasBack`. */\nexport function useHasPageHeaderBack(): boolean {\n return usePageHeaderStore((s) => s.hasBack)\n}\n\nexport { usePageHeaderStore }\n\n/**\n * Draws a page's own header into the bar WITHOUT leaving its React tree, so a\n * title made of plugin components still has its plugin's providers above it.\n * With nowhere to draw into it renders where it stands, as any fallback.\n */\nexport function PageTitlePortal({ children }: { children: React.ReactNode }): React.ReactElement | null {\n const slot = usePageTitleSlot()\n if (!slot) return React.createElement(React.Fragment, null, children)\n return createPortal(children, slot)\n}\n","import { create } from 'zustand'\n\ninterface LayoutState {\n sidebarOpen: boolean\n sidebarCollapsed: boolean\n mobileDrawerOpen: boolean\n commandPaletteOpen: boolean\n setMobileDrawerOpen: (open: boolean) => void\n toggleSidebar: () => void\n setSidebarCollapsed: (collapsed: boolean) => void\n toggleMobileDrawer: () => void\n setCommandPaletteOpen: (open: boolean) => void\n}\n\nexport const useLayoutStore = create<LayoutState>((set) => ({\n sidebarOpen: true,\n sidebarCollapsed: false,\n mobileDrawerOpen: false,\n commandPaletteOpen: false,\n\n toggleSidebar: () =>\n set((state) => ({ sidebarOpen: !state.sidebarOpen })),\n\n setSidebarCollapsed: (collapsed) =>\n set({ sidebarCollapsed: collapsed }),\n\n toggleMobileDrawer: () =>\n set((state) => ({ mobileDrawerOpen: !state.mobileDrawerOpen })),\n\n setMobileDrawerOpen: (open) => set({ mobileDrawerOpen: open }),\n\n // Opening search closes the mobile drawer. The palette is a full-screen\n // surface on a phone, and leaving the drawer mounted behind it means the user\n // dismisses the palette onto a menu they had already navigated away from.\n // Handled here so every entry point behaves the same — the rail's search\n // field, the topbar button and the ⌘K shortcut.\n setCommandPaletteOpen: (open) =>\n set(open ? { commandPaletteOpen: true, mobileDrawerOpen: false } : { commandPaletteOpen: false }),\n}))\n"]}
|