@tuturuuu/ui 0.8.0 → 0.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +40 -0
- package/biome.json +1 -1
- package/package.json +73 -71
- package/src/components/ui/accordion.tsx +1 -1
- package/src/components/ui/breadcrumb.tsx +1 -1
- package/src/components/ui/calendar-app/calendar-page-shell.tsx +4 -0
- package/src/components/ui/calendar-app/components/calendar-connections-settings-content.tsx +239 -33
- package/src/components/ui/calendar-app/components/load-smart-scheduling-tasks.tsx +143 -0
- package/src/components/ui/calendar-app/components/priority-view.tsx +10 -3
- package/src/components/ui/calendar-app/components/tasks-sidebar.tsx +4 -116
- package/src/components/ui/calendar-app/components/use-calendar-connections-manager.ts +67 -2
- package/src/components/ui/calendar.tsx +1 -1
- package/src/components/ui/carousel.tsx +1 -1
- package/src/components/ui/chat/chat-agent-details-external-thread-panel.test.tsx +1 -1
- package/src/components/ui/chat/chat-agent-details-external-thread-panel.tsx +1 -1
- package/src/components/ui/chat/chat-agent-details-operations-panel.test.tsx +1 -1
- package/src/components/ui/chat/chat-agent-details-operations-panel.tsx +1 -1
- package/src/components/ui/chat/chat-agent-details-setup-panel.tsx +1 -1
- package/src/components/ui/chat/chat-agent-details-sidebar.test.tsx +1 -1
- package/src/components/ui/chat/chat-agent-details-sidebar.tsx +2 -2
- package/src/components/ui/chat/chat-agent-details-utils.test.ts +1 -1
- package/src/components/ui/chat/chat-agent-details-utils.tsx +1 -1
- package/src/components/ui/chat/chat-agent-details-zalo-personal-panel.tsx +2 -2
- package/src/components/ui/checkbox.tsx +1 -1
- package/src/components/ui/color-picker.tsx +1 -1
- package/src/components/ui/command.tsx +1 -1
- package/src/components/ui/context-menu.tsx +5 -1
- package/src/components/ui/custom/__tests__/settings-dialog-shell.test.tsx +3 -0
- package/src/components/ui/custom/__tests__/workspace-select-helpers.test.ts +19 -0
- package/src/components/ui/custom/combobox.test.tsx +195 -0
- package/src/components/ui/custom/combobox.tsx +273 -156
- package/src/components/ui/custom/education/modules/youtube/delete-link-button.tsx +5 -13
- package/src/components/ui/custom/facebook-mockup/facebook-mockup.tsx +7 -1
- package/src/components/ui/custom/facebook-mockup/form.tsx +1 -1
- package/src/components/ui/custom/facebook-mockup/image-upload-field.tsx +1 -1
- package/src/components/ui/custom/facebook-mockup/preview.tsx +1 -1
- package/src/components/ui/custom/settings-dialog-shell.tsx +2 -1
- package/src/components/ui/custom/theme-toggle.tsx +1 -1
- package/src/components/ui/custom/workspace-select.tsx +8 -3
- package/src/components/ui/dialog.test.tsx +52 -0
- package/src/components/ui/dialog.tsx +6 -2
- package/src/components/ui/dropdown-menu.tsx +5 -1
- package/src/components/ui/finance/debts/debt-loan-form.tsx +12 -5
- package/src/components/ui/finance/debts/debt-loan-summary.tsx +3 -2
- package/src/components/ui/finance/debts/debts-page.test.tsx +54 -5
- package/src/components/ui/finance/debts/debts-page.tsx +15 -2
- package/src/components/ui/finance/invoices/components/subscription-group-selector.tsx +3 -5
- package/src/components/ui/finance/invoices/new-invoice-page.test.tsx +25 -5
- package/src/components/ui/finance/invoices/new-invoice-page.tsx +7 -2
- package/src/components/ui/finance/invoices/standard-invoice.tsx +4 -2
- package/src/components/ui/finance/invoices/subscription-invoice.tsx +4 -2
- package/src/components/ui/finance/invoices/utils.ts +3 -1
- package/src/components/ui/finance/transactions/form-content-dialog.tsx +3 -0
- package/src/components/ui/finance/transactions/form-types.ts +1 -0
- package/src/components/ui/finance/transactions/form.tsx +2 -0
- package/src/components/ui/finance/transactions/infinite-transactions-list.tsx +2 -0
- package/src/components/ui/finance/transactions/period-charts/category-breakdown-dialog.tsx +1 -1
- package/src/components/ui/finance/transactions/transaction-edit-dialog.tsx +1 -4
- package/src/components/ui/finance/transactions/transactions-create-summary.tsx +3 -0
- package/src/components/ui/finance/transactions/transactions-page.tsx +4 -1
- package/src/components/ui/finance/wallets/form.test.tsx +51 -3
- package/src/components/ui/finance/wallets/form.tsx +15 -4
- package/src/components/ui/finance/wallets/walletId/wallet-details-actions.tsx +4 -0
- package/src/components/ui/finance/wallets/walletId/wallet-details-page.tsx +4 -2
- package/src/components/ui/finance/wallets/wallets-data-table.tsx +1 -0
- package/src/components/ui/finance/wallets/wallets-page.tsx +5 -2
- package/src/components/ui/input-otp.tsx +1 -1
- package/src/components/ui/legacy/calendar/all-day-event-bar.tsx +28 -39
- package/src/components/ui/legacy/calendar/calendar-cell.tsx +2 -0
- package/src/components/ui/legacy/calendar/calendar-content.tsx +10 -6
- package/src/components/ui/legacy/calendar/calendar-header.tsx +23 -3
- package/src/components/ui/legacy/calendar/calendar-loading-skeleton.tsx +135 -0
- package/src/components/ui/legacy/calendar/calendar-matrix.tsx +175 -237
- package/src/components/ui/legacy/calendar/event-card.test.tsx +177 -0
- package/src/components/ui/legacy/calendar/event-card.tsx +220 -131
- package/src/components/ui/legacy/calendar/event-modal.tsx +17 -17
- package/src/components/ui/legacy/calendar/event-provider-display.tsx +69 -0
- package/src/components/ui/legacy/calendar/smart-calendar.test.tsx +86 -4
- package/src/components/ui/legacy/calendar/smart-calendar.tsx +32 -2
- package/src/components/ui/legacy/meet/create-plan-dialog.tsx +19 -10
- package/src/components/ui/navigation-menu.tsx +1 -1
- package/src/components/ui/pagination.tsx +1 -1
- package/src/components/ui/radio-group.tsx +1 -1
- package/src/components/ui/select.tsx +5 -1
- package/src/components/ui/sheet.tsx +1 -1
- package/src/components/ui/sidebar.tsx +1 -1
- package/src/components/ui/storefront/cart-popover.tsx +61 -0
- package/src/components/ui/storefront/cart-summary-parts.tsx +290 -0
- package/src/components/ui/storefront/cart-summary.tsx +93 -154
- package/src/components/ui/storefront/checkout-overlay.tsx +4 -5
- package/src/components/ui/storefront/listing-card.tsx +1 -1
- package/src/components/ui/storefront/merch-sections.tsx +70 -0
- package/src/components/ui/storefront/product-detail.tsx +1 -1
- package/src/components/ui/storefront/storefront-surface.test.tsx +106 -11
- package/src/components/ui/storefront/storefront-surface.tsx +101 -166
- package/src/components/ui/storefront/types.ts +4 -0
- package/src/components/ui/storefront/utils.ts +6 -0
- package/src/components/ui/text-editor/__tests__/extensions.test.ts +123 -0
- package/src/components/ui/text-editor/background-color-extension.ts +62 -0
- package/src/components/ui/text-editor/color-controls.tsx +284 -0
- package/src/components/ui/text-editor/editor.tsx +69 -14
- package/src/components/ui/text-editor/extensions.ts +8 -2
- package/src/components/ui/text-editor/highlight-extension.ts +22 -0
- package/src/components/ui/text-editor/tool-bar.tsx +9 -16
- package/src/components/ui/toast.tsx +1 -1
- package/src/components/ui/tu-do/boards/__tests__/board-share-dialog.test.tsx +270 -0
- package/src/components/ui/tu-do/boards/board-public-link-section.tsx +231 -0
- package/src/components/ui/tu-do/boards/board-share-dialog.tsx +222 -109
- package/src/components/ui/tu-do/boards/boardId/board-column.tsx +112 -43
- package/src/components/ui/tu-do/boards/boardId/kanban/bulk/bulk-mutations-clear-delete.ts +2 -0
- package/src/components/ui/tu-do/boards/boardId/kanban/bulk/bulk-mutations-move.ts +5 -0
- package/src/components/ui/tu-do/boards/boardId/kanban/bulk/bulk-mutations-updates.ts +3 -0
- package/src/components/ui/tu-do/boards/boardId/kanban/data/kanban-deadline-query.ts +50 -2
- package/src/components/ui/tu-do/boards/boardId/kanban/dnd/__tests__/column-reorder.test.ts +17 -0
- package/src/components/ui/tu-do/boards/boardId/kanban/dnd/column-reorder.ts +4 -1
- package/src/components/ui/tu-do/boards/boardId/kanban/dnd/task-drag-cache.ts +38 -9
- package/src/components/ui/tu-do/boards/boardId/kanban/dnd/task-drag-order.ts +2 -8
- package/src/components/ui/tu-do/boards/boardId/kanban/dnd/task-sort-key.ts +47 -0
- package/src/components/ui/tu-do/boards/boardId/kanban/dnd/use-kanban-dnd.ts +81 -30
- package/src/components/ui/tu-do/boards/boardId/kanban/planner/__tests__/kanban-planner-island.test.tsx +380 -0
- package/src/components/ui/tu-do/boards/boardId/kanban/planner/kanban-planner-dialog.tsx +204 -0
- package/src/components/ui/tu-do/boards/boardId/kanban/planner/planner-digest-panel.tsx +61 -0
- package/src/components/ui/tu-do/boards/boardId/kanban/planner/planner-item-strip.tsx +54 -0
- package/src/components/ui/tu-do/boards/boardId/kanban/planner/planner-plan-toolbar.tsx +251 -0
- package/src/components/ui/tu-do/boards/boardId/kanban/planner/planner-scope-badge.tsx +27 -0
- package/src/components/ui/tu-do/boards/boardId/kanban/planner/planner-section.tsx +58 -0
- package/src/components/ui/tu-do/boards/boardId/kanban/planner/planner-share-dialog.tsx +238 -0
- package/src/components/ui/tu-do/boards/boardId/kanban/planner/planner-target-controls.tsx +143 -0
- package/src/components/ui/tu-do/boards/boardId/kanban/planner/planner-utils.ts +65 -0
- package/src/components/ui/tu-do/boards/boardId/kanban/planner/use-kanban-planner-state.ts +234 -0
- package/src/components/ui/tu-do/boards/boardId/kanban/rendering/kanban-columns.test.tsx +397 -2
- package/src/components/ui/tu-do/boards/boardId/kanban/rendering/kanban-columns.tsx +103 -13
- package/src/components/ui/tu-do/boards/boardId/kanban/rendering/kanban-deadline-panels.tsx +443 -19
- package/src/components/ui/tu-do/boards/boardId/kanban/rendering/kanban-skeleton.tsx +94 -32
- package/src/components/ui/tu-do/boards/boardId/kanban.tsx +213 -106
- package/src/components/ui/tu-do/boards/boardId/task-board-server-page.test.tsx +26 -4
- package/src/components/ui/tu-do/boards/boardId/task-board-server-page.tsx +5 -2
- package/src/components/ui/tu-do/boards/boardId/task-card/measured-task-card.tsx +3 -0
- package/src/components/ui/tu-do/boards/boardId/task-card/task-card-comparator.ts +3 -0
- package/src/components/ui/tu-do/boards/boardId/task-card/task-card.tsx +191 -28
- package/src/components/ui/tu-do/boards/boardId/task-filter.test.tsx +152 -0
- package/src/components/ui/tu-do/boards/boardId/task-filter.tsx +555 -545
- package/src/components/ui/tu-do/boards/boardId/task-list.tsx +7 -0
- package/src/components/ui/tu-do/boards/share-section.tsx +100 -0
- package/src/components/ui/tu-do/drafts/draft-convert-dialog.tsx +10 -12
- package/src/components/ui/tu-do/drafts/drafts-page.tsx +33 -16
- package/src/components/ui/tu-do/initiatives/task-initiatives-client.tsx +56 -88
- package/src/components/ui/tu-do/my-tasks/my-tasks-content.tsx +26 -2
- package/src/components/ui/tu-do/my-tasks/use-my-tasks-state.ts +55 -8
- package/src/components/ui/tu-do/notes/note-edit-dialog.tsx +1 -4
- package/src/components/ui/tu-do/shared/__tests__/board-client.test.tsx +25 -0
- package/src/components/ui/tu-do/shared/__tests__/board-header.test.tsx +341 -38
- package/src/components/ui/tu-do/shared/__tests__/board-switcher.test.tsx +253 -0
- package/src/components/ui/tu-do/shared/__tests__/board-views.test.tsx +203 -2
- package/src/components/ui/tu-do/shared/__tests__/task-board-loading-state.test.tsx +17 -0
- package/src/components/ui/tu-do/shared/__tests__/task-legacy-route-recovery.test.tsx +16 -0
- package/src/components/ui/tu-do/shared/board-client.tsx +2 -7
- package/src/components/ui/tu-do/shared/board-config-storage.ts +7 -1
- package/src/components/ui/tu-do/shared/board-header.tsx +464 -975
- package/src/components/ui/tu-do/shared/board-layout-settings.tsx +165 -136
- package/src/components/ui/tu-do/shared/board-switcher.tsx +209 -217
- package/src/components/ui/tu-do/shared/board-views.tsx +587 -75
- package/src/components/ui/tu-do/shared/list-view.tsx +227 -1
- package/src/components/ui/tu-do/shared/recycle-bin-panel.tsx +142 -94
- package/src/components/ui/tu-do/shared/special-task-list-pins.ts +51 -0
- package/src/components/ui/tu-do/shared/task-board-loading-state.tsx +28 -0
- package/src/components/ui/tu-do/shared/task-edit-dialog/field-diff-viewer.tsx +3 -2
- package/src/components/ui/tu-do/shared/task-edit-dialog/selective-revert-panel.test.tsx +91 -0
- package/src/components/ui/tu-do/shared/task-edit-dialog/selective-revert-panel.tsx +123 -78
- package/src/components/ui/tu-do/shared/task-edit-dialog/task-activity-section.tsx +7 -1
- package/src/components/ui/tu-do/shared/task-edit-dialog/task-snapshot-dialog.tsx +8 -3
- package/src/components/ui/tu-do/shared/task-edit-dialog.tsx +2 -1
- package/src/components/ui/tu-do/shared/task-legacy-route-recovery.tsx +2 -9
- package/src/declarations.d.ts +1 -0
- package/src/hooks/__tests__/use-calendar-readonly.test.tsx +322 -2
- package/src/hooks/__tests__/use-calendar-sync.test.tsx +446 -0
- package/src/hooks/use-calendar-sync.tsx +247 -243
- package/src/hooks/use-calendar.tsx +323 -138
- package/src/hooks/use-task-actions.ts +24 -0
- package/src/hooks/use-user-workspace-config.ts +75 -0
- package/src/hooks/use-workspace-currency.ts +8 -3
- package/src/hooks/useBoardRealtimeEventHandler.ts +11 -0
|
@@ -1,19 +1,26 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
|
|
3
|
-
import { ArrowLeft
|
|
3
|
+
import { ArrowLeft } from '@tuturuuu/icons';
|
|
4
4
|
import type {
|
|
5
5
|
InventoryStorefront,
|
|
6
6
|
InventoryStorefrontListing,
|
|
7
|
-
InventoryStorefrontSection,
|
|
8
7
|
} from '@tuturuuu/internal-api/inventory';
|
|
9
8
|
import { cn } from '@tuturuuu/utils/format';
|
|
10
|
-
import type
|
|
9
|
+
import { type ReactNode, useState } from 'react';
|
|
10
|
+
import {
|
|
11
|
+
Dialog,
|
|
12
|
+
DialogContent,
|
|
13
|
+
DialogDescription,
|
|
14
|
+
DialogHeader,
|
|
15
|
+
DialogTitle,
|
|
16
|
+
} from '../dialog';
|
|
17
|
+
import { StorefrontCartPopover } from './cart-popover';
|
|
11
18
|
import { StorefrontCartSummary } from './cart-summary';
|
|
12
19
|
import { StorefrontCheckoutOverlay } from './checkout-overlay';
|
|
13
20
|
import { StorefrontEmptyListings } from './empty-listings';
|
|
14
21
|
import { StorefrontHeroPanel } from './hero-panel';
|
|
15
|
-
import { StorefrontImagePanel } from './image-panel';
|
|
16
22
|
import { StorefrontListingCard } from './listing-card';
|
|
23
|
+
import { StorefrontMerchSections } from './merch-sections';
|
|
17
24
|
import { StorefrontProductDetail } from './product-detail';
|
|
18
25
|
import { StorefrontProductDialog } from './product-dialog';
|
|
19
26
|
import type {
|
|
@@ -25,7 +32,6 @@ import type {
|
|
|
25
32
|
import { mergeStorefrontSurfaceLabels } from './types';
|
|
26
33
|
import {
|
|
27
34
|
getAccentStyle,
|
|
28
|
-
getSafeStorefrontHttpUrl,
|
|
29
35
|
getStorefrontLinePrice,
|
|
30
36
|
getStorefrontListingLimit,
|
|
31
37
|
getStorefrontVariantLimit,
|
|
@@ -39,6 +45,7 @@ export function StorefrontSurface({
|
|
|
39
45
|
buyerDefaults,
|
|
40
46
|
cartLines = [],
|
|
41
47
|
cartHref,
|
|
48
|
+
checkoutOpen,
|
|
42
49
|
checkoutHref,
|
|
43
50
|
className,
|
|
44
51
|
compactLayout = false,
|
|
@@ -53,6 +60,8 @@ export function StorefrontSurface({
|
|
|
53
60
|
mode,
|
|
54
61
|
notice,
|
|
55
62
|
onBuyNow,
|
|
63
|
+
onCheckoutOpen,
|
|
64
|
+
onCheckoutOpenChange,
|
|
56
65
|
onCheckoutSubmit,
|
|
57
66
|
onDecrement,
|
|
58
67
|
onDetailListingChange,
|
|
@@ -65,6 +74,7 @@ export function StorefrontSurface({
|
|
|
65
74
|
buyerDefaults?: StorefrontBuyerDefaults;
|
|
66
75
|
cartLines?: StorefrontCartLine[];
|
|
67
76
|
cartHref?: string;
|
|
77
|
+
checkoutOpen?: boolean;
|
|
68
78
|
checkoutHref?: string;
|
|
69
79
|
className?: string;
|
|
70
80
|
compactLayout?: boolean;
|
|
@@ -79,6 +89,8 @@ export function StorefrontSurface({
|
|
|
79
89
|
mode: StorefrontSurfaceMode;
|
|
80
90
|
notice?: ReactNode;
|
|
81
91
|
onBuyNow?: (listingId: string, variantId?: string | null) => void;
|
|
92
|
+
onCheckoutOpen?: () => void;
|
|
93
|
+
onCheckoutOpenChange?: (open: boolean) => void;
|
|
82
94
|
onCheckoutSubmit?: (formData: FormData) => void;
|
|
83
95
|
onDecrement?: (listingId: string, variantId?: string | null) => void;
|
|
84
96
|
onDetailListingChange?: (listingId: string | null) => void;
|
|
@@ -93,6 +105,7 @@ export function StorefrontSurface({
|
|
|
93
105
|
storefrontHref?: string;
|
|
94
106
|
}) {
|
|
95
107
|
const labels = mergeStorefrontSurfaceLabels(labelOverrides);
|
|
108
|
+
const [isCartPopoverOpen, setIsCartPopoverOpen] = useState(false);
|
|
96
109
|
const accentColor = sanitizeStorefrontAccentColor(storefront.accentColor);
|
|
97
110
|
const radius = storefrontRadiusClasses[storefront.cornerStyle];
|
|
98
111
|
const resolveVariant = (
|
|
@@ -135,12 +148,15 @@ export function StorefrontSurface({
|
|
|
135
148
|
? listings.filter((listing) => listing.id === selectedListingId)
|
|
136
149
|
: listings;
|
|
137
150
|
const isCheckout = mode === 'checkout';
|
|
151
|
+
const isCheckoutDialogOpen = checkoutOpen ?? isCheckout;
|
|
138
152
|
const isPreview = mode === 'preview';
|
|
139
|
-
const
|
|
140
|
-
const listingRows =
|
|
141
|
-
? cartEntries.map(({ listing }) => listing)
|
|
142
|
-
: visibleListings;
|
|
153
|
+
const isCartPage = mode === 'cart';
|
|
154
|
+
const listingRows = visibleListings;
|
|
143
155
|
const currency = storefront.currency ?? 'USD';
|
|
156
|
+
const handleCheckoutOpen = () => {
|
|
157
|
+
setIsCartPopoverOpen(false);
|
|
158
|
+
onCheckoutOpen?.();
|
|
159
|
+
};
|
|
144
160
|
|
|
145
161
|
const cartSummary = (
|
|
146
162
|
<StorefrontCartSummary
|
|
@@ -148,36 +164,53 @@ export function StorefrontSurface({
|
|
|
148
164
|
cartEntries={checkoutEntries}
|
|
149
165
|
checkoutHref={checkoutHref}
|
|
150
166
|
currency={currency}
|
|
151
|
-
isCheckout={
|
|
167
|
+
isCheckout={false}
|
|
152
168
|
isPreview={isPreview}
|
|
153
169
|
isSubmitting={isSubmitting}
|
|
154
170
|
labels={labels}
|
|
171
|
+
onCheckoutOpen={handleCheckoutOpen}
|
|
155
172
|
onCheckoutSubmit={onCheckoutSubmit}
|
|
156
173
|
onInstantCheckout={mode === 'cart' ? onInstantCheckout : undefined}
|
|
157
174
|
radius={radius}
|
|
158
175
|
storefront={storefront}
|
|
159
176
|
total={total}
|
|
177
|
+
variant="panel"
|
|
160
178
|
/>
|
|
161
179
|
);
|
|
162
|
-
const
|
|
163
|
-
|
|
164
|
-
|
|
180
|
+
const cartPopoverSummary = (
|
|
181
|
+
<StorefrontCartSummary
|
|
182
|
+
buyerDefaults={buyerDefaults}
|
|
183
|
+
cartEntries={checkoutEntries}
|
|
184
|
+
checkoutHref={checkoutHref}
|
|
185
|
+
currency={currency}
|
|
186
|
+
isPreview={isPreview}
|
|
187
|
+
isSubmitting={isSubmitting}
|
|
188
|
+
labels={labels}
|
|
189
|
+
onCheckoutOpen={handleCheckoutOpen}
|
|
190
|
+
onCheckoutSubmit={onCheckoutSubmit}
|
|
191
|
+
radius={radius}
|
|
192
|
+
storefront={storefront}
|
|
193
|
+
total={total}
|
|
194
|
+
variant="popover"
|
|
195
|
+
/>
|
|
165
196
|
);
|
|
166
|
-
const
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
197
|
+
const checkoutDialogSummary = (
|
|
198
|
+
<StorefrontCartSummary
|
|
199
|
+
buyerDefaults={buyerDefaults}
|
|
200
|
+
cartEntries={checkoutEntries}
|
|
201
|
+
checkoutHref={checkoutHref}
|
|
202
|
+
currency={currency}
|
|
203
|
+
isCheckout
|
|
204
|
+
isPreview={isPreview}
|
|
205
|
+
isSubmitting={isSubmitting}
|
|
206
|
+
labels={labels}
|
|
207
|
+
onCheckoutSubmit={onCheckoutSubmit}
|
|
208
|
+
radius={radius}
|
|
209
|
+
storefront={storefront}
|
|
210
|
+
total={total}
|
|
211
|
+
variant="checkout"
|
|
212
|
+
/>
|
|
179
213
|
);
|
|
180
|
-
|
|
181
214
|
return (
|
|
182
215
|
<main
|
|
183
216
|
className={cn(
|
|
@@ -187,13 +220,6 @@ export function StorefrontSurface({
|
|
|
187
220
|
)}
|
|
188
221
|
style={getAccentStyle(accentColor)}
|
|
189
222
|
>
|
|
190
|
-
{/* Accent strip — makes the storefront's accent color immediately visible. */}
|
|
191
|
-
<div
|
|
192
|
-
className="h-1 w-full"
|
|
193
|
-
style={{
|
|
194
|
-
backgroundColor: 'var(--storefront-accent, var(--primary))',
|
|
195
|
-
}}
|
|
196
|
-
/>
|
|
197
223
|
{notice ? (
|
|
198
224
|
<div className="border-border border-b bg-muted/35 px-4 py-2 text-center text-muted-foreground text-sm">
|
|
199
225
|
{notice}
|
|
@@ -206,7 +232,7 @@ export function StorefrontSurface({
|
|
|
206
232
|
<h1 className="truncate font-semibold text-xl">
|
|
207
233
|
{storefrontHref ? (
|
|
208
234
|
<a
|
|
209
|
-
className="block truncate rounded-sm transition hover:text-[var(--storefront-accent,var(--primary))] focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring/40"
|
|
235
|
+
className="block truncate rounded-sm transition hover:text-[var(--storefront-accent-text,var(--primary))] focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring/40"
|
|
210
236
|
href={storefrontHref}
|
|
211
237
|
title={storefront.name}
|
|
212
238
|
>
|
|
@@ -224,47 +250,28 @@ export function StorefrontSurface({
|
|
|
224
250
|
</div>
|
|
225
251
|
<div className="flex items-center gap-2">
|
|
226
252
|
{headerActions}
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
) : (
|
|
237
|
-
<span className={cartControlClassName} style={cartControlStyle}>
|
|
238
|
-
{cartControlContent}
|
|
239
|
-
</span>
|
|
240
|
-
)}
|
|
253
|
+
<StorefrontCartPopover
|
|
254
|
+
cartQuantity={cartQuantity}
|
|
255
|
+
labels={labels}
|
|
256
|
+
onOpenChange={setIsCartPopoverOpen}
|
|
257
|
+
open={isCartPopoverOpen}
|
|
258
|
+
radius={radius}
|
|
259
|
+
>
|
|
260
|
+
{cartPopoverSummary}
|
|
261
|
+
</StorefrontCartPopover>
|
|
241
262
|
</div>
|
|
242
263
|
</div>
|
|
243
264
|
</header>
|
|
244
265
|
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
</a>
|
|
255
|
-
) : null}
|
|
256
|
-
<h2 className="mb-4 font-semibold text-2xl tracking-tight">
|
|
257
|
-
{labels.checkout}
|
|
258
|
-
</h2>
|
|
259
|
-
{cartSummary}
|
|
260
|
-
</section>
|
|
261
|
-
) : (
|
|
262
|
-
<section
|
|
263
|
-
className={cn(
|
|
264
|
-
'mx-auto grid max-w-7xl gap-4 px-4 py-5',
|
|
265
|
-
compactLayout ? 'grid-cols-1' : 'lg:grid-cols-[minmax(0,1fr)_340px]'
|
|
266
|
-
)}
|
|
267
|
-
>
|
|
266
|
+
<section
|
|
267
|
+
className={cn(
|
|
268
|
+
'mx-auto max-w-7xl px-4 py-5',
|
|
269
|
+
compactLayout ? 'max-w-5xl' : null
|
|
270
|
+
)}
|
|
271
|
+
>
|
|
272
|
+
{isCartPage ? (
|
|
273
|
+
<div className="mx-auto max-w-2xl">{cartSummary}</div>
|
|
274
|
+
) : (
|
|
268
275
|
<div className="min-w-0">
|
|
269
276
|
{isProductDetail && selectedListing ? (
|
|
270
277
|
<>
|
|
@@ -323,39 +330,11 @@ export function StorefrontSurface({
|
|
|
323
330
|
)}
|
|
324
331
|
>
|
|
325
332
|
{listingRows.length === 0 ? (
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
)}
|
|
332
|
-
>
|
|
333
|
-
<div className="max-w-sm">
|
|
334
|
-
<ShoppingCart
|
|
335
|
-
aria-hidden
|
|
336
|
-
className="mx-auto size-8 text-muted-foreground"
|
|
337
|
-
/>
|
|
338
|
-
<p className="mt-3 font-semibold">
|
|
339
|
-
{labels.emptyCart}
|
|
340
|
-
</p>
|
|
341
|
-
{storefrontHref ? (
|
|
342
|
-
<a
|
|
343
|
-
className="mt-4 inline-flex items-center gap-1.5 font-medium text-[var(--storefront-accent,var(--primary))] text-sm hover:underline"
|
|
344
|
-
href={storefrontHref}
|
|
345
|
-
>
|
|
346
|
-
<ArrowLeft aria-hidden className="size-4" />
|
|
347
|
-
{labels.browse}
|
|
348
|
-
</a>
|
|
349
|
-
) : null}
|
|
350
|
-
</div>
|
|
351
|
-
</div>
|
|
352
|
-
) : (
|
|
353
|
-
<StorefrontEmptyListings
|
|
354
|
-
action={emptyAction}
|
|
355
|
-
labels={labels}
|
|
356
|
-
radius={radius}
|
|
357
|
-
/>
|
|
358
|
-
)
|
|
333
|
+
<StorefrontEmptyListings
|
|
334
|
+
action={emptyAction}
|
|
335
|
+
labels={labels}
|
|
336
|
+
radius={radius}
|
|
337
|
+
/>
|
|
359
338
|
) : (
|
|
360
339
|
listingRows.map((listing) => {
|
|
361
340
|
const line = cartLines.find(
|
|
@@ -390,10 +369,8 @@ export function StorefrontSurface({
|
|
|
390
369
|
</>
|
|
391
370
|
)}
|
|
392
371
|
</div>
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
</section>
|
|
396
|
-
)}
|
|
372
|
+
)}
|
|
373
|
+
</section>
|
|
397
374
|
|
|
398
375
|
<StorefrontProductDialog
|
|
399
376
|
cartHref={cartHref}
|
|
@@ -413,66 +390,24 @@ export function StorefrontSurface({
|
|
|
413
390
|
surfaceClassName={storefrontSurfaceClasses[storefront.surfaceStyle]}
|
|
414
391
|
/>
|
|
415
392
|
|
|
393
|
+
<Dialog
|
|
394
|
+
onOpenChange={(open) => onCheckoutOpenChange?.(open)}
|
|
395
|
+
open={isCheckoutDialogOpen}
|
|
396
|
+
>
|
|
397
|
+
<DialogContent className="grid max-h-[92dvh] max-w-[min(42rem,calc(100vw-1rem))] grid-rows-[auto_minmax(0,1fr)] gap-0 overflow-hidden border-border/60 p-0 shadow-2xl sm:rounded-2xl">
|
|
398
|
+
<DialogHeader className="px-5 pt-5 pb-2 text-left sm:px-6">
|
|
399
|
+
<DialogTitle>{labels.checkout}</DialogTitle>
|
|
400
|
+
<DialogDescription>{labels.reservedCopy}</DialogDescription>
|
|
401
|
+
</DialogHeader>
|
|
402
|
+
<div className="min-h-0 overflow-y-auto px-5 pt-3 pb-5 sm:px-6">
|
|
403
|
+
{checkoutDialogSummary}
|
|
404
|
+
</div>
|
|
405
|
+
</DialogContent>
|
|
406
|
+
</Dialog>
|
|
407
|
+
|
|
416
408
|
{isSubmitting || isRedirecting ? (
|
|
417
409
|
<StorefrontCheckoutOverlay label={labels.redirectingToCheckout} />
|
|
418
410
|
) : null}
|
|
419
411
|
</main>
|
|
420
412
|
);
|
|
421
413
|
}
|
|
422
|
-
|
|
423
|
-
function StorefrontMerchSections({
|
|
424
|
-
radius,
|
|
425
|
-
sections,
|
|
426
|
-
}: {
|
|
427
|
-
radius: string;
|
|
428
|
-
sections: InventoryStorefrontSection[];
|
|
429
|
-
}) {
|
|
430
|
-
const visibleSections = sections
|
|
431
|
-
.filter((section) => section.status === 'published')
|
|
432
|
-
.filter((section) => section.sectionType !== 'cover')
|
|
433
|
-
.sort((a, b) => a.sortOrder - b.sortOrder);
|
|
434
|
-
|
|
435
|
-
if (visibleSections.length === 0) return null;
|
|
436
|
-
|
|
437
|
-
return (
|
|
438
|
-
<div className="mt-4 grid gap-3">
|
|
439
|
-
{visibleSections.map((section) => {
|
|
440
|
-
const sectionHref = getSafeStorefrontHttpUrl(section.href);
|
|
441
|
-
|
|
442
|
-
return (
|
|
443
|
-
<section
|
|
444
|
-
className={cn(
|
|
445
|
-
'grid overflow-hidden border border-border bg-card md:grid-cols-[minmax(0,1fr)_280px]',
|
|
446
|
-
radius
|
|
447
|
-
)}
|
|
448
|
-
key={section.id}
|
|
449
|
-
>
|
|
450
|
-
<div className="flex min-w-0 flex-col justify-center gap-2 p-4">
|
|
451
|
-
{section.title ? (
|
|
452
|
-
<h2 className="font-semibold text-lg">{section.title}</h2>
|
|
453
|
-
) : null}
|
|
454
|
-
{section.description ? (
|
|
455
|
-
<p className="text-muted-foreground text-sm leading-6">
|
|
456
|
-
{section.description}
|
|
457
|
-
</p>
|
|
458
|
-
) : null}
|
|
459
|
-
{sectionHref ? (
|
|
460
|
-
<a
|
|
461
|
-
className="mt-1 w-fit font-medium text-sm underline-offset-4 hover:underline"
|
|
462
|
-
href={sectionHref}
|
|
463
|
-
>
|
|
464
|
-
{sectionHref.replace(/^https?:\/\//u, '')}
|
|
465
|
-
</a>
|
|
466
|
-
) : null}
|
|
467
|
-
</div>
|
|
468
|
-
<StorefrontImagePanel
|
|
469
|
-
className="min-h-36 md:min-h-full"
|
|
470
|
-
imageUrl={section.imageUrl}
|
|
471
|
-
label={section.title ?? 'Storefront section'}
|
|
472
|
-
/>
|
|
473
|
-
</section>
|
|
474
|
-
);
|
|
475
|
-
})}
|
|
476
|
-
</div>
|
|
477
|
-
);
|
|
478
|
-
}
|
|
@@ -37,11 +37,13 @@ export type StorefrontSurfaceLabels = {
|
|
|
37
37
|
checkout: string;
|
|
38
38
|
checkoutDisabled: string;
|
|
39
39
|
checkoutDisabledBadge: string;
|
|
40
|
+
contactDetails: string;
|
|
40
41
|
couponNote: string;
|
|
41
42
|
demoBadge: string;
|
|
42
43
|
emptyCart: string;
|
|
43
44
|
fromPrice: string;
|
|
44
45
|
instantCheckout: string;
|
|
46
|
+
orderSummary: string;
|
|
45
47
|
redirectingToCheckout: string;
|
|
46
48
|
selectOptions: string;
|
|
47
49
|
viewDetails: string;
|
|
@@ -77,11 +79,13 @@ export const defaultStorefrontSurfaceLabels: StorefrontSurfaceLabels = {
|
|
|
77
79
|
checkout: 'Checkout',
|
|
78
80
|
checkoutDisabled: 'Checkout is disabled in preview',
|
|
79
81
|
checkoutDisabledBadge: 'Checkout disabled',
|
|
82
|
+
contactDetails: 'Contact details',
|
|
80
83
|
couponNote: 'Have a coupon? You can apply it at checkout.',
|
|
81
84
|
demoBadge: 'Demo',
|
|
82
85
|
emptyCart: 'Add a listing to start checkout.',
|
|
83
86
|
fromPrice: 'From',
|
|
84
87
|
instantCheckout: 'Instant checkout',
|
|
88
|
+
orderSummary: 'Order summary',
|
|
85
89
|
redirectingToCheckout: 'Taking you to secure checkout…',
|
|
86
90
|
selectOptions: 'Select options',
|
|
87
91
|
viewDetails: 'View details',
|
|
@@ -49,7 +49,10 @@ export const storefrontThemeClasses: Record<
|
|
|
49
49
|
|
|
50
50
|
export type StorefrontAccentStyle = CSSProperties & {
|
|
51
51
|
'--storefront-accent'?: string;
|
|
52
|
+
'--storefront-accent-border'?: string;
|
|
52
53
|
'--storefront-accent-foreground'?: string;
|
|
54
|
+
'--storefront-accent-soft'?: string;
|
|
55
|
+
'--storefront-accent-text'?: string;
|
|
53
56
|
};
|
|
54
57
|
|
|
55
58
|
export function sanitizeStorefrontAccentColor(value?: string | null) {
|
|
@@ -182,7 +185,10 @@ export function getAccentStyle(
|
|
|
182
185
|
|
|
183
186
|
return {
|
|
184
187
|
'--storefront-accent': accentColor,
|
|
188
|
+
'--storefront-accent-border': `color-mix(in oklab, ${accentColor} 42%, var(--border))`,
|
|
185
189
|
'--storefront-accent-foreground': getAccentForeground(accentColor),
|
|
190
|
+
'--storefront-accent-soft': `color-mix(in oklab, ${accentColor} 14%, var(--background))`,
|
|
191
|
+
'--storefront-accent-text': `color-mix(in oklab, ${accentColor} 68%, var(--foreground))`,
|
|
186
192
|
};
|
|
187
193
|
}
|
|
188
194
|
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { generateHTML, generateJSON } from '@tiptap/core';
|
|
1
2
|
import type SupabaseProvider from '@tuturuuu/ui/hooks/supabase-provider';
|
|
2
3
|
import { describe, expect, it } from 'vitest';
|
|
3
4
|
import * as Y from 'yjs';
|
|
@@ -34,6 +35,128 @@ describe('text editor extensions', () => {
|
|
|
34
35
|
expect(names).toContain('collaborationCaret');
|
|
35
36
|
});
|
|
36
37
|
|
|
38
|
+
it('registers theme-aware highlight, text color, and background color extensions', () => {
|
|
39
|
+
const names = extensionNames({});
|
|
40
|
+
|
|
41
|
+
expect(names).toContain('highlight');
|
|
42
|
+
expect(names).toContain('textStyle');
|
|
43
|
+
expect(names).toContain('color');
|
|
44
|
+
expect(names).toContain('backgroundColor');
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
it('round-trips theme-aware highlight, text color, and background color marks', () => {
|
|
48
|
+
const extensions = getEditorExtensions();
|
|
49
|
+
const content = {
|
|
50
|
+
type: 'doc',
|
|
51
|
+
content: [
|
|
52
|
+
{
|
|
53
|
+
type: 'paragraph',
|
|
54
|
+
attrs: { textAlign: null },
|
|
55
|
+
content: [
|
|
56
|
+
{
|
|
57
|
+
type: 'text',
|
|
58
|
+
marks: [
|
|
59
|
+
{
|
|
60
|
+
type: 'highlight',
|
|
61
|
+
attrs: {
|
|
62
|
+
color: 'var(--calendar-bg-yellow)',
|
|
63
|
+
textColor: 'var(--yellow)',
|
|
64
|
+
},
|
|
65
|
+
},
|
|
66
|
+
],
|
|
67
|
+
text: 'Highlighted',
|
|
68
|
+
},
|
|
69
|
+
{ type: 'text', text: ' ' },
|
|
70
|
+
{
|
|
71
|
+
type: 'text',
|
|
72
|
+
marks: [
|
|
73
|
+
{
|
|
74
|
+
type: 'textStyle',
|
|
75
|
+
attrs: {
|
|
76
|
+
color: 'var(--blue)',
|
|
77
|
+
backgroundColor: 'var(--calendar-bg-blue)',
|
|
78
|
+
},
|
|
79
|
+
},
|
|
80
|
+
],
|
|
81
|
+
text: 'Colored',
|
|
82
|
+
},
|
|
83
|
+
],
|
|
84
|
+
},
|
|
85
|
+
],
|
|
86
|
+
};
|
|
87
|
+
|
|
88
|
+
const html = generateHTML(content, extensions);
|
|
89
|
+
|
|
90
|
+
expect(html).toContain('var(--calendar-bg-yellow)');
|
|
91
|
+
expect(html).toContain('var(--yellow)');
|
|
92
|
+
expect(html).toContain('var(--calendar-bg-blue)');
|
|
93
|
+
expect(html).toContain('var(--blue)');
|
|
94
|
+
|
|
95
|
+
const parsed = generateJSON(html, extensions);
|
|
96
|
+
const paragraph = parsed.content?.[0];
|
|
97
|
+
const highlightedText = paragraph?.content?.[0];
|
|
98
|
+
const coloredText = paragraph?.content?.[2];
|
|
99
|
+
|
|
100
|
+
expect(highlightedText?.marks).toEqual(
|
|
101
|
+
expect.arrayContaining([
|
|
102
|
+
expect.objectContaining({
|
|
103
|
+
type: 'highlight',
|
|
104
|
+
attrs: expect.objectContaining({
|
|
105
|
+
color: 'var(--calendar-bg-yellow)',
|
|
106
|
+
textColor: 'var(--yellow)',
|
|
107
|
+
}),
|
|
108
|
+
}),
|
|
109
|
+
])
|
|
110
|
+
);
|
|
111
|
+
expect(coloredText?.marks).toEqual(
|
|
112
|
+
expect.arrayContaining([
|
|
113
|
+
expect.objectContaining({
|
|
114
|
+
type: 'textStyle',
|
|
115
|
+
attrs: expect.objectContaining({
|
|
116
|
+
color: 'var(--blue)',
|
|
117
|
+
backgroundColor: 'var(--calendar-bg-blue)',
|
|
118
|
+
}),
|
|
119
|
+
}),
|
|
120
|
+
])
|
|
121
|
+
);
|
|
122
|
+
});
|
|
123
|
+
|
|
124
|
+
it('keeps legacy and raw hex highlight content valid', () => {
|
|
125
|
+
const html = generateHTML(
|
|
126
|
+
{
|
|
127
|
+
type: 'doc',
|
|
128
|
+
content: [
|
|
129
|
+
{
|
|
130
|
+
type: 'paragraph',
|
|
131
|
+
attrs: { textAlign: null },
|
|
132
|
+
content: [
|
|
133
|
+
{
|
|
134
|
+
type: 'text',
|
|
135
|
+
marks: [{ type: 'highlight' }],
|
|
136
|
+
text: 'Legacy',
|
|
137
|
+
},
|
|
138
|
+
{ type: 'text', text: ' ' },
|
|
139
|
+
{
|
|
140
|
+
type: 'text',
|
|
141
|
+
marks: [
|
|
142
|
+
{
|
|
143
|
+
type: 'highlight',
|
|
144
|
+
attrs: { color: '#FFF59D' },
|
|
145
|
+
},
|
|
146
|
+
],
|
|
147
|
+
text: 'Hex',
|
|
148
|
+
},
|
|
149
|
+
],
|
|
150
|
+
},
|
|
151
|
+
],
|
|
152
|
+
},
|
|
153
|
+
getEditorExtensions()
|
|
154
|
+
);
|
|
155
|
+
|
|
156
|
+
expect(html).toContain('<mark');
|
|
157
|
+
expect(html).toContain('#FFF59D');
|
|
158
|
+
});
|
|
159
|
+
|
|
37
160
|
it('round-trips task mention workspace metadata through HTML attrs', () => {
|
|
38
161
|
const renderOutput = (Mention.config as any).renderHTML({
|
|
39
162
|
HTMLAttributes: {
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import { Extension } from '@tiptap/core';
|
|
2
|
+
|
|
3
|
+
export interface BackgroundColorOptions {
|
|
4
|
+
types: string[];
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
declare module '@tiptap/core' {
|
|
8
|
+
interface Commands<ReturnType> {
|
|
9
|
+
backgroundColor: {
|
|
10
|
+
setBackgroundColor: (color: string) => ReturnType;
|
|
11
|
+
unsetBackgroundColor: () => ReturnType;
|
|
12
|
+
};
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export const BackgroundColor = Extension.create<BackgroundColorOptions>({
|
|
17
|
+
name: 'backgroundColor',
|
|
18
|
+
|
|
19
|
+
addOptions() {
|
|
20
|
+
return {
|
|
21
|
+
types: ['textStyle'],
|
|
22
|
+
};
|
|
23
|
+
},
|
|
24
|
+
|
|
25
|
+
addGlobalAttributes() {
|
|
26
|
+
return [
|
|
27
|
+
{
|
|
28
|
+
types: this.options.types,
|
|
29
|
+
attributes: {
|
|
30
|
+
backgroundColor: {
|
|
31
|
+
default: null,
|
|
32
|
+
parseHTML: (element) =>
|
|
33
|
+
element.style.getPropertyValue('background-color') || null,
|
|
34
|
+
renderHTML: (attributes) => {
|
|
35
|
+
if (!attributes.backgroundColor) return {};
|
|
36
|
+
|
|
37
|
+
return {
|
|
38
|
+
style: `background-color: ${attributes.backgroundColor}`,
|
|
39
|
+
};
|
|
40
|
+
},
|
|
41
|
+
},
|
|
42
|
+
},
|
|
43
|
+
},
|
|
44
|
+
];
|
|
45
|
+
},
|
|
46
|
+
|
|
47
|
+
addCommands() {
|
|
48
|
+
return {
|
|
49
|
+
setBackgroundColor:
|
|
50
|
+
(color) =>
|
|
51
|
+
({ chain }) =>
|
|
52
|
+
chain().setMark('textStyle', { backgroundColor: color }).run(),
|
|
53
|
+
unsetBackgroundColor:
|
|
54
|
+
() =>
|
|
55
|
+
({ chain }) =>
|
|
56
|
+
chain()
|
|
57
|
+
.setMark('textStyle', { backgroundColor: null })
|
|
58
|
+
.removeEmptyTextStyle()
|
|
59
|
+
.run(),
|
|
60
|
+
};
|
|
61
|
+
},
|
|
62
|
+
});
|