@aglyn/plugins-ai 1.0.0-beta.291 → 1.0.0-beta.293
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/package.json +17 -17
- package/src/lib/components/ai-site-import-job.component.js +5 -4
- package/src/lib/components/ai-site-import-job.component.js.map +1 -1
- package/src/lib/components/ai-site-import-start.component.js +3 -3
- package/src/lib/components/ai-site-import-start.component.js.map +1 -1
- package/src/lib/components/ai-site-start-card.component.d.ts +1 -1
- package/src/lib/components/ai-site-start-card.component.js +129 -29
- package/src/lib/components/ai-site-start-card.component.js.map +1 -1
- package/src/lib/components/ai-site-start-steps.component.d.ts +15 -3
- package/src/lib/components/ai-site-start-steps.component.js +42 -25
- package/src/lib/components/ai-site-start-steps.component.js.map +1 -1
- package/src/lib/components/ai-site-style-previews.d.ts +17 -0
- package/src/lib/components/ai-site-style-previews.js +26 -95
- package/src/lib/components/ai-site-style-previews.js.map +1 -1
- package/src/lib/declarations.server.js +22 -0
- package/src/lib/declarations.server.js.map +1 -1
- package/src/lib/jobs/ai-job-import-step.d.ts +21 -2
- package/src/lib/jobs/ai-job-import-step.js +179 -49
- package/src/lib/jobs/ai-job-import-step.js.map +1 -1
- package/src/lib/jobs/ai-job-layout-language.d.ts +27 -3
- package/src/lib/jobs/ai-job-layout-language.js +156 -113
- package/src/lib/jobs/ai-job-layout-language.js.map +1 -1
- package/src/lib/jobs/ai-job-layout-step.js +56 -17
- package/src/lib/jobs/ai-job-layout-step.js.map +1 -1
- package/src/lib/jobs/ai-job-page-language.d.ts +25 -15
- package/src/lib/jobs/ai-job-page-language.js +120 -66
- package/src/lib/jobs/ai-job-page-language.js.map +1 -1
- package/src/lib/jobs/ai-job-page-step.js +27 -7
- package/src/lib/jobs/ai-job-page-step.js.map +1 -1
- package/src/lib/jobs/ai-job-site-datasets.d.ts +12 -4
- package/src/lib/jobs/ai-job-site-datasets.js +36 -16
- package/src/lib/jobs/ai-job-site-datasets.js.map +1 -1
- package/src/lib/jobs/ai-jobs.js +6 -2
- package/src/lib/jobs/ai-jobs.js.map +1 -1
- package/src/lib/jobs/ai-layout-stock-photos.d.ts +50 -1
- package/src/lib/jobs/ai-layout-stock-photos.js +586 -35
- package/src/lib/jobs/ai-layout-stock-photos.js.map +1 -1
- package/src/lib/jobs/ai-site-import-compile.d.ts +147 -0
- package/src/lib/jobs/ai-site-import-compile.js +1143 -0
- package/src/lib/jobs/ai-site-import-compile.js.map +1 -0
- package/src/lib/jobs/ai-site-import-document.d.ts +9 -36
- package/src/lib/jobs/ai-site-import-document.js +23 -489
- package/src/lib/jobs/ai-site-import-document.js.map +1 -1
- package/src/lib/jobs/ai-site-import-look.d.ts +54 -7
- package/src/lib/jobs/ai-site-import-look.js +163 -52
- package/src/lib/jobs/ai-site-import-look.js.map +1 -1
- package/src/lib/jobs/ai-site-import-publish.d.ts +7 -1
- package/src/lib/jobs/ai-site-import-publish.js +19 -5
- package/src/lib/jobs/ai-site-import-publish.js.map +1 -1
- package/src/lib/jobs/ai-site-late-photos.js +4 -2
- package/src/lib/jobs/ai-site-late-photos.js.map +1 -1
- package/src/lib/jobs/ai-site-publish.d.ts +10 -1
- package/src/lib/jobs/ai-site-publish.js +8 -3
- package/src/lib/jobs/ai-site-publish.js.map +1 -1
- package/src/lib/jobs/fixtures/ai-stock-photo-agl4092-1029-recordings.d.ts +23 -0
- package/src/lib/jobs/fixtures/ai-stock-photo-agl4092-1029-recordings.js +677 -0
- package/src/lib/jobs/fixtures/ai-stock-photo-agl4092-1029-recordings.js.map +1 -0
- package/src/lib/layout-language/ai-layout-compiler.d.ts +26 -0
- package/src/lib/layout-language/ai-layout-compiler.js +677 -33
- package/src/lib/layout-language/ai-layout-compiler.js.map +1 -1
- package/src/lib/layout-language/ai-layout-frame-hollows.js +5 -1
- package/src/lib/layout-language/ai-layout-frame-hollows.js.map +1 -1
- package/src/lib/layout-language/ai-layout-frame-source.d.ts +196 -0
- package/src/lib/layout-language/ai-layout-frame-source.js +726 -0
- package/src/lib/layout-language/ai-layout-frame-source.js.map +1 -0
- package/src/lib/layout-language/ai-layout-frame.d.ts +22 -0
- package/src/lib/layout-language/ai-layout-frame.js +121 -29
- package/src/lib/layout-language/ai-layout-frame.js.map +1 -1
- package/src/lib/layout-language/ai-layout-language.d.ts +103 -37
- package/src/lib/layout-language/ai-layout-language.js +85 -120
- package/src/lib/layout-language/ai-layout-language.js.map +1 -1
- package/src/lib/model/ai-credit-estimate.d.ts +11 -7
- package/src/lib/model/ai-credit-estimate.js +11 -7
- package/src/lib/model/ai-credit-estimate.js.map +1 -1
- package/src/lib/model/ai-site-import.d.ts +307 -13
- package/src/lib/model/ai-site-import.js +227 -19
- package/src/lib/model/ai-site-import.js.map +1 -1
- package/src/lib/model/ai-site-look.d.ts +65 -1
- package/src/lib/model/ai-site-look.js +141 -23
- package/src/lib/model/ai-site-look.js.map +1 -1
- package/src/lib/model/ai-site-photos.d.ts +2 -0
- package/src/lib/model/ai-site-photos.js.map +1 -1
- package/src/lib/model/ai-site-start-steps.d.ts +4 -3
- package/src/lib/model/ai-site-start-steps.js +11 -3
- package/src/lib/model/ai-site-start-steps.js.map +1 -1
- package/src/lib/model/ai-site-start.d.ts +23 -2
- package/src/lib/model/ai-site-start.js +25 -3
- package/src/lib/model/ai-site-start.js.map +1 -1
- package/src/lib/providers/routing.d.ts +11 -0
- package/src/lib/providers/routing.js +23 -0
- package/src/lib/providers/routing.js.map +1 -1
- package/src/lib/runtime/ai-doctrine-validators.d.ts +4 -2
- package/src/lib/runtime/ai-doctrine-validators.js +7 -2
- package/src/lib/runtime/ai-doctrine-validators.js.map +1 -1
- package/src/lib/runtime/ai-icon-library.d.ts +4 -0
- package/src/lib/runtime/ai-icon-library.js +4 -0
- package/src/lib/runtime/ai-icon-library.js.map +1 -1
- package/src/lib/runtime/ai-node-tree.js +5 -1
- package/src/lib/runtime/ai-node-tree.js.map +1 -1
- package/src/lib/runtime/ai-palette.d.ts +2 -0
- package/src/lib/runtime/ai-palette.generated.js +19 -3
- package/src/lib/runtime/ai-palette.generated.js.map +1 -1
- package/src/lib/runtime/ai-palette.js +2 -1
- package/src/lib/runtime/ai-palette.js.map +1 -1
- package/src/lib/site-import/fixtures/site-layout-recordings.d.ts +50 -0
- package/src/lib/site-import/fixtures/site-layout-recordings.js +86 -0
- package/src/lib/site-import/fixtures/site-layout-recordings.js.map +1 -0
- package/src/lib/site-import/site-import-crawl.d.ts +18 -1
- package/src/lib/site-import/site-import-crawl.js +70 -10
- package/src/lib/site-import/site-import-crawl.js.map +1 -1
- package/src/lib/site-import/site-import-css.d.ts +110 -0
- package/src/lib/site-import/site-import-css.js +826 -0
- package/src/lib/site-import/site-import-css.js.map +1 -0
- package/src/lib/site-import/site-import-html.d.ts +116 -1
- package/src/lib/site-import/site-import-html.js +538 -46
- package/src/lib/site-import/site-import-html.js.map +1 -1
- package/src/lib/site-import/site-import-layout.d.ts +79 -0
- package/src/lib/site-import/site-import-layout.js +1608 -0
- package/src/lib/site-import/site-import-layout.js.map +1 -0
- package/src/lib/site-import/site-import-nav.d.ts +40 -0
- package/src/lib/site-import/site-import-nav.js +437 -0
- package/src/lib/site-import/site-import-nav.js.map +1 -0
- package/src/lib/site-import/site-import-page.d.ts +8 -1
- package/src/lib/site-import/site-import-page.js +24 -2
- package/src/lib/site-import/site-import-page.js.map +1 -1
- package/src/lib/assets/site-styles/beauty.webp +0 -0
- package/src/lib/assets/site-styles/blog.webp +0 -0
- package/src/lib/assets/site-styles/business.webp +0 -0
- package/src/lib/assets/site-styles/coming-soon.webp +0 -0
- package/src/lib/assets/site-styles/education.webp +0 -0
- package/src/lib/assets/site-styles/events.webp +0 -0
- package/src/lib/assets/site-styles/fitness.webp +0 -0
- package/src/lib/assets/site-styles/landing.webp +0 -0
- package/src/lib/assets/site-styles/music.webp +0 -0
- package/src/lib/assets/site-styles/nonprofit.webp +0 -0
- package/src/lib/assets/site-styles/personal.webp +0 -0
- package/src/lib/assets/site-styles/photography.webp +0 -0
- package/src/lib/assets/site-styles/portfolio.webp +0 -0
- package/src/lib/assets/site-styles/professional.webp +0 -0
- package/src/lib/assets/site-styles/realestate.webp +0 -0
- package/src/lib/assets/site-styles/restaurant.webp +0 -0
- package/src/lib/assets/site-styles/store.webp +0 -0
- package/src/lib/assets/site-styles/studio.webp +0 -0
- package/src/lib/assets/site-styles/trades.webp +0 -0
- package/src/lib/assets/site-styles/wellness.webp +0 -0
- package/src/lib/assets/site-styles/yoga.webp +0 -0
- package/src/lib/site-import/site-import-review.d.ts +0 -40
- package/src/lib/site-import/site-import-review.js +0 -149
- package/src/lib/site-import/site-import-review.js.map +0 -1
|
@@ -0,0 +1,726 @@
|
|
|
1
|
+
import { _ as _extends } from "@swc/helpers/_/_extends";
|
|
2
|
+
/**
|
|
3
|
+
* @license
|
|
4
|
+
* Copyright 2026 Aglyn LLC
|
|
5
|
+
*
|
|
6
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
7
|
+
* you may not use this file except in compliance with the License.
|
|
8
|
+
* You may obtain a copy of the License at
|
|
9
|
+
*
|
|
10
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
11
|
+
*
|
|
12
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
13
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
14
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
15
|
+
* See the License for the specific language governing permissions and
|
|
16
|
+
* limitations under the License.
|
|
17
|
+
*/ /*==========================================
|
|
18
|
+
* A HEADER AND FOOTER BROUGHT IN AS THE SOURCE BUILDS THEM (AGL-4108).
|
|
19
|
+
*
|
|
20
|
+
* On 10/29 an imported header was the wave's page titles, seven of them cut
|
|
21
|
+
* to 28 characters, wrapping onto two and three lines from 900 px up, with
|
|
22
|
+
* no menus, its logo over "Home" at 1280 and its "Start free" sent to the
|
|
23
|
+
* Forms page. The source's own header was five items, two of them menus,
|
|
24
|
+
* and two buttons that leave for the app.
|
|
25
|
+
*
|
|
26
|
+
* So a site brought in from elsewhere is given its source's header and
|
|
27
|
+
* footer as the reader found them (`site-import-nav.ts`), and they are drawn
|
|
28
|
+
* here by code, with no model:
|
|
29
|
+
*
|
|
30
|
+
* - **The menu.** Each top-level item as the source has it: a link, or a
|
|
31
|
+
* menu of its entries — the Besigner's Mega Menu where its entries carry a
|
|
32
|
+
* line under them or sit in headed columns, its Dropdown Menu otherwise.
|
|
33
|
+
* Every label on one line. The row shows from the screen width it fits at
|
|
34
|
+
* (`aiLayoutNavBreakpoint`), and below it the phone menu's button.
|
|
35
|
+
* - **The buttons.** The source's own, with their own addresses: an address
|
|
36
|
+
* off the site stays off it, as the source sends it. Drawn as the source
|
|
37
|
+
* draws them: filled, outlined or as text.
|
|
38
|
+
* - **The phone menu.** Each menu as a heading over its entries, then the
|
|
39
|
+
* links, then the buttons: every part of the menu as present as the rest.
|
|
40
|
+
* - **The footer.** The source's columns under their headings, its words,
|
|
41
|
+
* its profiles, its address, phone and email, and its © line word for word.
|
|
42
|
+
*
|
|
43
|
+
* Every word is the source's, as written; nothing here is a model's.
|
|
44
|
+
*=========================================*/ import { aiIsIconPathData } from "../runtime/ai-icon-library.js";
|
|
45
|
+
import { isContactHref, isHttpsUrl, isRootRelativePath } from "../runtime/ai-node-tree.js";
|
|
46
|
+
/** The most kept of each part: the reader's own limits, held again on the way in. */ export const AI_LAYOUT_FRAME_SOURCE_LIMITS = {
|
|
47
|
+
groups: 12,
|
|
48
|
+
entries: 40,
|
|
49
|
+
ctas: 4,
|
|
50
|
+
columns: 12,
|
|
51
|
+
links: 60,
|
|
52
|
+
text: 12,
|
|
53
|
+
social: 20,
|
|
54
|
+
label: 120,
|
|
55
|
+
line: 300
|
|
56
|
+
};
|
|
57
|
+
const SCREEN_ID = /^[A-Za-z0-9_-]{1,64}$/;
|
|
58
|
+
const STYLES = [
|
|
59
|
+
'primary',
|
|
60
|
+
'secondary',
|
|
61
|
+
'quiet'
|
|
62
|
+
];
|
|
63
|
+
const GROUNDS = [
|
|
64
|
+
'page',
|
|
65
|
+
'paper',
|
|
66
|
+
'brand',
|
|
67
|
+
'dark'
|
|
68
|
+
];
|
|
69
|
+
/** Whether a destination is one a link of the frame may carry. */ export function aiLayoutFrameSourceToAllowed(to) {
|
|
70
|
+
if (typeof to !== 'string' || !to || to.length > 2048) return false;
|
|
71
|
+
if (to.startsWith('page:')) return SCREEN_ID.test(to.slice(5));
|
|
72
|
+
return isHttpsUrl(to) || isRootRelativePath(to) || isContactHref(to);
|
|
73
|
+
}
|
|
74
|
+
const isRecord = (value)=>!!value && typeof value === 'object' && !Array.isArray(value);
|
|
75
|
+
const words = (value, max)=>{
|
|
76
|
+
const line = typeof value === 'string' ? value.replace(/\s+/g, ' ').trim() : '';
|
|
77
|
+
return line.length > max ? `${line.slice(0, max - 1).trimEnd()}…` : line;
|
|
78
|
+
};
|
|
79
|
+
const listOf = (value)=>Array.isArray(value) ? value : [];
|
|
80
|
+
function linkOf(raw) {
|
|
81
|
+
if (!isRecord(raw)) return null;
|
|
82
|
+
const label = words(raw['label'], AI_LAYOUT_FRAME_SOURCE_LIMITS.label);
|
|
83
|
+
return label && aiLayoutFrameSourceToAllowed(raw['to']) ? {
|
|
84
|
+
label,
|
|
85
|
+
to: raw['to']
|
|
86
|
+
} : null;
|
|
87
|
+
}
|
|
88
|
+
const present = (value)=>value !== null;
|
|
89
|
+
/**
|
|
90
|
+
* A source frame as a job carries it, read defensively: every label cut to
|
|
91
|
+
* its limit, every destination one a link may carry, every icon one path.
|
|
92
|
+
* `null` where it holds no menu item and no button to draw.
|
|
93
|
+
*/ export function aiLayoutFrameSourceOf(raw) {
|
|
94
|
+
if (!isRecord(raw)) return null;
|
|
95
|
+
const limits = AI_LAYOUT_FRAME_SOURCE_LIMITS;
|
|
96
|
+
const nav = listOf(raw['nav']).slice(0, limits.groups).map((group)=>{
|
|
97
|
+
if (!isRecord(group)) return null;
|
|
98
|
+
const label = words(group['label'], limits.label);
|
|
99
|
+
if (!label) return null;
|
|
100
|
+
const items = listOf(group['items']).slice(0, limits.entries).map((entry)=>{
|
|
101
|
+
const link = linkOf(entry);
|
|
102
|
+
if (!link || !isRecord(entry)) return null;
|
|
103
|
+
const text = words(entry['text'], limits.line);
|
|
104
|
+
const column = words(entry['column'], limits.label);
|
|
105
|
+
return _extends({}, link, text ? {
|
|
106
|
+
text
|
|
107
|
+
} : {}, aiIsIconPathData(entry['iconPath']) ? {
|
|
108
|
+
iconPath: entry['iconPath']
|
|
109
|
+
} : {}, column ? {
|
|
110
|
+
column
|
|
111
|
+
} : {});
|
|
112
|
+
}).filter(present);
|
|
113
|
+
const to = aiLayoutFrameSourceToAllowed(group['to']) ? group['to'] : null;
|
|
114
|
+
// A name that opens nothing and goes nowhere is no item.
|
|
115
|
+
return items.length || to ? {
|
|
116
|
+
label,
|
|
117
|
+
to,
|
|
118
|
+
items
|
|
119
|
+
} : null;
|
|
120
|
+
}).filter(present);
|
|
121
|
+
const ctas = listOf(raw['ctas']).slice(0, limits.ctas).map((cta)=>{
|
|
122
|
+
const link = linkOf(cta);
|
|
123
|
+
if (!link || !isRecord(cta)) return null;
|
|
124
|
+
const style = STYLES.includes(cta['style']) ? cta['style'] : 'primary';
|
|
125
|
+
return _extends({}, link, {
|
|
126
|
+
style
|
|
127
|
+
});
|
|
128
|
+
}).filter(present);
|
|
129
|
+
if (!nav.length && !ctas.length) return null;
|
|
130
|
+
const header = GROUNDS.includes(raw['header']) ? raw['header'] : 'paper';
|
|
131
|
+
return {
|
|
132
|
+
nav,
|
|
133
|
+
ctas,
|
|
134
|
+
header,
|
|
135
|
+
footer: footerOf(raw['footer'])
|
|
136
|
+
};
|
|
137
|
+
}
|
|
138
|
+
function footerOf(raw) {
|
|
139
|
+
if (!isRecord(raw)) return null;
|
|
140
|
+
const limits = AI_LAYOUT_FRAME_SOURCE_LIMITS;
|
|
141
|
+
const columns = listOf(raw['columns']).slice(0, limits.columns).map((column)=>{
|
|
142
|
+
if (!isRecord(column)) return null;
|
|
143
|
+
const heading = words(column['heading'], limits.label);
|
|
144
|
+
const links = listOf(column['links']).slice(0, limits.links).map(linkOf).filter(present);
|
|
145
|
+
return heading && links.length ? {
|
|
146
|
+
heading,
|
|
147
|
+
links
|
|
148
|
+
} : null;
|
|
149
|
+
}).filter(present);
|
|
150
|
+
const links = listOf(raw['links']).slice(0, limits.links).map(linkOf).filter(present);
|
|
151
|
+
const text = listOf(raw['text']).slice(0, limits.text).map((line)=>words(line, limits.line)).filter(Boolean);
|
|
152
|
+
const social = listOf(raw['social']).slice(0, limits.social).map((entry)=>{
|
|
153
|
+
const link = linkOf(entry);
|
|
154
|
+
const network = isRecord(entry) ? words(entry['network'], 40) : '';
|
|
155
|
+
return link && network && isHttpsUrl(link.to) ? _extends({}, link, {
|
|
156
|
+
network
|
|
157
|
+
}) : null;
|
|
158
|
+
}).filter(present);
|
|
159
|
+
const fact = (key)=>words(raw[key], limits.line) || null;
|
|
160
|
+
const footer = {
|
|
161
|
+
columns,
|
|
162
|
+
links,
|
|
163
|
+
text,
|
|
164
|
+
social,
|
|
165
|
+
address: fact('address'),
|
|
166
|
+
phone: fact('phone'),
|
|
167
|
+
email: fact('email'),
|
|
168
|
+
copyright: fact('copyright')
|
|
169
|
+
};
|
|
170
|
+
const empty = !columns.length && !links.length && !text.length && !social.length && !footer.address && !footer.phone && !footer.email && !footer.copyright;
|
|
171
|
+
return empty ? null : footer;
|
|
172
|
+
}
|
|
173
|
+
/** The pages a source frame links by id, each once. */ export function aiLayoutFrameSourcePageIds(source) {
|
|
174
|
+
const all = [
|
|
175
|
+
...source.nav.flatMap((group)=>[
|
|
176
|
+
group.to,
|
|
177
|
+
...group.items.map((entry)=>entry.to)
|
|
178
|
+
]),
|
|
179
|
+
...source.ctas.map((cta)=>cta.to),
|
|
180
|
+
...source.footer ? [
|
|
181
|
+
...source.footer.columns.flatMap((column)=>column.links.map((entry)=>entry.to)),
|
|
182
|
+
...source.footer.links.map((entry)=>entry.to)
|
|
183
|
+
] : []
|
|
184
|
+
];
|
|
185
|
+
return [
|
|
186
|
+
...new Set(all.filter((to)=>typeof to === 'string' && to.startsWith('page:')).map((to)=>to.slice('page:'.length)))
|
|
187
|
+
];
|
|
188
|
+
}
|
|
189
|
+
/** A link's props for where it goes: a page by its id, anywhere else by its address. */ export function aiLayoutFrameSourceLinkProps(to) {
|
|
190
|
+
return to.startsWith('page:') ? {
|
|
191
|
+
screenId: to.slice('page:'.length)
|
|
192
|
+
} : {
|
|
193
|
+
href: to
|
|
194
|
+
};
|
|
195
|
+
}
|
|
196
|
+
/*------------------------------------------
|
|
197
|
+
* Where the menu row fits
|
|
198
|
+
*-----------------------------------------*/ /**
|
|
199
|
+
* The widths the header row is held to, in CSS pixels: a character of a
|
|
200
|
+
* label at the body size, the chevron a menu's trigger carries, the gap
|
|
201
|
+
* between the row's items and between its buttons, a button's padding, and
|
|
202
|
+
* the row's room at each breakpoint (the Container's `lg` width less its
|
|
203
|
+
* gutters).
|
|
204
|
+
*/ export const AI_LAYOUT_HEADER_MEASURE = {
|
|
205
|
+
char: 9,
|
|
206
|
+
chevron: 28,
|
|
207
|
+
gap: 24,
|
|
208
|
+
buttonGap: 8,
|
|
209
|
+
padding: {
|
|
210
|
+
primary: 32,
|
|
211
|
+
secondary: 32,
|
|
212
|
+
quiet: 16
|
|
213
|
+
},
|
|
214
|
+
room: {
|
|
215
|
+
md: 900 - 48,
|
|
216
|
+
lg: 1200 - 48
|
|
217
|
+
},
|
|
218
|
+
/** What share of the room the row may fill: the estimate is a character's average, so it keeps a margin. */ fill: 0.9,
|
|
219
|
+
/** A logo's width where its picture's shape is unknown. */ logo: 160
|
|
220
|
+
};
|
|
221
|
+
/** The header row's width in CSS pixels, its brand, its menu and its buttons side by side. */ export function aiLayoutHeaderRowWidth(input) {
|
|
222
|
+
const measure = AI_LAYOUT_HEADER_MEASURE;
|
|
223
|
+
const navWidth = input.nav.reduce((sum, item)=>sum + item.label.length * measure.char + (item.menu ? measure.chevron : 0), 0) + Math.max(0, input.nav.length - 1) * measure.gap;
|
|
224
|
+
const ctaWidth = input.ctas.reduce((sum, cta)=>sum + cta.label.length * measure.char + measure.padding[cta.style], 0) + Math.max(0, input.ctas.length - 1) * measure.buttonGap;
|
|
225
|
+
const parts = [
|
|
226
|
+
input.brandWidth,
|
|
227
|
+
navWidth,
|
|
228
|
+
ctaWidth
|
|
229
|
+
].filter((width)=>width > 0);
|
|
230
|
+
return parts.reduce((sum, width)=>sum + width, 0) + Math.max(0, parts.length - 1) * measure.gap;
|
|
231
|
+
}
|
|
232
|
+
/**
|
|
233
|
+
* The screen width the menu row shows from (AGL-4108): `md` where it fits a
|
|
234
|
+
* 900-pixel screen with room to spare, else `lg`; and `lg` whatever it
|
|
235
|
+
* measures for a row of more than five items or more than 60 characters.
|
|
236
|
+
* Below it the row is the phone menu's button.
|
|
237
|
+
*/ export function aiLayoutNavBreakpoint(input) {
|
|
238
|
+
const chars = input.nav.reduce((sum, item)=>sum + item.label.length, 0);
|
|
239
|
+
if (input.nav.length > 5 || chars > 60) return 'lg';
|
|
240
|
+
const measure = AI_LAYOUT_HEADER_MEASURE;
|
|
241
|
+
return aiLayoutHeaderRowWidth(input) <= measure.room.md * measure.fill ? 'md' : 'lg';
|
|
242
|
+
}
|
|
243
|
+
/** A source's header row as `aiLayoutHeaderRowWidth` measures it. */ export function aiLayoutFrameSourceRow(source, brandWidth) {
|
|
244
|
+
return {
|
|
245
|
+
brandWidth,
|
|
246
|
+
nav: source.nav.map((group)=>({
|
|
247
|
+
label: group.label,
|
|
248
|
+
menu: group.items.length > 0
|
|
249
|
+
})),
|
|
250
|
+
ctas: source.ctas.map((cta)=>({
|
|
251
|
+
label: cta.label,
|
|
252
|
+
style: cta.style
|
|
253
|
+
}))
|
|
254
|
+
};
|
|
255
|
+
}
|
|
256
|
+
/*------------------------------------------
|
|
257
|
+
* The header
|
|
258
|
+
*-----------------------------------------*/ const VARIANT = {
|
|
259
|
+
primary: 'contained',
|
|
260
|
+
secondary: 'outlined',
|
|
261
|
+
quiet: 'text'
|
|
262
|
+
};
|
|
263
|
+
/** The most entries one unheaded column of a mega menu holds before it is drawn as two. */ const COLUMN_ENTRIES = 7;
|
|
264
|
+
/** A menu's entries in the columns the source sets them in, in order; a long unheaded run in two. */ export function aiLayoutMenuColumns(entries) {
|
|
265
|
+
const columns = [];
|
|
266
|
+
for (const entry of entries){
|
|
267
|
+
var _entry_column;
|
|
268
|
+
const heading = (_entry_column = entry.column) != null ? _entry_column : null;
|
|
269
|
+
const last = columns[columns.length - 1];
|
|
270
|
+
if (last && last.heading === heading) last.entries.push(entry);
|
|
271
|
+
else columns.push({
|
|
272
|
+
heading,
|
|
273
|
+
entries: [
|
|
274
|
+
entry
|
|
275
|
+
]
|
|
276
|
+
});
|
|
277
|
+
}
|
|
278
|
+
return columns.flatMap((column)=>{
|
|
279
|
+
if (column.heading || column.entries.length <= COLUMN_ENTRIES) return [
|
|
280
|
+
column
|
|
281
|
+
];
|
|
282
|
+
const half = Math.ceil(column.entries.length / 2);
|
|
283
|
+
return [
|
|
284
|
+
{
|
|
285
|
+
heading: null,
|
|
286
|
+
entries: column.entries.slice(0, half)
|
|
287
|
+
},
|
|
288
|
+
{
|
|
289
|
+
heading: null,
|
|
290
|
+
entries: column.entries.slice(half)
|
|
291
|
+
}
|
|
292
|
+
];
|
|
293
|
+
});
|
|
294
|
+
}
|
|
295
|
+
/** Whether a menu is drawn as a Mega Menu: its entries carry a line under them, or sit in more than one headed column. */ export function aiLayoutMenuIsMega(group) {
|
|
296
|
+
if (group.items.some((entry)=>entry.text)) return true;
|
|
297
|
+
return new Set(group.items.map((entry)=>entry.column).filter(Boolean)).size >= 2 || group.items.length > COLUMN_ENTRIES * 2;
|
|
298
|
+
}
|
|
299
|
+
/**
|
|
300
|
+
* The source's header drawn (AGL-4108): its menu row, its buttons and its
|
|
301
|
+
* phone menu. `close` is the interaction a link of the phone menu closes it
|
|
302
|
+
* with.
|
|
303
|
+
*/ export function aiLayoutSourceHeader(tree, source, input) {
|
|
304
|
+
const breakpoint = aiLayoutNavBreakpoint(aiLayoutFrameSourceRow(source, input.brandWidth));
|
|
305
|
+
// A link's look is its parent's, which every link under it inherits: a
|
|
306
|
+
// style repeated on each is one the doctrine asks be carried once.
|
|
307
|
+
const link = (entry, role, interactions, sx = {})=>{
|
|
308
|
+
const id = tree.add('muiScreenLink', _extends({
|
|
309
|
+
children: entry.label
|
|
310
|
+
}, aiLayoutFrameSourceLinkProps(entry.to), {
|
|
311
|
+
renderAs: 'link',
|
|
312
|
+
color: 'inherit'
|
|
313
|
+
}), _extends({
|
|
314
|
+
textDecoration: 'none'
|
|
315
|
+
}, sx), null, role);
|
|
316
|
+
if (interactions) tree.nodes[id].interactions = interactions;
|
|
317
|
+
return id;
|
|
318
|
+
};
|
|
319
|
+
const items = source.nav.map((group)=>group.items.length ? menu(tree, group, link) : group.to ? link({
|
|
320
|
+
label: group.label,
|
|
321
|
+
to: group.to
|
|
322
|
+
}, 'navLink') : null);
|
|
323
|
+
const nav = items.some(Boolean) ? tree.add('muiStack', {
|
|
324
|
+
component: 'nav',
|
|
325
|
+
ariaLabel: 'Main',
|
|
326
|
+
direction: 'row',
|
|
327
|
+
spacing: '3',
|
|
328
|
+
alignItems: 'center'
|
|
329
|
+
}, // Every label on one line, and the row never narrower than its labels.
|
|
330
|
+
{
|
|
331
|
+
display: {
|
|
332
|
+
xs: 'none',
|
|
333
|
+
[breakpoint]: 'flex'
|
|
334
|
+
},
|
|
335
|
+
whiteSpace: 'nowrap',
|
|
336
|
+
flexShrink: 0
|
|
337
|
+
}, items, 'nav') : null;
|
|
338
|
+
const button = (cta, role, extra = {})=>tree.add('muiButton', _extends({
|
|
339
|
+
children: cta.label,
|
|
340
|
+
variant: VARIANT[cta.style],
|
|
341
|
+
color: input.onBrand ? 'secondary' : 'primary'
|
|
342
|
+
}, aiLayoutFrameSourceLinkProps(cta.to), extra), null, null, role);
|
|
343
|
+
// On the smallest phones the buttons move into the menu. Their words on one line, as the row's.
|
|
344
|
+
const ctas = source.ctas.length ? tree.add('muiStack', {
|
|
345
|
+
direction: 'row',
|
|
346
|
+
spacing: '1',
|
|
347
|
+
alignItems: 'center'
|
|
348
|
+
}, {
|
|
349
|
+
display: {
|
|
350
|
+
xs: 'none',
|
|
351
|
+
sm: 'flex'
|
|
352
|
+
},
|
|
353
|
+
flexShrink: 0,
|
|
354
|
+
whiteSpace: 'nowrap',
|
|
355
|
+
ml: 'auto'
|
|
356
|
+
}, source.ctas.map((cta)=>button(cta, 'cta')), 'ctas') : null;
|
|
357
|
+
// The phone menu: each menu as a heading over its entries, its columns'
|
|
358
|
+
// headings among them, the links, then the buttons.
|
|
359
|
+
const drawer = [];
|
|
360
|
+
let links = [];
|
|
361
|
+
const flushLinks = ()=>{
|
|
362
|
+
if (!links.length) return;
|
|
363
|
+
drawer.push(tree.add('muiStack', {
|
|
364
|
+
spacing: '1.5'
|
|
365
|
+
}, {
|
|
366
|
+
typography: 'subtitle1'
|
|
367
|
+
}, links, 'menuLinks'));
|
|
368
|
+
links = [];
|
|
369
|
+
};
|
|
370
|
+
for (const group of source.nav){
|
|
371
|
+
if (!group.items.length) {
|
|
372
|
+
if (group.to) links.push(link({
|
|
373
|
+
label: group.label,
|
|
374
|
+
to: group.to
|
|
375
|
+
}, 'menuLink', input.close));
|
|
376
|
+
continue;
|
|
377
|
+
}
|
|
378
|
+
flushLinks();
|
|
379
|
+
// The menu's name heads its entries; it links where the source's name opens a page and no entry does.
|
|
380
|
+
const own = aiLayoutMenuOwnPage(group);
|
|
381
|
+
const heading = own ? link({
|
|
382
|
+
label: group.label,
|
|
383
|
+
to: own
|
|
384
|
+
}, 'menuHeading', input.close, {
|
|
385
|
+
typography: 'overline'
|
|
386
|
+
}) : tree.add('muiTypography', {
|
|
387
|
+
children: group.label,
|
|
388
|
+
variant: 'overline',
|
|
389
|
+
component: 'p'
|
|
390
|
+
}, {
|
|
391
|
+
color: 'text.secondary'
|
|
392
|
+
}, null, 'menuHeading');
|
|
393
|
+
const entries = [];
|
|
394
|
+
let column = null;
|
|
395
|
+
for (const entry of group.items){
|
|
396
|
+
var _entry_column;
|
|
397
|
+
const under = (_entry_column = entry.column) != null ? _entry_column : null;
|
|
398
|
+
if (under && under !== column) entries.push(tree.add('muiTypography', {
|
|
399
|
+
children: under,
|
|
400
|
+
variant: 'caption',
|
|
401
|
+
component: 'p'
|
|
402
|
+
}, {
|
|
403
|
+
color: 'text.secondary'
|
|
404
|
+
}, null, 'menuColumn'));
|
|
405
|
+
column = under;
|
|
406
|
+
entries.push(link(entry, 'menuEntry', input.close));
|
|
407
|
+
}
|
|
408
|
+
drawer.push(tree.add('muiStack', {
|
|
409
|
+
spacing: '1'
|
|
410
|
+
}, {
|
|
411
|
+
typography: 'body1'
|
|
412
|
+
}, [
|
|
413
|
+
heading,
|
|
414
|
+
...entries
|
|
415
|
+
], 'menuGroup'));
|
|
416
|
+
}
|
|
417
|
+
flushLinks();
|
|
418
|
+
if (source.ctas.length) {
|
|
419
|
+
drawer.push(tree.add('muiStack', {
|
|
420
|
+
spacing: '1'
|
|
421
|
+
}, {
|
|
422
|
+
mt: 1
|
|
423
|
+
}, source.ctas.map((cta)=>button(cta, 'menuCta', {
|
|
424
|
+
fullWidth: true
|
|
425
|
+
})), 'menuCtas'));
|
|
426
|
+
}
|
|
427
|
+
return {
|
|
428
|
+
nav,
|
|
429
|
+
ctas,
|
|
430
|
+
drawer,
|
|
431
|
+
breakpoint
|
|
432
|
+
};
|
|
433
|
+
}
|
|
434
|
+
/**
|
|
435
|
+
* The page a menu's own name opens, where none of its entries does: a page
|
|
436
|
+
* of the site, never an address elsewhere (a Squarespace folder's address
|
|
437
|
+
* is no page). `null` otherwise.
|
|
438
|
+
*/ export function aiLayoutMenuOwnPage(group) {
|
|
439
|
+
const to = group.to;
|
|
440
|
+
if (!to || !(to.startsWith('page:') || isRootRelativePath(to))) return null;
|
|
441
|
+
return group.items.some((entry)=>entry.to === to) ? null : to;
|
|
442
|
+
}
|
|
443
|
+
/**
|
|
444
|
+
* A top-level item with entries: the Mega Menu, its entries in the source's
|
|
445
|
+
* columns, each with its icon and the line under it; or the Dropdown Menu.
|
|
446
|
+
* Where the item's own name goes somewhere none of its entries does, the menu
|
|
447
|
+
* opens with that link, so the page the source's name opens stays reachable.
|
|
448
|
+
*/ function menu(tree, group, link) {
|
|
449
|
+
const own = aiLayoutMenuOwnPage(group);
|
|
450
|
+
const overview = own ? link({
|
|
451
|
+
label: group.label,
|
|
452
|
+
to: own
|
|
453
|
+
}, 'menuOverview', undefined, {
|
|
454
|
+
fontWeight: 700
|
|
455
|
+
}) : null;
|
|
456
|
+
if (!aiLayoutMenuIsMega(group)) {
|
|
457
|
+
// Its entries' words wrap as the source's do; only the row's labels are held to one line.
|
|
458
|
+
return tree.add('muiNavMenu', {
|
|
459
|
+
label: group.label
|
|
460
|
+
}, {
|
|
461
|
+
flexShrink: 0
|
|
462
|
+
}, [
|
|
463
|
+
tree.add('muiStack', {
|
|
464
|
+
spacing: '1'
|
|
465
|
+
}, {
|
|
466
|
+
whiteSpace: 'normal'
|
|
467
|
+
}, [
|
|
468
|
+
overview,
|
|
469
|
+
...group.items.map((entry)=>link(entry, 'menuItem'))
|
|
470
|
+
], 'menuItems')
|
|
471
|
+
], 'navMenu');
|
|
472
|
+
}
|
|
473
|
+
const columns = aiLayoutMenuColumns(group.items);
|
|
474
|
+
// Its columns side by side from a tablet up, as many to a row as the source sets, three at most.
|
|
475
|
+
const span = 12 / Math.min(3, Math.max(1, columns.length));
|
|
476
|
+
const cells = columns.map((column)=>tree.add('muiGrid', {
|
|
477
|
+
size: `xs:12 sm:${span}`
|
|
478
|
+
}, null, [
|
|
479
|
+
tree.add('muiStack', {
|
|
480
|
+
spacing: '1'
|
|
481
|
+
}, null, [
|
|
482
|
+
column.heading ? tree.add('muiTypography', {
|
|
483
|
+
children: column.heading,
|
|
484
|
+
variant: 'overline',
|
|
485
|
+
component: 'p'
|
|
486
|
+
}, {
|
|
487
|
+
color: 'text.secondary'
|
|
488
|
+
}, null, 'menuColumnHeading') : null,
|
|
489
|
+
...column.entries.map((entry)=>entry.text || entry.iconPath ? described(tree, entry) : link(entry, 'menuItem'))
|
|
490
|
+
], 'menuColumn')
|
|
491
|
+
], 'menuCell'));
|
|
492
|
+
const grid = tree.add('muiGrid', {
|
|
493
|
+
container: true,
|
|
494
|
+
spacing: '2'
|
|
495
|
+
}, null, cells, 'menuColumns');
|
|
496
|
+
// Words in the panel wrap as the source's do; only the row's labels are held to one line.
|
|
497
|
+
const panel = tree.add('muiStack', {
|
|
498
|
+
spacing: '2'
|
|
499
|
+
}, {
|
|
500
|
+
whiteSpace: 'normal'
|
|
501
|
+
}, [
|
|
502
|
+
overview,
|
|
503
|
+
grid
|
|
504
|
+
], 'menuPanel');
|
|
505
|
+
return tree.add('muiMegaMenu', {
|
|
506
|
+
label: group.label,
|
|
507
|
+
panelWidth: 'wide'
|
|
508
|
+
}, {
|
|
509
|
+
flexShrink: 0
|
|
510
|
+
}, [
|
|
511
|
+
panel
|
|
512
|
+
], 'megaMenu');
|
|
513
|
+
}
|
|
514
|
+
/** A mega menu's entry: the whole of it one link, its icon beside its name and the line under it. */ function described(tree, entry) {
|
|
515
|
+
// In the brand's color, as the source draws its menu's icons.
|
|
516
|
+
const icon = entry.iconPath ? tree.add('icon', {
|
|
517
|
+
iconPath: entry.iconPath,
|
|
518
|
+
size: '24',
|
|
519
|
+
color: 'primary.main'
|
|
520
|
+
}, null, null, 'menuIcon') : null;
|
|
521
|
+
const name = tree.add('muiTypography', {
|
|
522
|
+
children: entry.label,
|
|
523
|
+
variant: 'subtitle2',
|
|
524
|
+
component: 'p'
|
|
525
|
+
}, {
|
|
526
|
+
fontWeight: 600
|
|
527
|
+
}, null, 'menuEntryName');
|
|
528
|
+
const line = entry.text ? tree.add('muiTypography', {
|
|
529
|
+
children: entry.text,
|
|
530
|
+
variant: 'body2',
|
|
531
|
+
component: 'p'
|
|
532
|
+
}, {
|
|
533
|
+
color: 'text.secondary'
|
|
534
|
+
}, null, 'menuEntryText') : null;
|
|
535
|
+
const words = tree.add('muiStack', {
|
|
536
|
+
spacing: '0.25'
|
|
537
|
+
}, null, [
|
|
538
|
+
name,
|
|
539
|
+
line
|
|
540
|
+
], 'menuEntryWords');
|
|
541
|
+
const row = tree.add('muiStack', {
|
|
542
|
+
direction: 'row',
|
|
543
|
+
spacing: '1.5',
|
|
544
|
+
alignItems: 'flex-start'
|
|
545
|
+
}, null, [
|
|
546
|
+
icon,
|
|
547
|
+
words
|
|
548
|
+
], 'menuEntryRow');
|
|
549
|
+
return tree.add('muiLinkBox', _extends({}, aiLayoutFrameSourceLinkProps(entry.to), {
|
|
550
|
+
ariaLabel: entry.label
|
|
551
|
+
}), {
|
|
552
|
+
color: 'inherit'
|
|
553
|
+
}, [
|
|
554
|
+
row
|
|
555
|
+
], 'menuEntry');
|
|
556
|
+
}
|
|
557
|
+
/*------------------------------------------
|
|
558
|
+
* The footer
|
|
559
|
+
*-----------------------------------------*/ /** The networks the Social Links element draws, by the reader's name for each. */ const SOCIAL_FIELDS = {
|
|
560
|
+
x: 'twitter',
|
|
561
|
+
twitter: 'twitter',
|
|
562
|
+
instagram: 'instagram',
|
|
563
|
+
facebook: 'facebook',
|
|
564
|
+
linkedin: 'linkedin',
|
|
565
|
+
youtube: 'youtube',
|
|
566
|
+
github: 'github'
|
|
567
|
+
};
|
|
568
|
+
/** A phone number as a link that calls it: its digits, a `+` only first; `null` where it is no number. */ export function aiLayoutTelHref(phone) {
|
|
569
|
+
const digits = phone.replace(/[^\d+]/g, '').replace(/(?!^)\+/g, '');
|
|
570
|
+
const href = `tel:${digits}`;
|
|
571
|
+
return isContactHref(href) ? href : null;
|
|
572
|
+
}
|
|
573
|
+
/** The source's footer drawn (AGL-4108): its words and profiles beside its columns, its © line and other links under a rule. */ export function aiLayoutSourceFooter(tree, footer, input) {
|
|
574
|
+
var _footer_copyright;
|
|
575
|
+
// The footer's words are quiet and small, set once on the footer for every line and link under it.
|
|
576
|
+
const quiet = {
|
|
577
|
+
color: 'text.secondary'
|
|
578
|
+
};
|
|
579
|
+
const footerLink = (entry, role)=>tree.add('muiScreenLink', _extends({
|
|
580
|
+
children: entry.label
|
|
581
|
+
}, aiLayoutFrameSourceLinkProps(entry.to), {
|
|
582
|
+
renderAs: 'link',
|
|
583
|
+
color: 'inherit'
|
|
584
|
+
}), {
|
|
585
|
+
textDecoration: 'none'
|
|
586
|
+
}, null, role);
|
|
587
|
+
// Its words, its profiles and how to reach it, beside the columns.
|
|
588
|
+
const lines = footer.text.map((line)=>tree.add('muiTypography', {
|
|
589
|
+
children: line,
|
|
590
|
+
variant: 'body2',
|
|
591
|
+
component: 'p'
|
|
592
|
+
}, null, null, 'footerText'));
|
|
593
|
+
const drawn = {};
|
|
594
|
+
const others = [];
|
|
595
|
+
for (const profile of footer.social){
|
|
596
|
+
const field = SOCIAL_FIELDS[profile.network.toLowerCase()];
|
|
597
|
+
if (field && !drawn[field]) drawn[field] = profile.to;
|
|
598
|
+
else if (!field) others.push(profile);
|
|
599
|
+
}
|
|
600
|
+
const icons = Object.keys(drawn).length ? tree.add('socialLinks', drawn, {
|
|
601
|
+
ml: -1
|
|
602
|
+
}, null, 'social') : null;
|
|
603
|
+
// A profile the Social Links element has no icon for is linked by its name, beside the icons.
|
|
604
|
+
const named = others.map((profile)=>footerLink(profile, 'socialLink'));
|
|
605
|
+
const profiles = icons || named.length ? tree.add('muiStack', {
|
|
606
|
+
direction: 'row',
|
|
607
|
+
spacing: '1.5',
|
|
608
|
+
alignItems: 'center',
|
|
609
|
+
useFlexGap: true,
|
|
610
|
+
flexWrap: 'wrap'
|
|
611
|
+
}, null, [
|
|
612
|
+
icons,
|
|
613
|
+
...named
|
|
614
|
+
], 'profiles') : null;
|
|
615
|
+
const tel = footer.phone ? aiLayoutTelHref(footer.phone) : null;
|
|
616
|
+
const mail = footer.email ? `mailto:${footer.email}` : null;
|
|
617
|
+
const reach = [
|
|
618
|
+
footer.address ? tree.add('muiTypography', {
|
|
619
|
+
children: footer.address,
|
|
620
|
+
variant: 'body2',
|
|
621
|
+
component: 'p'
|
|
622
|
+
}, null, null, 'address') : null,
|
|
623
|
+
footer.phone && tel ? footerLink({
|
|
624
|
+
label: footer.phone,
|
|
625
|
+
to: tel
|
|
626
|
+
}, 'phone') : null,
|
|
627
|
+
footer.email && mail && isContactHref(mail) ? footerLink({
|
|
628
|
+
label: footer.email,
|
|
629
|
+
to: mail
|
|
630
|
+
}, 'email') : null
|
|
631
|
+
].filter((id)=>!!id);
|
|
632
|
+
const about = [
|
|
633
|
+
...lines,
|
|
634
|
+
profiles,
|
|
635
|
+
...reach.length ? [
|
|
636
|
+
tree.add('muiStack', {
|
|
637
|
+
spacing: '0.5'
|
|
638
|
+
}, null, reach, 'reach')
|
|
639
|
+
] : []
|
|
640
|
+
].filter((id)=>!!id);
|
|
641
|
+
// Its columns, each under its heading.
|
|
642
|
+
const count = footer.columns.length;
|
|
643
|
+
const span = Math.max(2, Math.min(6, Math.floor(12 / Math.max(1, count))));
|
|
644
|
+
const columns = footer.columns.map((column)=>tree.add('muiGrid', {
|
|
645
|
+
size: `xs:12 sm:4 md:${span}`
|
|
646
|
+
}, null, [
|
|
647
|
+
tree.add('muiStack', {
|
|
648
|
+
spacing: '1'
|
|
649
|
+
}, null, [
|
|
650
|
+
// A heading in the text's own color over its quiet links.
|
|
651
|
+
tree.add('muiTypography', {
|
|
652
|
+
children: column.heading,
|
|
653
|
+
variant: 'subtitle2',
|
|
654
|
+
component: 'p'
|
|
655
|
+
}, {
|
|
656
|
+
color: 'text.primary'
|
|
657
|
+
}, null, 'footerHeading'),
|
|
658
|
+
...column.links.map((entry)=>footerLink(entry, 'footerLink'))
|
|
659
|
+
], 'footerColumn')
|
|
660
|
+
], 'footerCell'));
|
|
661
|
+
const grid = columns.length ? tree.add('muiGrid', {
|
|
662
|
+
container: true,
|
|
663
|
+
spacing: '3'
|
|
664
|
+
}, null, columns, 'footerColumns') : null;
|
|
665
|
+
const aboutStack = about.length ? tree.add('muiStack', {
|
|
666
|
+
spacing: '2'
|
|
667
|
+
}, null, about, 'footerAbout') : null;
|
|
668
|
+
const top = aboutStack && grid ? tree.add('muiGrid', {
|
|
669
|
+
container: true,
|
|
670
|
+
spacing: '4'
|
|
671
|
+
}, {
|
|
672
|
+
alignItems: 'flex-start'
|
|
673
|
+
}, [
|
|
674
|
+
tree.add('muiGrid', {
|
|
675
|
+
size: 'xs:12 md:3'
|
|
676
|
+
}, null, [
|
|
677
|
+
aboutStack
|
|
678
|
+
], 'footerAboutCell'),
|
|
679
|
+
tree.add('muiGrid', {
|
|
680
|
+
size: 'xs:12 md:9'
|
|
681
|
+
}, null, [
|
|
682
|
+
grid
|
|
683
|
+
], 'footerColumnsCell')
|
|
684
|
+
], 'footerTop') : aboutStack != null ? aboutStack : grid;
|
|
685
|
+
// The © line word for word, and the links outside the columns beside it.
|
|
686
|
+
const copyright = tree.add('muiTypography', {
|
|
687
|
+
children: (_footer_copyright = footer.copyright) != null ? _footer_copyright : `© ${input.name}`,
|
|
688
|
+
variant: 'body2',
|
|
689
|
+
component: 'p'
|
|
690
|
+
}, null, null, 'copyright');
|
|
691
|
+
const legal = footer.links.length ? tree.add('muiStack', {
|
|
692
|
+
direction: 'row',
|
|
693
|
+
spacing: '2',
|
|
694
|
+
useFlexGap: true,
|
|
695
|
+
flexWrap: 'wrap'
|
|
696
|
+
}, null, footer.links.map((entry)=>footerLink(entry, 'footerLegal')), 'footerLinks') : null;
|
|
697
|
+
const bottom = tree.add('muiStack', {
|
|
698
|
+
spacing: '2',
|
|
699
|
+
useFlexGap: true
|
|
700
|
+
}, {
|
|
701
|
+
pt: 3,
|
|
702
|
+
borderTop: 1,
|
|
703
|
+
borderColor: 'divider',
|
|
704
|
+
flexDirection: {
|
|
705
|
+
xs: 'column',
|
|
706
|
+
md: 'row'
|
|
707
|
+
},
|
|
708
|
+
justifyContent: 'space-between',
|
|
709
|
+
alignItems: {
|
|
710
|
+
xs: 'flex-start',
|
|
711
|
+
md: 'center'
|
|
712
|
+
}
|
|
713
|
+
}, [
|
|
714
|
+
copyright,
|
|
715
|
+
legal
|
|
716
|
+
], 'footerBottom');
|
|
717
|
+
return {
|
|
718
|
+
top,
|
|
719
|
+
bottom,
|
|
720
|
+
sx: _extends({}, quiet, {
|
|
721
|
+
typography: 'body2'
|
|
722
|
+
})
|
|
723
|
+
};
|
|
724
|
+
}
|
|
725
|
+
|
|
726
|
+
//# sourceMappingURL=ai-layout-frame-source.js.map
|