astro-tractstack 2.0.30 → 2.0.32
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/index.js +10 -36
- package/package.json +1 -1
- package/templates/src/components/compositor/Compositor.tsx +8 -0
- package/templates/src/components/compositor/nodes/Pane_DesignLibrary.tsx +169 -48
- package/templates/src/components/compositor/nodes/Pane_eraser.tsx +153 -36
- package/templates/src/stores/nodes.ts +10 -0
- package/templates/src/utils/auth.ts +6 -3
- package/utils/inject-files.ts +0 -27
- package/templates/src/components/tenant/RegistrationForm.tsx +0 -449
- package/templates/src/pages/sandbox/activate.astro +0 -258
- package/templates/src/pages/sandbox/register.astro +0 -44
- package/templates/src/pages/sandbox/success.astro +0 -179
- package/templates/src/utils/api/tenantConfig.ts +0 -97
- package/templates/src/utils/api/tenantHelpers.ts +0 -172
package/dist/index.js
CHANGED
|
@@ -10,7 +10,7 @@ function b(t) {
|
|
|
10
10
|
}
|
|
11
11
|
function g(t, e) {
|
|
12
12
|
e.info("TractStack configuration applied"), t.enableMultiTenant && e.info("Multi-tenant mode enabled"), t.includeExamples && e.info("Example components will be included");
|
|
13
|
-
const c = process.env.PUBLIC_GO_BACKEND,
|
|
13
|
+
const c = process.env.PUBLIC_GO_BACKEND, o = process.env.PUBLIC_TENANTID;
|
|
14
14
|
if (!c)
|
|
15
15
|
e.warn("PUBLIC_GO_BACKEND not set - this will be required at runtime");
|
|
16
16
|
else
|
|
@@ -19,11 +19,11 @@ function g(t, e) {
|
|
|
19
19
|
} catch {
|
|
20
20
|
e.error(`PUBLIC_GO_BACKEND is not a valid URL: ${c}`);
|
|
21
21
|
}
|
|
22
|
-
return
|
|
22
|
+
return o ? /^[a-zA-Z0-9_-]+$/.test(o) ? e.info(`Tenant ID validated: ${o}`) : e.error(`PUBLIC_TENANTID contains invalid characters: ${o}`) : e.warn("PUBLIC_TENANTID not set - this will be required at runtime"), t;
|
|
23
23
|
}
|
|
24
24
|
async function w(t, e, c) {
|
|
25
25
|
e.info("TractStack: Injecting template files");
|
|
26
|
-
const
|
|
26
|
+
const o = [
|
|
27
27
|
// Core Configuration
|
|
28
28
|
{
|
|
29
29
|
src: t("../templates/env.example"),
|
|
@@ -2053,38 +2053,12 @@ async function w(t, e, c) {
|
|
|
2053
2053
|
src: t("../templates/socials/youtube.svg"),
|
|
2054
2054
|
dest: "public/socials/youtube.svg"
|
|
2055
2055
|
},
|
|
2056
|
-
// Multi-Tenant Features
|
|
2057
|
-
{
|
|
2058
|
-
src: t("../templates/src/components/tenant/RegistrationForm.tsx"),
|
|
2059
|
-
dest: "src/components/tenant/RegistrationForm.tsx"
|
|
2060
|
-
},
|
|
2061
|
-
{
|
|
2062
|
-
src: t("../templates/src/utils/api/tenantConfig.ts"),
|
|
2063
|
-
dest: "src/utils/api/tenantConfig.ts"
|
|
2064
|
-
},
|
|
2065
|
-
{
|
|
2066
|
-
src: t("../templates/src/utils/api/tenantHelpers.ts"),
|
|
2067
|
-
dest: "src/utils/api/tenantHelpers.ts"
|
|
2068
|
-
},
|
|
2069
2056
|
// Multi-Tenant Features (Conditional)
|
|
2070
2057
|
...c?.enableMultiTenant ? [
|
|
2071
2058
|
// Middleware
|
|
2072
2059
|
{
|
|
2073
2060
|
src: t("../templates/src/middleware.ts"),
|
|
2074
2061
|
dest: "src/middleware.ts"
|
|
2075
|
-
},
|
|
2076
|
-
// Pages
|
|
2077
|
-
{
|
|
2078
|
-
src: t("../templates/src/pages/sandbox/register.astro"),
|
|
2079
|
-
dest: "src/pages/sandbox/register.astro"
|
|
2080
|
-
},
|
|
2081
|
-
{
|
|
2082
|
-
src: t("../templates/src/pages/sandbox/activate.astro"),
|
|
2083
|
-
dest: "src/pages/sandbox/activate.astro"
|
|
2084
|
-
},
|
|
2085
|
-
{
|
|
2086
|
-
src: t("../templates/src/pages/sandbox/success.astro"),
|
|
2087
|
-
dest: "src/pages/sandbox/success.astro"
|
|
2088
2062
|
}
|
|
2089
2063
|
] : [],
|
|
2090
2064
|
// Multi-Tenant Types (Always included due to plan reference)
|
|
@@ -2164,12 +2138,12 @@ async function w(t, e, c) {
|
|
|
2164
2138
|
}
|
|
2165
2139
|
] : []
|
|
2166
2140
|
];
|
|
2167
|
-
for (const s of
|
|
2141
|
+
for (const s of o)
|
|
2168
2142
|
try {
|
|
2169
2143
|
const p = i(s.dest);
|
|
2170
2144
|
n(p) || x(p, { recursive: !0 });
|
|
2171
|
-
const
|
|
2172
|
-
if (!n(s.dest) ||
|
|
2145
|
+
const r = !s.protected && (s.dest === "tailwind.config.cjs" || s.dest.startsWith("src/components/codehooks/") || s.dest.startsWith("src/components/widgets/") || s.dest.startsWith("src/") || s.dest.startsWith("public/client/") || s.dest === ".gitignore");
|
|
2146
|
+
if (!n(s.dest) || r)
|
|
2173
2147
|
if (n(s.src))
|
|
2174
2148
|
k(s.src, s.dest), e.info(`Updated ${s.dest}`);
|
|
2175
2149
|
else {
|
|
@@ -2178,8 +2152,8 @@ async function w(t, e, c) {
|
|
|
2178
2152
|
}
|
|
2179
2153
|
else s.protected ? e.info(`Protected: ${s.dest} (skipped overwrite)`) : e.info(`Skipped existing ${s.dest}`);
|
|
2180
2154
|
} catch (p) {
|
|
2181
|
-
const
|
|
2182
|
-
e.error(`Failed to create ${s.dest}: ${
|
|
2155
|
+
const r = p instanceof Error ? p.message : String(p);
|
|
2156
|
+
e.error(`Failed to create ${s.dest}: ${r}`);
|
|
2183
2157
|
}
|
|
2184
2158
|
}
|
|
2185
2159
|
function _(t) {
|
|
@@ -2198,7 +2172,7 @@ function C(t = {}) {
|
|
|
2198
2172
|
return {
|
|
2199
2173
|
name: "astro-tractstack",
|
|
2200
2174
|
hooks: {
|
|
2201
|
-
"astro:config:setup": async ({ config: c, updateConfig:
|
|
2175
|
+
"astro:config:setup": async ({ config: c, updateConfig: o, logger: s }) => {
|
|
2202
2176
|
g(t, s);
|
|
2203
2177
|
const p = t.enableMultiTenant || !1;
|
|
2204
2178
|
if (s.info(
|
|
@@ -2218,7 +2192,7 @@ function C(t = {}) {
|
|
|
2218
2192
|
), new Error(
|
|
2219
2193
|
"TractStack requires an SSR adapter. Please add @astrojs/node adapter to your astro.config.mjs"
|
|
2220
2194
|
);
|
|
2221
|
-
|
|
2195
|
+
o({
|
|
2222
2196
|
vite: {
|
|
2223
2197
|
define: {
|
|
2224
2198
|
__TRACTSTACK_VERSION__: JSON.stringify("2.0.0-alpha.1"),
|
package/package.json
CHANGED
|
@@ -16,6 +16,8 @@ import {
|
|
|
16
16
|
hasArtpacksStore,
|
|
17
17
|
settingsPanelStore,
|
|
18
18
|
brandConfigStore,
|
|
19
|
+
viewportModeStore,
|
|
20
|
+
setViewportMode,
|
|
19
21
|
} from '@/stores/storykeep';
|
|
20
22
|
import { getCtx, ROOT_NODE_NAME, type NodesContext } from '@/stores/nodes';
|
|
21
23
|
import { stopLoadingAnimation } from '@/utils/helpers';
|
|
@@ -284,6 +286,12 @@ export const Compositor = (props: CompositorProps) => {
|
|
|
284
286
|
selectionOrigin.current = null;
|
|
285
287
|
};
|
|
286
288
|
|
|
289
|
+
useEffect(() => {
|
|
290
|
+
if (viewportModeStore.get() === 'auto') {
|
|
291
|
+
setViewportMode('auto');
|
|
292
|
+
}
|
|
293
|
+
}, []);
|
|
294
|
+
|
|
287
295
|
useEffect(() => {
|
|
288
296
|
fullContentMapStore.set(props.fullContentMap);
|
|
289
297
|
hasAssemblyAIStore.set(props.config?.HAS_AAI || false);
|
|
@@ -9,26 +9,53 @@ import { getCtx } from '@/stores/nodes';
|
|
|
9
9
|
import { RenderChildren } from './RenderChildren';
|
|
10
10
|
import { CodeHookContainer } from './Pane';
|
|
11
11
|
import type { NodeProps } from '@/types/nodeProps';
|
|
12
|
+
import type { BgImageNode, ArtpackImageNode } from '@/types/compositorTypes';
|
|
12
13
|
import { SaveToLibraryModal } from '@/components/edit/state/SaveToLibraryModal';
|
|
13
14
|
import { RestylePaneModal } from '@/components/edit/pane/RestylePaneModal';
|
|
14
15
|
import { selectionStore } from '@/stores/selection';
|
|
15
16
|
import { copyPaneToClipboard } from '@/utils/compositor/designLibraryHelper';
|
|
16
17
|
|
|
18
|
+
function getSizeClasses(
|
|
19
|
+
size: string,
|
|
20
|
+
side: 'image' | 'content',
|
|
21
|
+
viewport: string
|
|
22
|
+
): string {
|
|
23
|
+
if (viewport === 'mobile') {
|
|
24
|
+
return 'w-full';
|
|
25
|
+
}
|
|
26
|
+
switch (size) {
|
|
27
|
+
case 'narrow':
|
|
28
|
+
return side === 'image' ? 'w-1/3' : 'w-2/3';
|
|
29
|
+
case 'wide':
|
|
30
|
+
return side === 'image' ? 'w-2/3' : 'w-1/3';
|
|
31
|
+
default:
|
|
32
|
+
return 'w-1/2';
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
17
36
|
export const Pane_DesignLibrary = (props: NodeProps) => {
|
|
18
37
|
const ctx = getCtx(props);
|
|
19
|
-
|
|
20
38
|
const { isRestyleModalOpen } = useStore(selectionStore, {
|
|
21
39
|
keys: ['isRestyleModalOpen'],
|
|
22
40
|
});
|
|
23
|
-
|
|
24
|
-
const wrapperClasses = `grid ${ctx.getNodeClasses(
|
|
25
|
-
props.nodeId,
|
|
41
|
+
const [currentViewport, setCurrentViewport] = useState(
|
|
26
42
|
viewportKeyStore.get().value
|
|
27
|
-
)
|
|
43
|
+
);
|
|
44
|
+
|
|
45
|
+
useEffect(() => {
|
|
46
|
+
const unsubscribeViewport = viewportKeyStore.subscribe((newViewport) => {
|
|
47
|
+
setCurrentViewport(newViewport.value);
|
|
48
|
+
});
|
|
49
|
+
return () => unsubscribeViewport();
|
|
50
|
+
}, []);
|
|
51
|
+
|
|
52
|
+
const wrapperClasses = `grid ${ctx.getNodeClasses(props.nodeId, currentViewport)}`;
|
|
28
53
|
const contentClasses = 'relative w-full h-auto justify-self-start';
|
|
29
54
|
const contentStyles: CSSProperties = {
|
|
30
55
|
...ctx.getNodeCSSPropertiesStyles(props.nodeId),
|
|
31
56
|
gridArea: '1/1/1/1',
|
|
57
|
+
position: 'relative',
|
|
58
|
+
zIndex: 1,
|
|
32
59
|
};
|
|
33
60
|
const codeHookPayload = ctx.getNodeCodeHookPayload(props.nodeId);
|
|
34
61
|
const [children, setChildren] = useState<string[]>([
|
|
@@ -36,11 +63,14 @@ export const Pane_DesignLibrary = (props: NodeProps) => {
|
|
|
36
63
|
]);
|
|
37
64
|
const [isSaveModalOpen, setIsSaveModalOpen] = useState(false);
|
|
38
65
|
const [wasCopied, setWasCopied] = useState(false);
|
|
66
|
+
const [renderCount, setRenderCount] = useState(0);
|
|
67
|
+
|
|
39
68
|
const getPaneId = (): string => `pane-${props.nodeId}`;
|
|
40
69
|
|
|
41
70
|
useEffect(() => {
|
|
42
71
|
const unsubscribe = ctx.notifications.subscribe(props.nodeId, () => {
|
|
43
72
|
setChildren([...ctx.getChildNodeIDs(props.nodeId)]);
|
|
73
|
+
setRenderCount((prev) => prev + 1);
|
|
44
74
|
});
|
|
45
75
|
return unsubscribe;
|
|
46
76
|
}, [props.nodeId, ctx.notifications]);
|
|
@@ -65,54 +95,145 @@ export const Pane_DesignLibrary = (props: NodeProps) => {
|
|
|
65
95
|
}
|
|
66
96
|
};
|
|
67
97
|
|
|
98
|
+
const Buttons = () => (
|
|
99
|
+
<div className="absolute left-2 top-2 z-10 flex flex-row gap-x-2">
|
|
100
|
+
{!props.isSandboxMode && (
|
|
101
|
+
<button
|
|
102
|
+
title="Save Pane to Design Library"
|
|
103
|
+
onClick={handleSaveClick}
|
|
104
|
+
className="flex h-10 w-10 items-center justify-center rounded-full bg-cyan-600 p-1.5 shadow-lg hover:bg-cyan-700"
|
|
105
|
+
>
|
|
106
|
+
<ArchiveBoxArrowDownIcon className="h-7 w-7 text-white" />
|
|
107
|
+
</button>
|
|
108
|
+
)}
|
|
109
|
+
<button
|
|
110
|
+
title="Restyle Pane from Design Library"
|
|
111
|
+
onClick={handleRestyleClick}
|
|
112
|
+
className="flex h-10 w-10 items-center justify-center rounded-full bg-blue-600 p-1.5 shadow-lg hover:bg-blue-700"
|
|
113
|
+
>
|
|
114
|
+
<ArrowPathRoundedSquareIcon className="h-7 w-7 text-white" />
|
|
115
|
+
</button>
|
|
116
|
+
<button
|
|
117
|
+
title="Copy Pane Design to Clipboard"
|
|
118
|
+
onClick={handleCopyToClipboard}
|
|
119
|
+
className={`flex h-10 w-10 items-center justify-center rounded-full p-1.5 shadow-lg transition-colors ${
|
|
120
|
+
wasCopied ? 'bg-green-500' : 'bg-gray-600 hover:bg-gray-700'
|
|
121
|
+
}`}
|
|
122
|
+
>
|
|
123
|
+
{wasCopied ? (
|
|
124
|
+
<CheckIcon className="h-7 w-7 text-white" />
|
|
125
|
+
) : (
|
|
126
|
+
<ArrowDownTrayIcon className="h-7 w-7 text-white" />
|
|
127
|
+
)}
|
|
128
|
+
</button>
|
|
129
|
+
</div>
|
|
130
|
+
);
|
|
131
|
+
|
|
132
|
+
const allNodes = ctx.allNodes.get();
|
|
133
|
+
const bgNode = children
|
|
134
|
+
.map((id) => allNodes.get(id))
|
|
135
|
+
.find(
|
|
136
|
+
(node) =>
|
|
137
|
+
node?.nodeType === 'BgPane' &&
|
|
138
|
+
'type' in node &&
|
|
139
|
+
(node.type === 'background-image' || node.type === 'artpack-image')
|
|
140
|
+
) as (BgImageNode | ArtpackImageNode) | undefined;
|
|
141
|
+
|
|
142
|
+
const useFlexLayout =
|
|
143
|
+
bgNode &&
|
|
144
|
+
(bgNode.position === 'leftBleed' || bgNode.position === 'rightBleed');
|
|
145
|
+
const deferFlexLayout =
|
|
146
|
+
bgNode && (bgNode.position === 'left' || bgNode.position === 'right');
|
|
147
|
+
|
|
148
|
+
const flexDirection =
|
|
149
|
+
currentViewport === 'mobile'
|
|
150
|
+
? 'flex-col'
|
|
151
|
+
: bgNode?.position === 'rightBleed'
|
|
152
|
+
? 'flex-row-reverse'
|
|
153
|
+
: 'flex-row';
|
|
154
|
+
|
|
68
155
|
return (
|
|
69
156
|
<div id={getPaneId()} className="pane min-h-16">
|
|
70
|
-
<div
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
>
|
|
86
|
-
<ArchiveBoxArrowDownIcon className="h-7 w-7 text-white" />
|
|
87
|
-
</button>
|
|
88
|
-
)}
|
|
89
|
-
<button
|
|
90
|
-
title="Restyle Pane from Design Library"
|
|
91
|
-
onClick={handleRestyleClick}
|
|
92
|
-
className="flex h-10 w-10 items-center justify-center rounded-full bg-blue-600 p-1.5 shadow-lg hover:bg-blue-700"
|
|
157
|
+
<div
|
|
158
|
+
id={ctx.getNodeSlug(props.nodeId)}
|
|
159
|
+
className={useFlexLayout ? '' : wrapperClasses}
|
|
160
|
+
>
|
|
161
|
+
{codeHookPayload ? (
|
|
162
|
+
<div className={contentClasses} style={contentStyles}>
|
|
163
|
+
<Buttons />
|
|
164
|
+
<CodeHookContainer payload={codeHookPayload} />
|
|
165
|
+
</div>
|
|
166
|
+
) : useFlexLayout ? (
|
|
167
|
+
<div
|
|
168
|
+
className={`flex flex-nowrap ${flexDirection} ${ctx.getNodeClasses(props.nodeId, currentViewport)}`}
|
|
169
|
+
>
|
|
170
|
+
<div
|
|
171
|
+
className={`relative overflow-hidden ${getSizeClasses(bgNode.size || 'equal', 'image', currentViewport)}`}
|
|
93
172
|
>
|
|
94
|
-
<
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
173
|
+
<RenderChildren
|
|
174
|
+
children={children.filter((id) => {
|
|
175
|
+
const node = allNodes.get(id);
|
|
176
|
+
return node?.nodeType === 'BgPane';
|
|
177
|
+
})}
|
|
178
|
+
nodeProps={props}
|
|
179
|
+
key={`bg-children-${props.nodeId}-${renderCount}`}
|
|
180
|
+
/>
|
|
181
|
+
</div>
|
|
182
|
+
<div
|
|
183
|
+
className={`${contentClasses} ${getSizeClasses(bgNode.size || 'equal', 'content', currentViewport)}`}
|
|
184
|
+
style={ctx.getNodeCSSPropertiesStyles(props.nodeId)}
|
|
185
|
+
onClick={(e) => {
|
|
186
|
+
ctx.setClickedNodeId(props.nodeId);
|
|
187
|
+
e.stopPropagation();
|
|
188
|
+
}}
|
|
102
189
|
>
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
190
|
+
<Buttons />
|
|
191
|
+
<RenderChildren
|
|
192
|
+
children={children.filter((id) => {
|
|
193
|
+
const node = allNodes.get(id);
|
|
194
|
+
return node?.nodeType !== 'BgPane';
|
|
195
|
+
})}
|
|
196
|
+
nodeProps={props}
|
|
197
|
+
key={`content-children-${props.nodeId}-${renderCount}`}
|
|
198
|
+
/>
|
|
199
|
+
</div>
|
|
109
200
|
</div>
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
201
|
+
) : deferFlexLayout ? (
|
|
202
|
+
<div
|
|
203
|
+
className={contentClasses}
|
|
204
|
+
style={contentStyles}
|
|
205
|
+
onClick={(e) => {
|
|
206
|
+
ctx.setClickedNodeId(props.nodeId);
|
|
207
|
+
e.stopPropagation();
|
|
208
|
+
}}
|
|
209
|
+
>
|
|
210
|
+
<Buttons />
|
|
211
|
+
<RenderChildren
|
|
212
|
+
children={children.filter((id) => {
|
|
213
|
+
const node = allNodes.get(id);
|
|
214
|
+
return node?.nodeType !== 'BgPane';
|
|
215
|
+
})}
|
|
216
|
+
nodeProps={props}
|
|
217
|
+
key={`content-children-${props.nodeId}-${renderCount}`}
|
|
218
|
+
/>
|
|
219
|
+
</div>
|
|
220
|
+
) : (
|
|
221
|
+
<div
|
|
222
|
+
className={contentClasses}
|
|
223
|
+
style={contentStyles}
|
|
224
|
+
onClick={(e) => {
|
|
225
|
+
ctx.setClickedNodeId(props.nodeId);
|
|
226
|
+
e.stopPropagation();
|
|
227
|
+
}}
|
|
228
|
+
>
|
|
229
|
+
<Buttons />
|
|
230
|
+
<RenderChildren
|
|
231
|
+
children={children}
|
|
232
|
+
nodeProps={props}
|
|
233
|
+
key={`render-children-${props.nodeId}-${renderCount}`}
|
|
234
|
+
/>
|
|
235
|
+
</div>
|
|
236
|
+
)}
|
|
116
237
|
</div>
|
|
117
238
|
{isSaveModalOpen && (
|
|
118
239
|
<SaveToLibraryModal
|
|
@@ -5,64 +5,181 @@ import { getCtx } from '@/stores/nodes';
|
|
|
5
5
|
import { RenderChildren } from './RenderChildren';
|
|
6
6
|
import { CodeHookContainer } from './Pane';
|
|
7
7
|
import type { NodeProps } from '@/types/nodeProps';
|
|
8
|
+
import type { BgImageNode, ArtpackImageNode } from '@/types/compositorTypes';
|
|
9
|
+
|
|
10
|
+
function getSizeClasses(
|
|
11
|
+
size: string,
|
|
12
|
+
side: 'image' | 'content',
|
|
13
|
+
viewport: string
|
|
14
|
+
): string {
|
|
15
|
+
if (viewport === 'mobile') {
|
|
16
|
+
return 'w-full';
|
|
17
|
+
}
|
|
18
|
+
switch (size) {
|
|
19
|
+
case 'narrow':
|
|
20
|
+
return side === 'image' ? 'w-1/3' : 'w-2/3';
|
|
21
|
+
case 'wide':
|
|
22
|
+
return side === 'image' ? 'w-2/3' : 'w-1/3';
|
|
23
|
+
default:
|
|
24
|
+
return 'w-1/2';
|
|
25
|
+
}
|
|
26
|
+
}
|
|
8
27
|
|
|
9
28
|
export const PaneEraser = (props: NodeProps) => {
|
|
10
|
-
const
|
|
29
|
+
const ctx = getCtx(props);
|
|
30
|
+
const [currentViewport, setCurrentViewport] = useState(
|
|
31
|
+
viewportKeyStore.get().value
|
|
32
|
+
);
|
|
33
|
+
|
|
34
|
+
useEffect(() => {
|
|
35
|
+
const unsubscribeViewport = viewportKeyStore.subscribe((newViewport) => {
|
|
36
|
+
setCurrentViewport(newViewport.value);
|
|
37
|
+
});
|
|
38
|
+
return () => unsubscribeViewport();
|
|
39
|
+
}, []);
|
|
40
|
+
|
|
41
|
+
const wrapperClasses = `grid ${ctx.getNodeClasses(props.nodeId, currentViewport)}`;
|
|
11
42
|
const contentClasses = 'relative w-full h-auto justify-self-start';
|
|
12
43
|
const contentStyles: CSSProperties = {
|
|
13
|
-
...
|
|
44
|
+
...ctx.getNodeCSSPropertiesStyles(props.nodeId),
|
|
14
45
|
gridArea: '1/1/1/1',
|
|
46
|
+
position: 'relative',
|
|
47
|
+
zIndex: 1,
|
|
15
48
|
};
|
|
16
|
-
const codeHookPayload =
|
|
49
|
+
const codeHookPayload = ctx.getNodeCodeHookPayload(props.nodeId);
|
|
17
50
|
const [children, setChildren] = useState<string[]>([
|
|
18
|
-
...
|
|
51
|
+
...ctx.getChildNodeIDs(props.nodeId),
|
|
19
52
|
]);
|
|
53
|
+
const [renderCount, setRenderCount] = useState(0);
|
|
20
54
|
|
|
21
55
|
const getPaneId = (): string => `pane-${props.nodeId}`;
|
|
22
56
|
|
|
23
57
|
useEffect(() => {
|
|
24
|
-
const unsubscribe =
|
|
25
|
-
props.nodeId
|
|
26
|
-
() =>
|
|
27
|
-
|
|
28
|
-
'notification received data update for pane node: ' + props.nodeId
|
|
29
|
-
);
|
|
30
|
-
setChildren([...getCtx(props).getChildNodeIDs(props.nodeId)]);
|
|
31
|
-
}
|
|
32
|
-
);
|
|
58
|
+
const unsubscribe = ctx.notifications.subscribe(props.nodeId, () => {
|
|
59
|
+
setChildren([...ctx.getChildNodeIDs(props.nodeId)]);
|
|
60
|
+
setRenderCount((prev) => prev + 1);
|
|
61
|
+
});
|
|
33
62
|
return unsubscribe;
|
|
34
|
-
}, []);
|
|
63
|
+
}, [props.nodeId, ctx.notifications]);
|
|
64
|
+
|
|
65
|
+
const DeleteButton = () => (
|
|
66
|
+
<button
|
|
67
|
+
title="Delete Pane"
|
|
68
|
+
onClick={(e) => {
|
|
69
|
+
ctx.setClickedNodeId(props.nodeId);
|
|
70
|
+
e.stopPropagation();
|
|
71
|
+
}}
|
|
72
|
+
className="absolute right-2 top-2 z-10 rounded-full bg-red-700 p-1.5 hover:bg-black"
|
|
73
|
+
>
|
|
74
|
+
<TrashIcon className="h-10 w-10 text-white" />
|
|
75
|
+
</button>
|
|
76
|
+
);
|
|
77
|
+
|
|
78
|
+
const allNodes = ctx.allNodes.get();
|
|
79
|
+
const bgNode = children
|
|
80
|
+
.map((id) => allNodes.get(id))
|
|
81
|
+
.find(
|
|
82
|
+
(node) =>
|
|
83
|
+
node?.nodeType === 'BgPane' &&
|
|
84
|
+
'type' in node &&
|
|
85
|
+
(node.type === 'background-image' || node.type === 'artpack-image')
|
|
86
|
+
) as (BgImageNode | ArtpackImageNode) | undefined;
|
|
87
|
+
|
|
88
|
+
const useFlexLayout =
|
|
89
|
+
bgNode &&
|
|
90
|
+
(bgNode.position === 'leftBleed' || bgNode.position === 'rightBleed');
|
|
91
|
+
const deferFlexLayout =
|
|
92
|
+
bgNode && (bgNode.position === 'left' || bgNode.position === 'right');
|
|
93
|
+
|
|
94
|
+
const flexDirection =
|
|
95
|
+
currentViewport === 'mobile'
|
|
96
|
+
? 'flex-col'
|
|
97
|
+
: bgNode?.position === 'rightBleed'
|
|
98
|
+
? 'flex-row-reverse'
|
|
99
|
+
: 'flex-row';
|
|
35
100
|
|
|
36
101
|
return (
|
|
37
102
|
<div id={getPaneId()} className="pane min-h-16">
|
|
38
103
|
<div
|
|
39
|
-
id={
|
|
40
|
-
className={wrapperClasses}
|
|
104
|
+
id={ctx.getNodeSlug(props.nodeId)}
|
|
105
|
+
className={useFlexLayout ? '' : wrapperClasses}
|
|
41
106
|
>
|
|
42
|
-
|
|
43
|
-
className={contentClasses}
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
107
|
+
{codeHookPayload ? (
|
|
108
|
+
<div className={contentClasses} style={contentStyles}>
|
|
109
|
+
<DeleteButton />
|
|
110
|
+
<CodeHookContainer payload={codeHookPayload} />
|
|
111
|
+
</div>
|
|
112
|
+
) : useFlexLayout ? (
|
|
113
|
+
<div
|
|
114
|
+
className={`flex flex-nowrap ${flexDirection} ${ctx.getNodeClasses(props.nodeId, currentViewport)}`}
|
|
115
|
+
>
|
|
116
|
+
<div
|
|
117
|
+
className={`relative overflow-hidden ${getSizeClasses(bgNode.size || 'equal', 'image', currentViewport)}`}
|
|
118
|
+
>
|
|
119
|
+
<RenderChildren
|
|
120
|
+
children={children.filter((id) => {
|
|
121
|
+
const node = allNodes.get(id);
|
|
122
|
+
return node?.nodeType === 'BgPane';
|
|
123
|
+
})}
|
|
124
|
+
nodeProps={props}
|
|
125
|
+
key={`bg-children-${props.nodeId}-${renderCount}`}
|
|
126
|
+
/>
|
|
127
|
+
</div>
|
|
128
|
+
<div
|
|
129
|
+
className={`${contentClasses} ${getSizeClasses(bgNode.size || 'equal', 'content', currentViewport)}`}
|
|
130
|
+
style={ctx.getNodeCSSPropertiesStyles(props.nodeId)}
|
|
131
|
+
onClick={(e) => {
|
|
132
|
+
ctx.setClickedNodeId(props.nodeId);
|
|
133
|
+
e.stopPropagation();
|
|
134
|
+
}}
|
|
135
|
+
>
|
|
136
|
+
<DeleteButton />
|
|
137
|
+
<RenderChildren
|
|
138
|
+
children={children.filter((id) => {
|
|
139
|
+
const node = allNodes.get(id);
|
|
140
|
+
return node?.nodeType !== 'BgPane';
|
|
141
|
+
})}
|
|
142
|
+
nodeProps={props}
|
|
143
|
+
key={`content-children-${props.nodeId}-${renderCount}`}
|
|
144
|
+
/>
|
|
145
|
+
</div>
|
|
146
|
+
</div>
|
|
147
|
+
) : deferFlexLayout ? (
|
|
148
|
+
<div
|
|
149
|
+
className={contentClasses}
|
|
150
|
+
style={contentStyles}
|
|
52
151
|
onClick={(e) => {
|
|
53
|
-
|
|
152
|
+
ctx.setClickedNodeId(props.nodeId);
|
|
54
153
|
e.stopPropagation();
|
|
55
154
|
}}
|
|
56
|
-
className="absolute right-2 top-2 z-10 rounded-full bg-red-700 p-1.5 hover:bg-black"
|
|
57
155
|
>
|
|
58
|
-
<
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
156
|
+
<DeleteButton />
|
|
157
|
+
<RenderChildren
|
|
158
|
+
children={children.filter((id) => {
|
|
159
|
+
const node = allNodes.get(id);
|
|
160
|
+
return node?.nodeType !== 'BgPane';
|
|
161
|
+
})}
|
|
162
|
+
nodeProps={props}
|
|
163
|
+
key={`content-children-${props.nodeId}-${renderCount}`}
|
|
164
|
+
/>
|
|
165
|
+
</div>
|
|
166
|
+
) : (
|
|
167
|
+
<div
|
|
168
|
+
className={contentClasses}
|
|
169
|
+
style={contentStyles}
|
|
170
|
+
onClick={(e) => {
|
|
171
|
+
ctx.setClickedNodeId(props.nodeId);
|
|
172
|
+
e.stopPropagation();
|
|
173
|
+
}}
|
|
174
|
+
>
|
|
175
|
+
<DeleteButton />
|
|
176
|
+
<RenderChildren
|
|
177
|
+
children={children}
|
|
178
|
+
nodeProps={props}
|
|
179
|
+
key={`render-children-${props.nodeId}-${renderCount}`}
|
|
180
|
+
/>
|
|
181
|
+
</div>
|
|
182
|
+
)}
|
|
66
183
|
</div>
|
|
67
184
|
</div>
|
|
68
185
|
);
|
|
@@ -35,6 +35,7 @@ import type {
|
|
|
35
35
|
MenuNode,
|
|
36
36
|
NodeType,
|
|
37
37
|
PaneFragmentNode,
|
|
38
|
+
BgImageNode,
|
|
38
39
|
PaneNode,
|
|
39
40
|
StoryFragmentNode,
|
|
40
41
|
Tag,
|
|
@@ -3264,6 +3265,15 @@ export class NodesContext {
|
|
|
3264
3265
|
breakMobile: visualBreakPane.breakMobile,
|
|
3265
3266
|
};
|
|
3266
3267
|
allNodes.push(bgPaneNode);
|
|
3268
|
+
} else if (paneTemplate.bgPane.type === 'background-image') {
|
|
3269
|
+
const bgImagePane = paneTemplate.bgPane as BgImageNode;
|
|
3270
|
+
const bgPaneNode: BgImageNode = {
|
|
3271
|
+
...bgImagePane,
|
|
3272
|
+
id: bgPaneId,
|
|
3273
|
+
nodeType: 'BgPane',
|
|
3274
|
+
parentId: newPaneId,
|
|
3275
|
+
};
|
|
3276
|
+
allNodes.push(bgPaneNode);
|
|
3267
3277
|
} else if (paneTemplate.bgPane.type === 'artpack-image') {
|
|
3268
3278
|
const artpackBgPane = paneTemplate.bgPane as ArtpackImageNode;
|
|
3269
3279
|
const bgPaneNode: ArtpackImageNode = {
|
|
@@ -78,7 +78,8 @@ export function getUserRole(astro: any): 'admin' | 'editor' | null {
|
|
|
78
78
|
*/
|
|
79
79
|
export function requireAdmin(astro: any): Response | undefined {
|
|
80
80
|
if (!isAdmin(astro)) {
|
|
81
|
-
|
|
81
|
+
const target = encodeURIComponent(astro.url.pathname + astro.url.search);
|
|
82
|
+
return astro.redirect(`/storykeep/login?redirect=${target}`);
|
|
82
83
|
}
|
|
83
84
|
}
|
|
84
85
|
|
|
@@ -88,7 +89,8 @@ export function requireAdmin(astro: any): Response | undefined {
|
|
|
88
89
|
*/
|
|
89
90
|
export function requireEditor(astro: any): Response | undefined {
|
|
90
91
|
if (!isEditor(astro)) {
|
|
91
|
-
|
|
92
|
+
const target = encodeURIComponent(astro.url.pathname + astro.url.search);
|
|
93
|
+
return astro.redirect(`/storykeep/login?redirect=${target}`);
|
|
92
94
|
}
|
|
93
95
|
}
|
|
94
96
|
|
|
@@ -98,7 +100,8 @@ export function requireEditor(astro: any): Response | undefined {
|
|
|
98
100
|
*/
|
|
99
101
|
export function requireAdminOrEditor(astro: any): Response | undefined {
|
|
100
102
|
if (!isAuthenticated(astro)) {
|
|
101
|
-
|
|
103
|
+
const target = encodeURIComponent(astro.url.pathname + astro.url.search);
|
|
104
|
+
return astro.redirect(`/storykeep/login?redirect=${target}`);
|
|
102
105
|
}
|
|
103
106
|
}
|
|
104
107
|
|