astro-tractstack 2.3.1 → 2.3.3
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/bin/create-tractstack.js +3 -3
- package/dist/index.js +69 -11
- package/package.json +1 -1
- package/templates/custom/shopify/Cart.tsx +99 -19
- package/templates/custom/shopify/CheckoutModal.tsx +196 -10
- package/templates/custom/shopify/ShopifyCartManager.tsx +79 -76
- package/templates/custom/shopify/ShopifyCheckout.tsx +4 -33
- package/templates/custom/shopify/ShopifyProductGrid.tsx +42 -14
- package/templates/custom/shopify/ShopifyServiceList.tsx +94 -50
- package/templates/custom/shopify/cart.astro +7 -1
- package/templates/src/components/Footer.astro +2 -2
- package/templates/src/components/Header.astro +17 -9
- package/templates/src/components/Menu.tsx +157 -135
- package/templates/src/components/codehooks/BunnyVideoSetup.tsx +2 -2
- package/templates/src/components/codehooks/EpinetDurationSelector.tsx +27 -6
- package/templates/src/components/codehooks/EpinetTableView.tsx +153 -112
- package/templates/src/components/codehooks/EpinetWrapper.tsx +4 -1
- package/templates/src/components/codehooks/FeaturedArticleSetup.tsx +8 -1
- package/templates/src/components/codehooks/ProductCardSetup.tsx +9 -1
- package/templates/src/components/codehooks/ProductGridSetup.tsx +9 -1
- package/templates/src/components/compositor/nodes/BgPaneWrapper.tsx +2 -1
- package/templates/src/components/compositor/nodes/GhostInsertBlock.tsx +1 -1
- package/templates/src/components/edit/ToolBar.tsx +2 -1
- package/templates/src/components/edit/context/ContextPaneConfig_slug.tsx +2 -2
- package/templates/src/components/edit/pane/AddPanePanel_codehook.tsx +13 -0
- package/templates/src/components/edit/pane/AddPanePanel_newCustomCopy.tsx +2 -2
- package/templates/src/components/edit/pane/ConfigPanePanel.tsx +1 -1
- package/templates/src/components/edit/state/SaveModal.tsx +1 -1
- package/templates/src/components/edit/widgets/InteractiveDisclosureWidget.tsx +8 -3
- package/templates/src/components/form/DateTimeInput.tsx +10 -3
- package/templates/src/components/form/FileUpload.tsx +11 -5
- package/templates/src/components/form/NumberInput.tsx +2 -2
- package/templates/src/components/form/advanced/APIConfigSection.tsx +221 -39
- package/templates/src/components/form/brand/SiteConfigSection.tsx +10 -0
- package/templates/src/components/form/shopify/SchedulingSection.tsx +44 -0
- package/templates/src/components/storykeep/Dashboard_Advanced.tsx +10 -1
- package/templates/src/components/storykeep/Dashboard_Shopify.tsx +16 -8
- package/templates/src/components/storykeep/controls/content/BeliefForm.tsx +2 -2
- package/templates/src/components/storykeep/controls/content/ManageContent.tsx +1 -0
- package/templates/src/components/storykeep/controls/content/ProductTable.tsx +2 -2
- package/templates/src/components/storykeep/controls/content/ResourceBulkIngest.tsx +79 -51
- package/templates/src/components/storykeep/controls/content/ResourceForm.tsx +80 -0
- package/templates/src/components/storykeep/controls/content/ResourceTable.tsx +1 -0
- package/templates/src/components/storykeep/email-builder/Blocks.tsx +169 -0
- package/templates/src/components/storykeep/email-builder/EmailBuilder.tsx +223 -0
- package/templates/src/components/storykeep/email-builder/PreviewModal.tsx +136 -0
- package/templates/src/components/storykeep/email-builder/PropertyPanel.tsx +154 -0
- package/templates/src/components/storykeep/shopify/ShopifyDashboard.tsx +1 -8
- package/templates/src/components/storykeep/shopify/ShopifyDashboard_Bookings.tsx +118 -14
- package/templates/src/components/storykeep/shopify/ShopifyDashboard_Emails.tsx +105 -0
- package/templates/src/constants.ts +2 -0
- package/templates/src/layouts/Layout.astro +8 -5
- package/templates/src/pages/api/google/oauth/callback.ts +50 -0
- package/templates/src/pages/api/google/oauth/disconnect.ts +32 -0
- package/templates/src/pages/api/google/oauth/start.ts +32 -0
- package/templates/src/pages/api/google/oauth/status.ts +32 -0
- package/templates/src/pages/privacy.astro +84 -0
- package/templates/src/pages/terms.astro +47 -0
- package/templates/src/stores/shopify.ts +21 -0
- package/templates/src/types/formTypes.ts +4 -2
- package/templates/src/types/tractstack.ts +35 -2
- package/templates/src/utils/api/advancedHelpers.ts +16 -0
- package/templates/src/utils/api/bookingHelpers.ts +3 -1
- package/templates/src/utils/api/brandConfig.ts +2 -0
- package/templates/src/utils/api/brandHelpers.ts +24 -1
- package/templates/src/utils/api/emailHelpers.ts +105 -0
- package/templates/src/utils/booking/appointmentMode.ts +135 -0
- package/templates/src/utils/customHelpers.ts +2 -0
- package/templates/src/utils/tenantResolver.ts +1 -1
- package/utils/inject-files.ts +63 -5
- package/templates/src/components/codehooks/SandboxAuthWrapper.tsx +0 -101
- package/templates/src/utils/actions/actionButton.ts +0 -103
- package/templates/src/utils/actions/preParse_Clicked.ts +0 -87
|
@@ -1,5 +1,10 @@
|
|
|
1
1
|
import { useStore } from '@nanostores/react';
|
|
2
|
-
import {
|
|
2
|
+
import {
|
|
3
|
+
cartStore,
|
|
4
|
+
addQueue,
|
|
5
|
+
getCartItemKey,
|
|
6
|
+
type CartAction,
|
|
7
|
+
} from '@/stores/shopify';
|
|
3
8
|
import type { ResourceNode } from '@/types/compositorTypes';
|
|
4
9
|
|
|
5
10
|
interface Props {
|
|
@@ -11,6 +16,8 @@ interface Props {
|
|
|
11
16
|
};
|
|
12
17
|
}
|
|
13
18
|
|
|
19
|
+
const HEX_BG_RE = /^#([0-9a-fA-F]{3}|[0-9a-fA-F]{6})$/;
|
|
20
|
+
|
|
14
21
|
export default function ShopifyServiceList({ resources = {}, options }: Props) {
|
|
15
22
|
const cart = useStore(cartStore);
|
|
16
23
|
|
|
@@ -18,9 +25,18 @@ export default function ShopifyServiceList({ resources = {}, options }: Props) {
|
|
|
18
25
|
let services = resources['service'] || [];
|
|
19
26
|
|
|
20
27
|
let group = '';
|
|
28
|
+
let title = '';
|
|
29
|
+
let bgColor = '#f9f9f9';
|
|
21
30
|
try {
|
|
22
31
|
const parsedOptions = JSON.parse(options?.params?.options || '{}');
|
|
23
|
-
group = parsedOptions.group
|
|
32
|
+
group = typeof parsedOptions.group === 'string' ? parsedOptions.group : '';
|
|
33
|
+
if (typeof parsedOptions.title === 'string') {
|
|
34
|
+
title = parsedOptions.title.trim();
|
|
35
|
+
}
|
|
36
|
+
const rawBg = parsedOptions.bgColor;
|
|
37
|
+
if (typeof rawBg === 'string' && HEX_BG_RE.test(rawBg)) {
|
|
38
|
+
bgColor = rawBg;
|
|
39
|
+
}
|
|
24
40
|
} catch (e) {
|
|
25
41
|
// Ignore JSON parse errors
|
|
26
42
|
}
|
|
@@ -81,55 +97,83 @@ export default function ShopifyServiceList({ resources = {}, options }: Props) {
|
|
|
81
97
|
}
|
|
82
98
|
|
|
83
99
|
return (
|
|
84
|
-
<
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
}`}
|
|
120
|
-
role="switch"
|
|
121
|
-
aria-checked={isSelected}
|
|
122
|
-
>
|
|
123
|
-
<span
|
|
124
|
-
className={`pointer-events-none inline-block h-5 w-5 transform rounded-full bg-white shadow ring-0 transition duration-200 ease-in-out ${
|
|
125
|
-
isSelected ? 'translate-x-5' : 'translate-x-0'
|
|
100
|
+
<section className="w-full">
|
|
101
|
+
<div
|
|
102
|
+
className="flex w-full flex-col p-12 md:p-12 xl:p-16"
|
|
103
|
+
style={{ backgroundColor: bgColor }}
|
|
104
|
+
>
|
|
105
|
+
{title ? (
|
|
106
|
+
<header className="max-w-4xl">
|
|
107
|
+
<h3
|
|
108
|
+
className="mb-6 text-balance font-action text-2xl font-bold md:text-3xl xl:text-4xl"
|
|
109
|
+
style={{ color: '#2d2923' }}
|
|
110
|
+
>
|
|
111
|
+
{title}
|
|
112
|
+
</h3>
|
|
113
|
+
</header>
|
|
114
|
+
) : null}
|
|
115
|
+
<section className="w-full">
|
|
116
|
+
<div className="space-y-4">
|
|
117
|
+
{displayServices.map((resource) => {
|
|
118
|
+
const variantId = getServiceVariantId(resource);
|
|
119
|
+
const key = getCartItemKey({
|
|
120
|
+
resourceId: resource.id,
|
|
121
|
+
variantId,
|
|
122
|
+
});
|
|
123
|
+
|
|
124
|
+
const cartItem = cart[key];
|
|
125
|
+
const isSelected = (cartItem?.quantity || 0) > 0;
|
|
126
|
+
const duration = resource.optionsPayload?.bookingLengthMinutes;
|
|
127
|
+
|
|
128
|
+
return (
|
|
129
|
+
<div
|
|
130
|
+
key={resource.id}
|
|
131
|
+
className={`flex items-center justify-between rounded-lg border p-4 transition-colors ${
|
|
132
|
+
isSelected
|
|
133
|
+
? 'border-black bg-gray-50'
|
|
134
|
+
: 'border-gray-200 bg-white hover:border-gray-300'
|
|
126
135
|
}`}
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
136
|
+
>
|
|
137
|
+
<div className="flex-grow">
|
|
138
|
+
<div className="flex items-center gap-2">
|
|
139
|
+
<h3 className="font-bold text-gray-900">
|
|
140
|
+
{resource.title}
|
|
141
|
+
</h3>
|
|
142
|
+
{duration && (
|
|
143
|
+
<span className="inline-flex items-center rounded-sm bg-blue-50 px-2 py-0.5 text-xs font-bold text-blue-700">
|
|
144
|
+
{duration} mins
|
|
145
|
+
</span>
|
|
146
|
+
)}
|
|
147
|
+
</div>
|
|
148
|
+
<p className="mt-1 text-sm text-gray-500">
|
|
149
|
+
{resource.oneliner}
|
|
150
|
+
</p>
|
|
151
|
+
</div>
|
|
152
|
+
|
|
153
|
+
<div className="ml-4 flex-shrink-0">
|
|
154
|
+
<button
|
|
155
|
+
onClick={() =>
|
|
156
|
+
handleToggle(resource, cartItem?.quantity || 0)
|
|
157
|
+
}
|
|
158
|
+
className={`relative inline-flex h-6 w-11 flex-shrink-0 cursor-pointer rounded-full border-2 border-transparent transition-colors duration-200 ease-in-out focus:outline-none ${
|
|
159
|
+
isSelected ? 'bg-black' : 'bg-gray-200'
|
|
160
|
+
}`}
|
|
161
|
+
role="switch"
|
|
162
|
+
aria-checked={isSelected}
|
|
163
|
+
>
|
|
164
|
+
<span
|
|
165
|
+
className={`pointer-events-none inline-block h-5 w-5 transform rounded-full bg-white shadow ring-0 transition duration-200 ease-in-out ${
|
|
166
|
+
isSelected ? 'translate-x-5' : 'translate-x-0'
|
|
167
|
+
}`}
|
|
168
|
+
/>
|
|
169
|
+
</button>
|
|
170
|
+
</div>
|
|
171
|
+
</div>
|
|
172
|
+
);
|
|
173
|
+
})}
|
|
130
174
|
</div>
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
</
|
|
175
|
+
</section>
|
|
176
|
+
</div>
|
|
177
|
+
</section>
|
|
134
178
|
);
|
|
135
179
|
}
|
|
@@ -18,6 +18,12 @@ const resources = await getHeaderResources(tenantId, resourceCategories);
|
|
|
18
18
|
|
|
19
19
|
<Layout title="Your Cart" slug="cart">
|
|
20
20
|
<main class="mx-auto max-w-7xl px-4 py-16 md:px-6 xl:px-8">
|
|
21
|
-
<Cart
|
|
21
|
+
<Cart
|
|
22
|
+
resources={resources}
|
|
23
|
+
allowRemote={brandConfig?.SCHEDULING?.allowRemote || false}
|
|
24
|
+
remoteOnly={brandConfig?.SCHEDULING?.remoteOnly || false}
|
|
25
|
+
embedded={true}
|
|
26
|
+
client:only="react"
|
|
27
|
+
/>
|
|
22
28
|
</main>
|
|
23
29
|
</Layout>
|
|
@@ -58,7 +58,7 @@ if (menu?.optionsPayload) {
|
|
|
58
58
|
return item;
|
|
59
59
|
});
|
|
60
60
|
|
|
61
|
-
allLinks =
|
|
61
|
+
allLinks = featuredLinks.concat(additionalLinks);
|
|
62
62
|
}
|
|
63
63
|
|
|
64
64
|
// Parse socials string
|
|
@@ -106,7 +106,7 @@ const createdDate = created ? new Date(created) : new Date();
|
|
|
106
106
|
{allLinks.map((item: any) => (
|
|
107
107
|
<a
|
|
108
108
|
href={item.to}
|
|
109
|
-
class="z-10 whitespace-nowrap rounded bg-brand-7 px-3.5 py-1.5 text-lg text-white shadow-sm transition-colors hover:bg-myblack hover:text-white focus:bg-brand-7 focus:text-white"
|
|
109
|
+
class="z-10 whitespace-nowrap rounded-md bg-brand-7 px-3.5 py-1.5 text-lg text-white shadow-sm transition-colors hover:bg-myblack hover:text-white focus:bg-brand-7 focus:text-white"
|
|
110
110
|
title={item.description}
|
|
111
111
|
>
|
|
112
112
|
<span class="font-bold">{item.name}</span>
|
|
@@ -82,7 +82,9 @@ if (hasShopify) {
|
|
|
82
82
|
<CheckoutModal
|
|
83
83
|
client:only="react"
|
|
84
84
|
resources={shopifyResources}
|
|
85
|
-
maxLength={brandConfig?.
|
|
85
|
+
maxLength={brandConfig?.SCHEDULING?.maxLengthMinutes || 180}
|
|
86
|
+
allowRemote={brandConfig?.SCHEDULING?.allowRemote || false}
|
|
87
|
+
remoteOnly={brandConfig?.SCHEDULING?.remoteOnly || false}
|
|
86
88
|
/>
|
|
87
89
|
)}
|
|
88
90
|
</>
|
|
@@ -123,13 +125,15 @@ if (hasShopify) {
|
|
|
123
125
|
|
|
124
126
|
{
|
|
125
127
|
!!menu ? (
|
|
126
|
-
<
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
128
|
+
<div class="flex min-w-0 flex-1 justify-end">
|
|
129
|
+
<Menu
|
|
130
|
+
payload={menu}
|
|
131
|
+
slug={slug}
|
|
132
|
+
brandConfig={brandConfig}
|
|
133
|
+
isContext={isContext}
|
|
134
|
+
client:load
|
|
135
|
+
/>
|
|
136
|
+
</div>
|
|
133
137
|
) : null
|
|
134
138
|
}
|
|
135
139
|
</div>
|
|
@@ -368,7 +372,11 @@ if (hasShopify) {
|
|
|
368
372
|
{
|
|
369
373
|
!isStoryKeep && hasShopify && (
|
|
370
374
|
<>
|
|
371
|
-
<ShopifyCartManager
|
|
375
|
+
<ShopifyCartManager
|
|
376
|
+
resources={shopifyResources}
|
|
377
|
+
brandConfig={brandConfig}
|
|
378
|
+
client:only="react"
|
|
379
|
+
/>
|
|
372
380
|
<CartModal client:only="react" />
|
|
373
381
|
</>
|
|
374
382
|
)
|
|
@@ -1,38 +1,12 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { useState } from 'react';
|
|
2
|
+
import { Dialog } from '@ark-ui/react/dialog';
|
|
2
3
|
import { Portal } from '@ark-ui/react/portal';
|
|
3
4
|
import ChevronDownIcon from '@heroicons/react/20/solid/ChevronDownIcon';
|
|
5
|
+
import XMarkIcon from '@heroicons/react/24/outline/XMarkIcon';
|
|
4
6
|
import { lispLexer } from '@/utils/actions/lispLexer';
|
|
5
7
|
import { preParseAction } from '@/utils/actions/preParse_Action';
|
|
6
8
|
import type { LispToken } from '@/types/compositorTypes';
|
|
7
9
|
|
|
8
|
-
// CSS to style the menu items with hover and selection states
|
|
9
|
-
const menuStyles = `
|
|
10
|
-
.menu-content {
|
|
11
|
-
transition-property: opacity, transform;
|
|
12
|
-
transition-duration: 200ms;
|
|
13
|
-
z-index: 10050;
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
.menu-content[data-state="open"] {
|
|
17
|
-
opacity: 1;
|
|
18
|
-
transform: translateY(0);
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
.menu-content[data-state="closed"] {
|
|
22
|
-
opacity: 0;
|
|
23
|
-
transform: translateY(1px);
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
.menu-item[data-highlighted] {
|
|
27
|
-
background-color: #f3f4f6;
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
.menu-item:focus {
|
|
31
|
-
outline: 2px solid #0891b2;
|
|
32
|
-
outline-offset: -2px;
|
|
33
|
-
}
|
|
34
|
-
`;
|
|
35
|
-
|
|
36
10
|
interface MenuLink {
|
|
37
11
|
name: string;
|
|
38
12
|
description: string;
|
|
@@ -63,6 +37,7 @@ interface MenuProps {
|
|
|
63
37
|
const MenuComponent = (props: MenuProps) => {
|
|
64
38
|
const { payload, slug, isContext, brandConfig } = props;
|
|
65
39
|
const thisPayload = payload.optionsPayload;
|
|
40
|
+
const [mobileOpen, setMobileOpen] = useState(false);
|
|
66
41
|
|
|
67
42
|
function processMenuLink(e: MenuLink): ProcessedMenuLinkDatum {
|
|
68
43
|
const item = { ...e } as ProcessedMenuLinkDatum;
|
|
@@ -181,12 +156,112 @@ const MenuComponent = (props: MenuProps) => {
|
|
|
181
156
|
);
|
|
182
157
|
};
|
|
183
158
|
|
|
159
|
+
const MobileMenuItem = ({ item }: { item: ProcessedMenuLinkDatum }) => {
|
|
160
|
+
if (item.renderAs === 'button') {
|
|
161
|
+
return (
|
|
162
|
+
<button
|
|
163
|
+
type="button"
|
|
164
|
+
className="block w-full rounded-xl p-4 text-left hover:bg-mygreen/20 focus:outline-none focus:ring-2 focus:ring-myblue"
|
|
165
|
+
aria-label={`${item.name} - ${item.description}`}
|
|
166
|
+
hx-post="/api/v1/state"
|
|
167
|
+
hx-swap="none"
|
|
168
|
+
hx-vals={item.htmxVals}
|
|
169
|
+
onClick={() => setMobileOpen(false)}
|
|
170
|
+
>
|
|
171
|
+
<p className="text-xl font-bold leading-7 text-myblack">
|
|
172
|
+
{item.name}
|
|
173
|
+
</p>
|
|
174
|
+
<p className="mt-1 text-base leading-6 text-mydarkgrey">
|
|
175
|
+
{item.description}
|
|
176
|
+
</p>
|
|
177
|
+
</button>
|
|
178
|
+
);
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
if (item.renderAs === 'a') {
|
|
182
|
+
return (
|
|
183
|
+
<a
|
|
184
|
+
href={item.href}
|
|
185
|
+
className="block w-full rounded-xl p-4 text-left hover:bg-mygreen/20 focus:outline-none focus:ring-2 focus:ring-myblue"
|
|
186
|
+
aria-label={`${item.name} - ${item.description}`}
|
|
187
|
+
onClick={() => setMobileOpen(false)}
|
|
188
|
+
>
|
|
189
|
+
<p className="text-xl font-bold leading-7 text-myblack">
|
|
190
|
+
{item.name}
|
|
191
|
+
</p>
|
|
192
|
+
<p className="mt-1 text-base leading-6 text-mydarkgrey">
|
|
193
|
+
{item.description}
|
|
194
|
+
</p>
|
|
195
|
+
</a>
|
|
196
|
+
);
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
return (
|
|
200
|
+
<span
|
|
201
|
+
className="block w-full rounded-xl p-4 text-left opacity-60"
|
|
202
|
+
aria-label={`${item.name} - ${item.description}`}
|
|
203
|
+
>
|
|
204
|
+
<p className="text-xl font-bold leading-7 text-myblack">{item.name}</p>
|
|
205
|
+
<p className="mt-1 text-base leading-6 text-mydarkgrey">
|
|
206
|
+
{item.description}
|
|
207
|
+
</p>
|
|
208
|
+
</span>
|
|
209
|
+
);
|
|
210
|
+
};
|
|
211
|
+
|
|
212
|
+
const MobileCompactItem = ({ item }: { item: ProcessedMenuLinkDatum }) => {
|
|
213
|
+
if (item.renderAs === 'button') {
|
|
214
|
+
return (
|
|
215
|
+
<button
|
|
216
|
+
type="button"
|
|
217
|
+
className="block w-full rounded-lg p-3 text-left hover:bg-mygreen/20 focus:outline-none focus:ring-2 focus:ring-myblue"
|
|
218
|
+
title={item.description}
|
|
219
|
+
aria-label={`${item.name} - ${item.description}`}
|
|
220
|
+
hx-post="/api/v1/state"
|
|
221
|
+
hx-swap="none"
|
|
222
|
+
hx-vals={item.htmxVals}
|
|
223
|
+
onClick={() => setMobileOpen(false)}
|
|
224
|
+
>
|
|
225
|
+
<span className="block text-base font-bold leading-6 text-mydarkgrey">
|
|
226
|
+
{item.name}
|
|
227
|
+
</span>
|
|
228
|
+
</button>
|
|
229
|
+
);
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
if (item.renderAs === 'a') {
|
|
233
|
+
return (
|
|
234
|
+
<a
|
|
235
|
+
href={item.href}
|
|
236
|
+
className="block w-full rounded-lg p-3 text-left hover:bg-mygreen/20 focus:outline-none focus:ring-2 focus:ring-myblue"
|
|
237
|
+
title={item.description}
|
|
238
|
+
aria-label={`${item.name} - ${item.description}`}
|
|
239
|
+
onClick={() => setMobileOpen(false)}
|
|
240
|
+
>
|
|
241
|
+
<span className="block text-base font-bold leading-6 text-mydarkgrey">
|
|
242
|
+
{item.name}
|
|
243
|
+
</span>
|
|
244
|
+
</a>
|
|
245
|
+
);
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
return (
|
|
249
|
+
<span
|
|
250
|
+
className="block w-full rounded-lg p-3 text-left opacity-60"
|
|
251
|
+
title={item.description}
|
|
252
|
+
aria-label={`${item.name} - ${item.description}`}
|
|
253
|
+
>
|
|
254
|
+
<span className="block text-base font-bold leading-6 text-mydarkgrey">
|
|
255
|
+
{item.name}
|
|
256
|
+
</span>
|
|
257
|
+
</span>
|
|
258
|
+
);
|
|
259
|
+
};
|
|
260
|
+
|
|
184
261
|
return (
|
|
185
262
|
<>
|
|
186
|
-
<style dangerouslySetInnerHTML={{ __html: menuStyles }} />
|
|
187
|
-
|
|
188
263
|
{/* Desktop Navigation */}
|
|
189
|
-
<nav className="ml-6 hidden flex-wrap items-center justify-end space-x-3 font-action md:flex md:space-x-6">
|
|
264
|
+
<nav className="ml-6 hidden min-w-0 max-w-full flex-wrap items-center justify-end space-x-3 font-action md:flex md:space-x-6">
|
|
190
265
|
{featuredLinks.map((item: ProcessedMenuLinkDatum) => (
|
|
191
266
|
<div key={item.name} className="relative py-1.5">
|
|
192
267
|
<InteractiveMenuItem item={item} />
|
|
@@ -196,134 +271,81 @@ const MenuComponent = (props: MenuProps) => {
|
|
|
196
271
|
|
|
197
272
|
{/* Mobile Navigation Menu */}
|
|
198
273
|
<div className="font-action md:hidden">
|
|
199
|
-
<
|
|
200
|
-
|
|
274
|
+
<Dialog.Root
|
|
275
|
+
open={mobileOpen}
|
|
276
|
+
onOpenChange={(details) => setMobileOpen(details.open)}
|
|
277
|
+
>
|
|
278
|
+
<Dialog.Trigger
|
|
201
279
|
className="inline-flex rounded-md px-3 py-2 text-xl font-bold text-myblue hover:text-black focus:outline-none focus:ring-2 focus:ring-myblue"
|
|
202
280
|
aria-label="Open navigation menu"
|
|
203
281
|
>
|
|
204
282
|
<span>MENU</span>
|
|
205
283
|
<ChevronDownIcon className="ml-1 h-5 w-5" aria-hidden="true" />
|
|
206
|
-
</
|
|
284
|
+
</Dialog.Trigger>
|
|
207
285
|
|
|
208
286
|
<Portal>
|
|
209
|
-
<
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
287
|
+
<Dialog.Backdrop
|
|
288
|
+
className="fixed inset-0 bg-black/40"
|
|
289
|
+
style={{ zIndex: 10050 }}
|
|
290
|
+
/>
|
|
291
|
+
<Dialog.Positioner
|
|
292
|
+
className="fixed inset-0"
|
|
293
|
+
style={{ zIndex: 10051 }}
|
|
294
|
+
>
|
|
295
|
+
<Dialog.Content className="h-full w-full overflow-hidden bg-white">
|
|
296
|
+
<div className="flex h-full flex-col overflow-hidden">
|
|
297
|
+
<div className="border-b border-mylightgrey px-4 py-3">
|
|
298
|
+
<div className="flex items-center justify-between gap-3">
|
|
299
|
+
<Dialog.Title className="text-lg font-bold text-myblack">
|
|
300
|
+
Navigation Menu
|
|
301
|
+
</Dialog.Title>
|
|
302
|
+
<Dialog.CloseTrigger
|
|
303
|
+
className="rounded-full p-2 text-mydarkgrey hover:bg-mylightgrey focus:outline-none focus:ring-2 focus:ring-myblue"
|
|
304
|
+
aria-label="Close navigation menu"
|
|
305
|
+
>
|
|
306
|
+
<XMarkIcon className="h-6 w-6" aria-hidden="true" />
|
|
307
|
+
</Dialog.CloseTrigger>
|
|
308
|
+
</div>
|
|
309
|
+
</div>
|
|
310
|
+
|
|
311
|
+
<div className="min-h-0 flex-1 overflow-y-auto overflow-x-hidden px-4 py-4">
|
|
312
|
+
<ul role="list" className="space-y-3">
|
|
215
313
|
{featuredLinks.map((item: ProcessedMenuLinkDatum) => (
|
|
216
|
-
<
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
className="menu-item group relative flex gap-x-6 rounded-lg p-4 hover:bg-mygreen/20"
|
|
220
|
-
>
|
|
221
|
-
<div>
|
|
222
|
-
{item.renderAs === 'button' ? (
|
|
223
|
-
<button
|
|
224
|
-
type="button"
|
|
225
|
-
className="font-action text-xl text-myblack hover:text-black focus:text-black focus:outline-none"
|
|
226
|
-
aria-label={`${item.name} - ${item.description}`}
|
|
227
|
-
hx-post="/api/v1/state"
|
|
228
|
-
hx-swap="none"
|
|
229
|
-
hx-vals={item.htmxVals}
|
|
230
|
-
>
|
|
231
|
-
{item.name}
|
|
232
|
-
<span className="absolute inset-0" />
|
|
233
|
-
</button>
|
|
234
|
-
) : item.renderAs === 'a' ? (
|
|
235
|
-
<a
|
|
236
|
-
href={item.href}
|
|
237
|
-
className="font-action text-xl text-myblack hover:text-black focus:text-black focus:outline-none"
|
|
238
|
-
aria-label={`${item.name} - ${item.description}`}
|
|
239
|
-
>
|
|
240
|
-
{item.name}
|
|
241
|
-
<span className="absolute inset-0" />
|
|
242
|
-
</a>
|
|
243
|
-
) : (
|
|
244
|
-
<span
|
|
245
|
-
className="font-action text-xl text-myblack opacity-50"
|
|
246
|
-
aria-label={`${item.name} - ${item.description}`}
|
|
247
|
-
>
|
|
248
|
-
{item.name}
|
|
249
|
-
</span>
|
|
250
|
-
)}
|
|
251
|
-
<p className="mt-1 text-mydarkgrey">
|
|
252
|
-
{item.description}
|
|
253
|
-
</p>
|
|
254
|
-
</div>
|
|
255
|
-
</Menu.Item>
|
|
314
|
+
<li key={item.name} className="min-w-0">
|
|
315
|
+
<MobileMenuItem item={item} />
|
|
316
|
+
</li>
|
|
256
317
|
))}
|
|
257
|
-
</
|
|
318
|
+
</ul>
|
|
258
319
|
|
|
259
|
-
{/* Additional Links Section */}
|
|
260
320
|
{additionalLinks.length > 0 && (
|
|
261
|
-
<
|
|
262
|
-
<
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
</h3>
|
|
269
|
-
</div>
|
|
321
|
+
<section className="mt-6 rounded-xl bg-slate-50 p-4">
|
|
322
|
+
<h3
|
|
323
|
+
className="text-sm font-bold leading-6 text-myblue"
|
|
324
|
+
id="additional-links-heading"
|
|
325
|
+
>
|
|
326
|
+
Additional Links
|
|
327
|
+
</h3>
|
|
270
328
|
<ul
|
|
271
329
|
role="list"
|
|
272
|
-
className="mt-
|
|
330
|
+
className="mt-3 space-y-2"
|
|
273
331
|
aria-labelledby="additional-links-heading"
|
|
274
332
|
>
|
|
275
333
|
{additionalLinks.map(
|
|
276
334
|
(item: ProcessedMenuLinkDatum) => (
|
|
277
|
-
<li key={item.name} className="
|
|
278
|
-
<
|
|
279
|
-
value={item.name}
|
|
280
|
-
className="menu-item block w-full text-left"
|
|
281
|
-
>
|
|
282
|
-
{item.renderAs === 'button' ? (
|
|
283
|
-
<button
|
|
284
|
-
type="button"
|
|
285
|
-
className="block truncate rounded p-2 text-sm font-bold leading-6 text-mydarkgrey hover:text-black focus:text-black focus:underline focus:outline-none"
|
|
286
|
-
title={item.description}
|
|
287
|
-
aria-label={`${item.name} - ${item.description}`}
|
|
288
|
-
hx-post="/api/v1/state"
|
|
289
|
-
hx-swap="none"
|
|
290
|
-
hx-vals={item.htmxVals}
|
|
291
|
-
>
|
|
292
|
-
{item.name}
|
|
293
|
-
<span className="absolute inset-0" />
|
|
294
|
-
</button>
|
|
295
|
-
) : item.renderAs === 'a' ? (
|
|
296
|
-
<a
|
|
297
|
-
href={item.href}
|
|
298
|
-
className="block truncate rounded p-2 text-sm font-bold leading-6 text-mydarkgrey hover:text-black focus:text-black focus:underline focus:outline-none"
|
|
299
|
-
title={item.description}
|
|
300
|
-
aria-label={`${item.name} - ${item.description}`}
|
|
301
|
-
>
|
|
302
|
-
{item.name}
|
|
303
|
-
<span className="absolute inset-0" />
|
|
304
|
-
</a>
|
|
305
|
-
) : (
|
|
306
|
-
<span
|
|
307
|
-
className="block truncate rounded p-2 text-sm font-bold leading-6 text-mydarkgrey opacity-50"
|
|
308
|
-
title={item.description}
|
|
309
|
-
aria-label={`${item.name} - ${item.description}`}
|
|
310
|
-
>
|
|
311
|
-
{item.name}
|
|
312
|
-
</span>
|
|
313
|
-
)}
|
|
314
|
-
</Menu.Item>
|
|
335
|
+
<li key={item.name} className="min-w-0">
|
|
336
|
+
<MobileCompactItem item={item} />
|
|
315
337
|
</li>
|
|
316
338
|
)
|
|
317
339
|
)}
|
|
318
340
|
</ul>
|
|
319
|
-
</
|
|
341
|
+
</section>
|
|
320
342
|
)}
|
|
321
343
|
</div>
|
|
322
344
|
</div>
|
|
323
|
-
</
|
|
324
|
-
</
|
|
345
|
+
</Dialog.Content>
|
|
346
|
+
</Dialog.Positioner>
|
|
325
347
|
</Portal>
|
|
326
|
-
</
|
|
348
|
+
</Dialog.Root>
|
|
327
349
|
</div>
|
|
328
350
|
</>
|
|
329
351
|
);
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { useState, useEffect, useRef } from 'react';
|
|
1
|
+
import { useState, useEffect, useRef, type FocusEvent } from 'react';
|
|
2
2
|
import XMarkIcon from '@heroicons/react/24/outline/XMarkIcon';
|
|
3
3
|
import PlusIcon from '@heroicons/react/24/outline/PlusIcon';
|
|
4
4
|
import TrashIcon from '@heroicons/react/24/outline/TrashIcon';
|
|
@@ -162,7 +162,7 @@ const BunnyVideoSetup = ({ nodeId, params }: BunnyVideoSetupProps) => {
|
|
|
162
162
|
};
|
|
163
163
|
|
|
164
164
|
const saveChanges = (
|
|
165
|
-
customChapters?: Chapter[] |
|
|
165
|
+
customChapters?: Chapter[] | FocusEvent<HTMLInputElement>
|
|
166
166
|
) => {
|
|
167
167
|
if (!customChapters || 'target' in customChapters) {
|
|
168
168
|
updatePaneNode();
|