@open-mercato/core 0.7.1-develop.7202.1.a52dc6707c → 0.8.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/.turbo/turbo-build.log +1 -1
- package/build.mjs +11 -0
- package/dist/modules/catalog/backend/catalog/products/[id]/page.js +23 -0
- package/dist/modules/catalog/backend/catalog/products/[id]/page.js.map +2 -2
- package/dist/modules/customers/backend/customers/companies/[id]/page.js +2 -1
- package/dist/modules/customers/backend/customers/companies/[id]/page.js.map +2 -2
- package/dist/modules/customers/backend/customers/companies-v2/[id]/page.js +2 -1
- package/dist/modules/customers/backend/customers/companies-v2/[id]/page.js.map +2 -2
- package/dist/modules/customers/backend/customers/people/[id]/page.js +2 -2
- package/dist/modules/customers/backend/customers/people/[id]/page.js.map +2 -2
- package/dist/modules/customers/backend/customers/people-v2/[id]/page.js +2 -1
- package/dist/modules/customers/backend/customers/people-v2/[id]/page.js.map +2 -2
- package/dist/modules/customers/lib/detailHelpers.js +4 -0
- package/dist/modules/customers/lib/detailHelpers.js.map +2 -2
- package/dist/modules/customers/lib/todoCompatibility.js +5 -0
- package/dist/modules/customers/lib/todoCompatibility.js.map +2 -2
- package/dist/modules/design_system/backend/design-system/page.meta.js +3 -2
- package/dist/modules/design_system/backend/design-system/page.meta.js.map +2 -2
- package/dist/modules/design_system/gallery/assets/ai-prompt-image.png +0 -0
- package/dist/modules/design_system/gallery/assets/avatar-illustration.png +0 -0
- package/dist/modules/design_system/gallery/assets/avatar-memoji.png +0 -0
- package/dist/modules/design_system/gallery/assets/avatar-photo.png +0 -0
- package/dist/modules/design_system/gallery/assets/blog-photo-0.png +0 -0
- package/dist/modules/design_system/gallery/assets/contact-support-portrait.png +0 -0
- package/dist/modules/design_system/gallery/assets/crypto-ada.png +0 -0
- package/dist/modules/design_system/gallery/assets/crypto-avax.png +0 -0
- package/dist/modules/design_system/gallery/assets/crypto-bnb.png +0 -0
- package/dist/modules/design_system/gallery/assets/crypto-brand.png +0 -0
- package/dist/modules/design_system/gallery/assets/crypto-btc.png +0 -0
- package/dist/modules/design_system/gallery/assets/crypto-clock.svg +3 -0
- package/dist/modules/design_system/gallery/assets/crypto-crown.svg +3 -0
- package/dist/modules/design_system/gallery/assets/crypto-dot.png +0 -0
- package/dist/modules/design_system/gallery/assets/crypto-eth.png +0 -0
- package/dist/modules/design_system/gallery/assets/crypto-hbar.png +0 -0
- package/dist/modules/design_system/gallery/assets/crypto-history.svg +3 -0
- package/dist/modules/design_system/gallery/assets/crypto-link.svg +3 -0
- package/dist/modules/design_system/gallery/assets/crypto-menu.svg +3 -0
- package/dist/modules/design_system/gallery/assets/crypto-metamask.png +0 -0
- package/dist/modules/design_system/gallery/assets/crypto-more.svg +3 -0
- package/dist/modules/design_system/gallery/assets/crypto-phantom.png +0 -0
- package/dist/modules/design_system/gallery/assets/crypto-rewards.svg +3 -0
- package/dist/modules/design_system/gallery/assets/crypto-sad-face.png +0 -0
- package/dist/modules/design_system/gallery/assets/crypto-sol.png +0 -0
- package/dist/modules/design_system/gallery/assets/crypto-star.svg +3 -0
- package/dist/modules/design_system/gallery/assets/crypto-status-cancelled.svg +3 -0
- package/dist/modules/design_system/gallery/assets/crypto-status-pending.svg +3 -0
- package/dist/modules/design_system/gallery/assets/crypto-status-success.svg +3 -0
- package/dist/modules/design_system/gallery/assets/crypto-status-warning.svg +3 -0
- package/dist/modules/design_system/gallery/assets/crypto-trx.png +0 -0
- package/dist/modules/design_system/gallery/assets/crypto-usdc.png +0 -0
- package/dist/modules/design_system/gallery/assets/crypto-usdt.png +0 -0
- package/dist/modules/design_system/gallery/assets/crypto-walletconnect.png +0 -0
- package/dist/modules/design_system/gallery/assets/crypto-wei.png +0 -0
- package/dist/modules/design_system/gallery/assets/crypto-xrp.png +0 -0
- package/dist/modules/design_system/gallery/assets/cryptocurrency-artwork.js +67 -0
- package/dist/modules/design_system/gallery/assets/cryptocurrency-artwork.js.map +7 -0
- package/dist/modules/design_system/gallery/assets/github-logo.png +0 -0
- package/dist/modules/design_system/gallery/assets/images.d.js +1 -0
- package/dist/modules/design_system/gallery/assets/images.d.js.map +7 -0
- package/dist/modules/design_system/gallery/assets/input-european-union.svg +18 -0
- package/dist/modules/design_system/gallery/assets/landing-teams-01.png +0 -0
- package/dist/modules/design_system/gallery/assets/marketing-artwork.js +83 -0
- package/dist/modules/design_system/gallery/assets/marketing-artwork.js.map +7 -0
- package/dist/modules/design_system/gallery/assets/menu-apex.svg +18 -0
- package/dist/modules/design_system/gallery/assets/menu-mastercard.svg +7 -0
- package/dist/modules/design_system/gallery/assets/menu-spotify.svg +4 -0
- package/dist/modules/design_system/gallery/assets/menu-united-states.svg +16 -0
- package/dist/modules/design_system/gallery/assets/select-google.png +0 -0
- package/dist/modules/design_system/gallery/assets/select-poland.png +0 -0
- package/dist/modules/design_system/gallery/assets/selection-artwork.js +5 -0
- package/dist/modules/design_system/gallery/assets/selection-artwork.js.map +7 -0
- package/dist/modules/design_system/gallery/assets/social-apple-filled.svg +4 -0
- package/dist/modules/design_system/gallery/assets/social-apple-stroke.svg +4 -0
- package/dist/modules/design_system/gallery/assets/social-dropbox-filled.svg +5 -0
- package/dist/modules/design_system/gallery/assets/social-dropbox-stroke.svg +5 -0
- package/dist/modules/design_system/gallery/assets/social-facebook-filled.svg +4 -0
- package/dist/modules/design_system/gallery/assets/social-facebook-stroke.svg +10 -0
- package/dist/modules/design_system/gallery/assets/social-github-filled.svg +4 -0
- package/dist/modules/design_system/gallery/assets/social-github-stroke.svg +4 -0
- package/dist/modules/design_system/gallery/assets/social-google-filled.svg +7 -0
- package/dist/modules/design_system/gallery/assets/social-google-stroke.svg +7 -0
- package/dist/modules/design_system/gallery/assets/social-linkedin-filled.svg +4 -0
- package/dist/modules/design_system/gallery/assets/social-linkedin-stroke.svg +4 -0
- package/dist/modules/design_system/gallery/assets/social-x-filled.svg +4 -0
- package/dist/modules/design_system/gallery/assets/social-x-stroke.svg +4 -0
- package/dist/modules/design_system/gallery/assets/style-studio-product.png +0 -0
- package/dist/modules/design_system/gallery/assets/upload-images.js +8 -0
- package/dist/modules/design_system/gallery/assets/upload-images.js.map +7 -0
- package/dist/modules/design_system/gallery/components/BrandStylePreview.js +74 -0
- package/dist/modules/design_system/gallery/components/BrandStylePreview.js.map +7 -0
- package/dist/modules/design_system/gallery/components/CodeSnippet.js +31 -31
- package/dist/modules/design_system/gallery/components/CodeSnippet.js.map +2 -2
- package/dist/modules/design_system/gallery/components/ColorPlayground.js +408 -0
- package/dist/modules/design_system/gallery/components/ColorPlayground.js.map +7 -0
- package/dist/modules/design_system/gallery/components/ColorStudioCompositions.js +166 -0
- package/dist/modules/design_system/gallery/components/ColorStudioCompositions.js.map +7 -0
- package/dist/modules/design_system/gallery/components/ColorStudioSpecimen.js +182 -0
- package/dist/modules/design_system/gallery/components/ColorStudioSpecimen.js.map +7 -0
- package/dist/modules/design_system/gallery/components/CompactEntrySpecimen.js +80 -0
- package/dist/modules/design_system/gallery/components/CompactEntrySpecimen.js.map +7 -0
- package/dist/modules/design_system/gallery/components/ComponentIndex.js +42 -0
- package/dist/modules/design_system/gallery/components/ComponentIndex.js.map +7 -0
- package/dist/modules/design_system/gallery/components/DesignSystemHome.js +69 -0
- package/dist/modules/design_system/gallery/components/DesignSystemHome.js.map +7 -0
- package/dist/modules/design_system/gallery/components/EntryCard.js +47 -41
- package/dist/modules/design_system/gallery/components/EntryCard.js.map +2 -2
- package/dist/modules/design_system/gallery/components/EntryHeader.js +68 -0
- package/dist/modules/design_system/gallery/components/EntryHeader.js.map +7 -0
- package/dist/modules/design_system/gallery/components/FamilyEntryPreview.js +19 -0
- package/dist/modules/design_system/gallery/components/FamilyEntryPreview.js.map +7 -0
- package/dist/modules/design_system/gallery/components/FoundationGuide.js +46 -0
- package/dist/modules/design_system/gallery/components/FoundationGuide.js.map +7 -0
- package/dist/modules/design_system/gallery/components/GalleryLink.js +21 -0
- package/dist/modules/design_system/gallery/components/GalleryLink.js.map +7 -0
- package/dist/modules/design_system/gallery/components/GalleryNavigation.js +82 -0
- package/dist/modules/design_system/gallery/components/GalleryNavigation.js.map +7 -0
- package/dist/modules/design_system/gallery/components/GalleryShell.js +147 -67
- package/dist/modules/design_system/gallery/components/GalleryShell.js.map +3 -3
- package/dist/modules/design_system/gallery/components/IconsBrowser.js +33 -0
- package/dist/modules/design_system/gallery/components/IconsBrowser.js.map +7 -0
- package/dist/modules/design_system/gallery/components/SourceLibrary.js +93 -0
- package/dist/modules/design_system/gallery/components/SourceLibrary.js.map +7 -0
- package/dist/modules/design_system/gallery/components/StyleAgents.js +139 -0
- package/dist/modules/design_system/gallery/components/StyleAgents.js.map +7 -0
- package/dist/modules/design_system/gallery/components/ThemeTokenReference.js +101 -0
- package/dist/modules/design_system/gallery/components/ThemeTokenReference.js.map +7 -0
- package/dist/modules/design_system/gallery/components/VariantPreview.js +2 -2
- package/dist/modules/design_system/gallery/components/VariantPreview.js.map +2 -2
- package/dist/modules/design_system/gallery/components/colorPreview.js +58 -0
- package/dist/modules/design_system/gallery/components/colorPreview.js.map +7 -0
- package/dist/modules/design_system/gallery/components/colorStudio.js +218 -0
- package/dist/modules/design_system/gallery/components/colorStudio.js.map +7 -0
- package/dist/modules/design_system/gallery/components/styleAgentBranding.js +46 -0
- package/dist/modules/design_system/gallery/components/styleAgentBranding.js.map +7 -0
- package/dist/modules/design_system/gallery/components/themeTokens.js +32 -0
- package/dist/modules/design_system/gallery/components/themeTokens.js.map +7 -0
- package/dist/modules/design_system/gallery/demos/ai-product-code.generated.js +194 -0
- package/dist/modules/design_system/gallery/demos/ai-product-code.generated.js.map +7 -0
- package/dist/modules/design_system/gallery/demos/ai-product.js +423 -0
- package/dist/modules/design_system/gallery/demos/ai-product.js.map +7 -0
- package/dist/modules/design_system/gallery/demos/buttons.js +289 -0
- package/dist/modules/design_system/gallery/demos/buttons.js.map +7 -0
- package/dist/modules/design_system/gallery/demos/cryptocurrency.js +1708 -0
- package/dist/modules/design_system/gallery/demos/cryptocurrency.js.map +7 -0
- package/dist/modules/design_system/gallery/demos/feeds-code.generated.js +92 -0
- package/dist/modules/design_system/gallery/demos/feeds-code.generated.js.map +7 -0
- package/dist/modules/design_system/gallery/demos/feeds.js +148 -0
- package/dist/modules/design_system/gallery/demos/feeds.js.map +7 -0
- package/dist/modules/design_system/gallery/demos/file-upload.js +72 -0
- package/dist/modules/design_system/gallery/demos/file-upload.js.map +7 -0
- package/dist/modules/design_system/gallery/demos/foundation-reference-data.js +4875 -0
- package/dist/modules/design_system/gallery/demos/foundation-reference-data.js.map +7 -0
- package/dist/modules/design_system/gallery/demos/foundation-references.js +188 -0
- package/dist/modules/design_system/gallery/demos/foundation-references.js.map +7 -0
- package/dist/modules/design_system/gallery/demos/key-components-code.generated.js +6 -0
- package/dist/modules/design_system/gallery/demos/key-components-code.generated.js.map +7 -0
- package/dist/modules/design_system/gallery/demos/key-components.js +127 -0
- package/dist/modules/design_system/gallery/demos/key-components.js.map +7 -0
- package/dist/modules/design_system/gallery/demos/marketing-controls-code.generated.js +98 -0
- package/dist/modules/design_system/gallery/demos/marketing-controls-code.generated.js.map +7 -0
- package/dist/modules/design_system/gallery/demos/marketing-controls.js +134 -0
- package/dist/modules/design_system/gallery/demos/marketing-controls.js.map +7 -0
- package/dist/modules/design_system/gallery/demos/marketing-widgets-code.generated.js +195 -0
- package/dist/modules/design_system/gallery/demos/marketing-widgets-code.generated.js.map +7 -0
- package/dist/modules/design_system/gallery/demos/marketing-widgets.js +586 -0
- package/dist/modules/design_system/gallery/demos/marketing-widgets.js.map +7 -0
- package/dist/modules/design_system/gallery/demos/menu-fixtures.js +40 -0
- package/dist/modules/design_system/gallery/demos/menu-fixtures.js.map +7 -0
- package/dist/modules/design_system/gallery/demos/menu-sources.js +295 -0
- package/dist/modules/design_system/gallery/demos/menu-sources.js.map +7 -0
- package/dist/modules/design_system/gallery/demos/menus.js +150 -0
- package/dist/modules/design_system/gallery/demos/menus.js.map +7 -0
- package/dist/modules/design_system/gallery/demos/rating-reviews.js +53 -0
- package/dist/modules/design_system/gallery/demos/rating-reviews.js.map +7 -0
- package/dist/modules/design_system/gallery/demos/registered-icons.js +104 -0
- package/dist/modules/design_system/gallery/demos/registered-icons.js.map +7 -0
- package/dist/modules/design_system/gallery/demos/selection-cards.js +99 -0
- package/dist/modules/design_system/gallery/demos/selection-cards.js.map +7 -0
- package/dist/modules/design_system/gallery/demos/shell-code.generated.js +189 -0
- package/dist/modules/design_system/gallery/demos/shell-code.generated.js.map +7 -0
- package/dist/modules/design_system/gallery/demos/shell.js +330 -0
- package/dist/modules/design_system/gallery/demos/shell.js.map +7 -0
- package/dist/modules/design_system/gallery/demos/source-artwork.js +120 -0
- package/dist/modules/design_system/gallery/demos/source-artwork.js.map +7 -0
- package/dist/modules/design_system/gallery/demos/source-filters-code.generated.js +125 -0
- package/dist/modules/design_system/gallery/demos/source-filters-code.generated.js.map +7 -0
- package/dist/modules/design_system/gallery/demos/source-filters.js +185 -0
- package/dist/modules/design_system/gallery/demos/source-filters.js.map +7 -0
- package/dist/modules/design_system/gallery/demos/source-icons.js +69 -0
- package/dist/modules/design_system/gallery/demos/source-icons.js.map +7 -0
- package/dist/modules/design_system/gallery/demos/table-cells-code.generated.js +150 -0
- package/dist/modules/design_system/gallery/demos/table-cells-code.generated.js.map +7 -0
- package/dist/modules/design_system/gallery/demos/table-cells.js +181 -0
- package/dist/modules/design_system/gallery/demos/table-cells.js.map +7 -0
- package/dist/modules/design_system/gallery/demos/text-inputs.js +113 -0
- package/dist/modules/design_system/gallery/demos/text-inputs.js.map +7 -0
- package/dist/modules/design_system/gallery/demos/time-picker-code.generated.js +41 -0
- package/dist/modules/design_system/gallery/demos/time-picker-code.generated.js.map +7 -0
- package/dist/modules/design_system/gallery/demos/time-picker.js +41 -0
- package/dist/modules/design_system/gallery/demos/time-picker.js.map +7 -0
- package/dist/modules/design_system/gallery/entries/banners.js +123 -60
- package/dist/modules/design_system/gallery/entries/banners.js.map +2 -2
- package/dist/modules/design_system/gallery/entries/buttons-snippets.js +433 -0
- package/dist/modules/design_system/gallery/entries/buttons-snippets.js.map +7 -0
- package/dist/modules/design_system/gallery/entries/buttons.js +127 -148
- package/dist/modules/design_system/gallery/entries/buttons.js.map +2 -2
- package/dist/modules/design_system/gallery/entries/charts.js +532 -186
- package/dist/modules/design_system/gallery/entries/charts.js.map +2 -2
- package/dist/modules/design_system/gallery/entries/dates.js +191 -3
- package/dist/modules/design_system/gallery/entries/dates.js.map +2 -2
- package/dist/modules/design_system/gallery/entries/detail.js +118 -77
- package/dist/modules/design_system/gallery/entries/detail.js.map +2 -2
- package/dist/modules/design_system/gallery/entries/display.js +1449 -240
- package/dist/modules/design_system/gallery/entries/display.js.map +2 -2
- package/dist/modules/design_system/gallery/entries/feedback.js +1058 -132
- package/dist/modules/design_system/gallery/entries/feedback.js.map +2 -2
- package/dist/modules/design_system/gallery/entries/filters.js +231 -103
- package/dist/modules/design_system/gallery/entries/filters.js.map +2 -2
- package/dist/modules/design_system/gallery/entries/foundations.js +368 -57
- package/dist/modules/design_system/gallery/entries/foundations.js.map +2 -2
- package/dist/modules/design_system/gallery/entries/icons.js +29 -91
- package/dist/modules/design_system/gallery/entries/icons.js.map +2 -2
- package/dist/modules/design_system/gallery/entries/inputs-snippets.js +154 -0
- package/dist/modules/design_system/gallery/entries/inputs-snippets.js.map +7 -0
- package/dist/modules/design_system/gallery/entries/inputs.js +2043 -306
- package/dist/modules/design_system/gallery/entries/inputs.js.map +2 -2
- package/dist/modules/design_system/gallery/entries/library.js +20 -0
- package/dist/modules/design_system/gallery/entries/library.js.map +7 -0
- package/dist/modules/design_system/gallery/entries/messages.js +121 -103
- package/dist/modules/design_system/gallery/entries/messages.js.map +2 -2
- package/dist/modules/design_system/gallery/entries/navigation.js +676 -103
- package/dist/modules/design_system/gallery/entries/navigation.js.map +2 -2
- package/dist/modules/design_system/gallery/entries/notifications.js +131 -78
- package/dist/modules/design_system/gallery/entries/notifications.js.map +2 -2
- package/dist/modules/design_system/gallery/entries/overlays.js +611 -203
- package/dist/modules/design_system/gallery/entries/overlays.js.map +2 -2
- package/dist/modules/design_system/gallery/entries/scaffolding.js +1175 -120
- package/dist/modules/design_system/gallery/entries/scaffolding.js.map +2 -2
- package/dist/modules/design_system/gallery/entries/schedule.js +79 -54
- package/dist/modules/design_system/gallery/entries/schedule.js.map +2 -2
- package/dist/modules/design_system/gallery/presentation.js +108 -0
- package/dist/modules/design_system/gallery/presentation.js.map +7 -0
- package/dist/modules/design_system/gallery/registry.js +8 -5
- package/dist/modules/design_system/gallery/registry.js.map +2 -2
- package/dist/modules/design_system/gallery/source-library.generated.js +5 -0
- package/dist/modules/design_system/gallery/source-library.generated.js.map +7 -0
- package/dist/modules/design_system/i18n/de.json +7167 -0
- package/dist/modules/design_system/i18n/en.json +7167 -0
- package/dist/modules/design_system/i18n/es.json +7167 -0
- package/dist/modules/design_system/i18n/ko.json +7167 -0
- package/dist/modules/design_system/i18n/pl.json +7167 -0
- package/dist/modules/push_notifications/lib/queue.js +1 -31
- package/dist/modules/push_notifications/lib/queue.js.map +2 -2
- package/jest.config.cjs +1 -0
- package/jest.mocks/image.js +1 -0
- package/package.json +8 -9
- package/src/modules/catalog/backend/catalog/products/[id]/page.tsx +50 -0
- package/src/modules/customers/backend/customers/companies/[id]/page.tsx +2 -1
- package/src/modules/customers/backend/customers/companies-v2/[id]/page.tsx +2 -1
- package/src/modules/customers/backend/customers/people/[id]/page.tsx +2 -2
- package/src/modules/customers/backend/customers/people-v2/[id]/page.tsx +2 -1
- package/src/modules/customers/lib/detailHelpers.ts +4 -0
- package/src/modules/customers/lib/todoCompatibility.ts +10 -0
- package/src/modules/design_system/backend/design-system/page.meta.ts +3 -2
- package/src/modules/design_system/gallery/assets/README.md +30 -0
- package/src/modules/design_system/gallery/assets/ai-prompt-image.png +0 -0
- package/src/modules/design_system/gallery/assets/avatar-illustration.png +0 -0
- package/src/modules/design_system/gallery/assets/avatar-memoji.png +0 -0
- package/src/modules/design_system/gallery/assets/avatar-photo.png +0 -0
- package/src/modules/design_system/gallery/assets/blog-photo-0.png +0 -0
- package/src/modules/design_system/gallery/assets/contact-support-portrait.png +0 -0
- package/src/modules/design_system/gallery/assets/crypto-ada.png +0 -0
- package/src/modules/design_system/gallery/assets/crypto-avax.png +0 -0
- package/src/modules/design_system/gallery/assets/crypto-bnb.png +0 -0
- package/src/modules/design_system/gallery/assets/crypto-brand.png +0 -0
- package/src/modules/design_system/gallery/assets/crypto-btc.png +0 -0
- package/src/modules/design_system/gallery/assets/crypto-clock.svg +3 -0
- package/src/modules/design_system/gallery/assets/crypto-crown.svg +3 -0
- package/src/modules/design_system/gallery/assets/crypto-dot.png +0 -0
- package/src/modules/design_system/gallery/assets/crypto-eth.png +0 -0
- package/src/modules/design_system/gallery/assets/crypto-hbar.png +0 -0
- package/src/modules/design_system/gallery/assets/crypto-history.svg +3 -0
- package/src/modules/design_system/gallery/assets/crypto-link.svg +3 -0
- package/src/modules/design_system/gallery/assets/crypto-menu.svg +3 -0
- package/src/modules/design_system/gallery/assets/crypto-metamask.png +0 -0
- package/src/modules/design_system/gallery/assets/crypto-more.svg +3 -0
- package/src/modules/design_system/gallery/assets/crypto-phantom.png +0 -0
- package/src/modules/design_system/gallery/assets/crypto-rewards.svg +3 -0
- package/src/modules/design_system/gallery/assets/crypto-sad-face.png +0 -0
- package/src/modules/design_system/gallery/assets/crypto-sol.png +0 -0
- package/src/modules/design_system/gallery/assets/crypto-star.svg +3 -0
- package/src/modules/design_system/gallery/assets/crypto-status-cancelled.svg +3 -0
- package/src/modules/design_system/gallery/assets/crypto-status-pending.svg +3 -0
- package/src/modules/design_system/gallery/assets/crypto-status-success.svg +3 -0
- package/src/modules/design_system/gallery/assets/crypto-status-warning.svg +3 -0
- package/src/modules/design_system/gallery/assets/crypto-trx.png +0 -0
- package/src/modules/design_system/gallery/assets/crypto-usdc.png +0 -0
- package/src/modules/design_system/gallery/assets/crypto-usdt.png +0 -0
- package/src/modules/design_system/gallery/assets/crypto-walletconnect.png +0 -0
- package/src/modules/design_system/gallery/assets/crypto-wei.png +0 -0
- package/src/modules/design_system/gallery/assets/crypto-xrp.png +0 -0
- package/src/modules/design_system/gallery/assets/cryptocurrency-artwork.ts +63 -0
- package/src/modules/design_system/gallery/assets/github-logo.png +0 -0
- package/src/modules/design_system/gallery/assets/images.d.ts +9 -0
- package/src/modules/design_system/gallery/assets/input-european-union.svg +18 -0
- package/src/modules/design_system/gallery/assets/landing-teams-01.png +0 -0
- package/src/modules/design_system/gallery/assets/marketing-artwork.ts +79 -0
- package/src/modules/design_system/gallery/assets/menu-apex.svg +18 -0
- package/src/modules/design_system/gallery/assets/menu-mastercard.svg +7 -0
- package/src/modules/design_system/gallery/assets/menu-spotify.svg +4 -0
- package/src/modules/design_system/gallery/assets/menu-united-states.svg +16 -0
- package/src/modules/design_system/gallery/assets/select-google.png +0 -0
- package/src/modules/design_system/gallery/assets/select-poland.png +0 -0
- package/src/modules/design_system/gallery/assets/selection-artwork.ts +1 -0
- package/src/modules/design_system/gallery/assets/social-apple-filled.svg +4 -0
- package/src/modules/design_system/gallery/assets/social-apple-stroke.svg +4 -0
- package/src/modules/design_system/gallery/assets/social-dropbox-filled.svg +5 -0
- package/src/modules/design_system/gallery/assets/social-dropbox-stroke.svg +5 -0
- package/src/modules/design_system/gallery/assets/social-facebook-filled.svg +4 -0
- package/src/modules/design_system/gallery/assets/social-facebook-stroke.svg +10 -0
- package/src/modules/design_system/gallery/assets/social-github-filled.svg +4 -0
- package/src/modules/design_system/gallery/assets/social-github-stroke.svg +4 -0
- package/src/modules/design_system/gallery/assets/social-google-filled.svg +7 -0
- package/src/modules/design_system/gallery/assets/social-google-stroke.svg +7 -0
- package/src/modules/design_system/gallery/assets/social-linkedin-filled.svg +4 -0
- package/src/modules/design_system/gallery/assets/social-linkedin-stroke.svg +4 -0
- package/src/modules/design_system/gallery/assets/social-x-filled.svg +4 -0
- package/src/modules/design_system/gallery/assets/social-x-stroke.svg +4 -0
- package/src/modules/design_system/gallery/assets/style-studio-product.png +0 -0
- package/src/modules/design_system/gallery/assets/upload-images.ts +5 -0
- package/src/modules/design_system/gallery/components/BrandStylePreview.tsx +94 -0
- package/src/modules/design_system/gallery/components/CodeSnippet.tsx +34 -34
- package/src/modules/design_system/gallery/components/ColorPlayground.tsx +324 -0
- package/src/modules/design_system/gallery/components/ColorStudioCompositions.tsx +96 -0
- package/src/modules/design_system/gallery/components/ColorStudioSpecimen.tsx +164 -0
- package/src/modules/design_system/gallery/components/CompactEntrySpecimen.tsx +65 -0
- package/src/modules/design_system/gallery/components/ComponentIndex.tsx +47 -0
- package/src/modules/design_system/gallery/components/DesignSystemHome.tsx +66 -0
- package/src/modules/design_system/gallery/components/EntryCard.tsx +52 -37
- package/src/modules/design_system/gallery/components/EntryHeader.tsx +80 -0
- package/src/modules/design_system/gallery/components/FamilyEntryPreview.tsx +22 -0
- package/src/modules/design_system/gallery/components/FoundationGuide.tsx +43 -0
- package/src/modules/design_system/gallery/components/GalleryLink.tsx +19 -0
- package/src/modules/design_system/gallery/components/GalleryNavigation.tsx +86 -0
- package/src/modules/design_system/gallery/components/GalleryShell.tsx +149 -63
- package/src/modules/design_system/gallery/components/IconsBrowser.tsx +40 -0
- package/src/modules/design_system/gallery/components/SourceLibrary.tsx +90 -0
- package/src/modules/design_system/gallery/components/StyleAgents.tsx +115 -0
- package/src/modules/design_system/gallery/components/ThemeTokenReference.tsx +76 -0
- package/src/modules/design_system/gallery/components/VariantPreview.tsx +2 -2
- package/src/modules/design_system/gallery/components/colorPreview.ts +63 -0
- package/src/modules/design_system/gallery/components/colorStudio.ts +260 -0
- package/src/modules/design_system/gallery/components/styleAgentBranding.ts +42 -0
- package/src/modules/design_system/gallery/components/themeTokens.ts +26 -0
- package/src/modules/design_system/gallery/demos/ai-product-code.generated.ts +3 -0
- package/src/modules/design_system/gallery/demos/ai-product.tsx +188 -0
- package/src/modules/design_system/gallery/demos/buttons.tsx +331 -0
- package/src/modules/design_system/gallery/demos/cryptocurrency.tsx +1943 -0
- package/src/modules/design_system/gallery/demos/feeds-code.generated.ts +4 -0
- package/src/modules/design_system/gallery/demos/feeds.tsx +82 -0
- package/src/modules/design_system/gallery/demos/file-upload.tsx +61 -0
- package/src/modules/design_system/gallery/demos/foundation-reference-data.ts +4872 -0
- package/src/modules/design_system/gallery/demos/foundation-references.tsx +140 -0
- package/src/modules/design_system/gallery/demos/key-components-code.generated.ts +3 -0
- package/src/modules/design_system/gallery/demos/key-components.tsx +106 -0
- package/src/modules/design_system/gallery/demos/marketing-controls-code.generated.ts +3 -0
- package/src/modules/design_system/gallery/demos/marketing-controls.tsx +92 -0
- package/src/modules/design_system/gallery/demos/marketing-widgets-code.generated.ts +3 -0
- package/src/modules/design_system/gallery/demos/marketing-widgets.tsx +189 -0
- package/src/modules/design_system/gallery/demos/menu-fixtures.tsx +48 -0
- package/src/modules/design_system/gallery/demos/menu-sources.ts +5 -0
- package/src/modules/design_system/gallery/demos/menus.tsx +122 -0
- package/src/modules/design_system/gallery/demos/rating-reviews.tsx +44 -0
- package/src/modules/design_system/gallery/demos/registered-icons.tsx +117 -0
- package/src/modules/design_system/gallery/demos/selection-cards.tsx +97 -0
- package/src/modules/design_system/gallery/demos/shell-code.generated.ts +3 -0
- package/src/modules/design_system/gallery/demos/shell.tsx +183 -0
- package/src/modules/design_system/gallery/demos/source-artwork.tsx +88 -0
- package/src/modules/design_system/gallery/demos/source-filters-code.generated.ts +3 -0
- package/src/modules/design_system/gallery/demos/source-filters.tsx +119 -0
- package/src/modules/design_system/gallery/demos/source-icons.tsx +62 -0
- package/src/modules/design_system/gallery/demos/table-cells-code.generated.ts +3 -0
- package/src/modules/design_system/gallery/demos/table-cells.tsx +144 -0
- package/src/modules/design_system/gallery/demos/text-inputs.tsx +90 -0
- package/src/modules/design_system/gallery/demos/time-picker-code.generated.ts +5 -0
- package/src/modules/design_system/gallery/demos/time-picker.tsx +31 -0
- package/src/modules/design_system/gallery/entries/banners.tsx +143 -94
- package/src/modules/design_system/gallery/entries/buttons-snippets.ts +432 -0
- package/src/modules/design_system/gallery/entries/buttons.tsx +125 -196
- package/src/modules/design_system/gallery/entries/charts.tsx +545 -233
- package/src/modules/design_system/gallery/entries/dates.tsx +171 -3
- package/src/modules/design_system/gallery/entries/detail.tsx +142 -115
- package/src/modules/design_system/gallery/entries/display.tsx +1691 -319
- package/src/modules/design_system/gallery/entries/feedback.tsx +1082 -169
- package/src/modules/design_system/gallery/entries/filters.tsx +238 -143
- package/src/modules/design_system/gallery/entries/foundations.tsx +404 -61
- package/src/modules/design_system/gallery/entries/icons.tsx +31 -117
- package/src/modules/design_system/gallery/entries/inputs-snippets.ts +156 -0
- package/src/modules/design_system/gallery/entries/inputs.tsx +2146 -503
- package/src/modules/design_system/gallery/entries/library.tsx +16 -0
- package/src/modules/design_system/gallery/entries/messages.tsx +114 -106
- package/src/modules/design_system/gallery/entries/navigation.tsx +782 -192
- package/src/modules/design_system/gallery/entries/notifications.tsx +145 -120
- package/src/modules/design_system/gallery/entries/overlays.tsx +696 -280
- package/src/modules/design_system/gallery/entries/scaffolding.tsx +1192 -158
- package/src/modules/design_system/gallery/entries/schedule.tsx +72 -58
- package/src/modules/design_system/gallery/presentation.ts +106 -0
- package/src/modules/design_system/gallery/registry.ts +8 -6
- package/src/modules/design_system/gallery/source-library.generated.ts +2 -0
- package/src/modules/design_system/i18n/de.json +7112 -3
- package/src/modules/design_system/i18n/en.json +7112 -3
- package/src/modules/design_system/i18n/es.json +7112 -3
- package/src/modules/design_system/i18n/ko.json +7112 -3
- package/src/modules/design_system/i18n/pl.json +7112 -3
- package/src/modules/push_notifications/AGENTS.md +9 -5
- package/src/modules/push_notifications/lib/queue.ts +15 -44
|
@@ -0,0 +1,324 @@
|
|
|
1
|
+
'use client'
|
|
2
|
+
|
|
3
|
+
import * as React from 'react'
|
|
4
|
+
import { ArrowLeft, ArrowRight, GripVertical, Check, Copy, RotateCcw, Sun, Moon, Paintbrush, MousePointer2, Layers, CircleCheck } from 'lucide-react'
|
|
5
|
+
import { useT } from '@open-mercato/shared/lib/i18n/context'
|
|
6
|
+
import { Button } from '@open-mercato/ui/primitives/button'
|
|
7
|
+
import { Input } from '@open-mercato/ui/primitives/input'
|
|
8
|
+
import { Label } from '@open-mercato/ui/primitives/label'
|
|
9
|
+
import { Badge } from '@open-mercato/ui/primitives/badge'
|
|
10
|
+
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@open-mercato/ui/primitives/select'
|
|
11
|
+
import { SegmentedControl, SegmentedControlItem } from '@open-mercato/ui/primitives/segmented-control'
|
|
12
|
+
import { readThemeTokens, type ThemeTokens } from './themeTokens'
|
|
13
|
+
import { contrastRatio, normalizeHex } from './colorPreview'
|
|
14
|
+
import { createColorStudio, exportColorStudio, type ColorStudio, type ColorStop, type Harmony, type NeutralTone, type Shade } from './colorStudio'
|
|
15
|
+
import { ColorStudioSpecimen } from './ColorStudioSpecimen'
|
|
16
|
+
import { ColorStudioCompositions } from './ColorStudioCompositions'
|
|
17
|
+
|
|
18
|
+
const INITIAL_COLOR = '#F4700D'
|
|
19
|
+
const PRESETS = ['#BC9AFF', INITIAL_COLOR, '#2563EB', '#0F766E', '#BE185D']
|
|
20
|
+
const THEMES = ['light', 'dark'] as const
|
|
21
|
+
const ROLE_ICONS = { action: Paintbrush, selection: MousePointer2, surface: Layers, status: CircleCheck }
|
|
22
|
+
|
|
23
|
+
function ShadeSelect({ label, scale, value, onChange }: { label: string; scale: ColorStop[]; value: Shade; onChange: (shade: Shade) => void }) {
|
|
24
|
+
const id = React.useId()
|
|
25
|
+
return <div className="min-w-0 space-y-2">
|
|
26
|
+
<Label htmlFor={id}>{label}</Label>
|
|
27
|
+
<Select value={String(value)} onValueChange={next => {
|
|
28
|
+
const stop = scale.find(item => String(item.step) === next)
|
|
29
|
+
if (stop) onChange(stop.step)
|
|
30
|
+
}}>
|
|
31
|
+
<SelectTrigger id={id} aria-label={label}><SelectValue /></SelectTrigger>
|
|
32
|
+
<SelectContent>{scale.map(stop => <SelectItem key={stop.step} value={String(stop.step)}>
|
|
33
|
+
<span className="inline-flex items-center gap-2"><span aria-hidden className="size-4 shrink-0 rounded-sm border border-border" style={{ backgroundColor: stop.hex }} /><span className="font-mono text-xs">{stop.step} · {stop.hex}</span></span>
|
|
34
|
+
</SelectItem>)}</SelectContent>
|
|
35
|
+
</Select>
|
|
36
|
+
</div>
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
const COLOR_ROLES = ['primary', 'secondary', 'tertiary'] as const
|
|
40
|
+
type ColorRole = typeof COLOR_ROLES[number]
|
|
41
|
+
|
|
42
|
+
function ColorRoleSwap({ role, disabled, onSwap, onDragStart, onDragEnd }: {
|
|
43
|
+
role: ColorRole
|
|
44
|
+
disabled: boolean
|
|
45
|
+
onSwap: (first: ColorRole, second: ColorRole) => void
|
|
46
|
+
onDragStart: (event: React.DragEvent) => void
|
|
47
|
+
onDragEnd: () => void
|
|
48
|
+
}) {
|
|
49
|
+
const t = useT()
|
|
50
|
+
const index = COLOR_ROLES.indexOf(role)
|
|
51
|
+
return <div className="flex items-center gap-1">
|
|
52
|
+
<span draggable={!disabled} onDragStart={onDragStart} onDragEnd={onDragEnd} title={t('design_system.colorStudio.dragColor')} className="inline-flex size-8 cursor-grab items-center justify-center text-muted-foreground active:cursor-grabbing" aria-hidden>
|
|
53
|
+
<GripVertical className="size-4" />
|
|
54
|
+
</span>
|
|
55
|
+
{(['left', 'right'] as const).map(direction => {
|
|
56
|
+
const target = COLOR_ROLES[index + (direction === 'left' ? -1 : 1)]
|
|
57
|
+
const Icon = direction === 'left' ? ArrowLeft : ArrowRight
|
|
58
|
+
return <Button key={direction} type="button" variant="ghost" size="icon" disabled={disabled || !target} aria-label={`${t(`design_system.colorStudio.move${direction === 'left' ? 'Left' : 'Right'}`)}: ${t(`design_system.colorStudio.${role}`)}`} className="shrink-0 text-muted-foreground" onClick={() => { if (target) onSwap(role, target) }}>
|
|
59
|
+
<Icon aria-hidden className="size-4" />
|
|
60
|
+
</Button>
|
|
61
|
+
})}
|
|
62
|
+
</div>
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
export function ColorPlayground({ initialSeeds, initialActionShades, initialView = 'compare', logo, onStudioChange }: {
|
|
66
|
+
initialSeeds?: ColorStudio['seeds']
|
|
67
|
+
initialActionShades?: { light?: Shade; dark?: Shade }
|
|
68
|
+
initialView?: 'light' | 'dark' | 'compare'
|
|
69
|
+
logo?: string | null
|
|
70
|
+
onStudioChange?: (studio: ColorStudio, valid: boolean) => void
|
|
71
|
+
} = {}) {
|
|
72
|
+
const t = useT()
|
|
73
|
+
const id = React.useId()
|
|
74
|
+
const [color, setColor] = React.useState(initialSeeds?.primary ?? INITIAL_COLOR)
|
|
75
|
+
const [draft, setDraft] = React.useState(initialSeeds?.primary ?? INITIAL_COLOR)
|
|
76
|
+
const [harmony, setHarmony] = React.useState<Harmony>('triadic')
|
|
77
|
+
const [supporting, setSupporting] = React.useState<{ secondary?: string; tertiary?: string }>(initialSeeds ? { secondary: initialSeeds.secondary, tertiary: initialSeeds.tertiary } : {})
|
|
78
|
+
const [supportingDrafts, setSupportingDrafts] = React.useState<{ secondary?: string; tertiary?: string }>(initialSeeds ? { secondary: initialSeeds.secondary, tertiary: initialSeeds.tertiary } : {})
|
|
79
|
+
const [neutral, setNeutral] = React.useState<NeutralTone>('neutral')
|
|
80
|
+
const [view, setView] = React.useState<'light' | 'dark' | 'compare'>(initialView)
|
|
81
|
+
const [actionShades, setActionShades] = React.useState<{ light?: Shade; dark?: Shade }>(initialActionShades ?? {})
|
|
82
|
+
const [hostTokens, setHostTokens] = React.useState<ThemeTokens | null>(null)
|
|
83
|
+
const [matrixForeground, setMatrixForeground] = React.useState<Shade>(900)
|
|
84
|
+
const [matrixBackground, setMatrixBackground] = React.useState<Shade>(50)
|
|
85
|
+
const [draggedRole, setDraggedRole] = React.useState<ColorRole | null>(null)
|
|
86
|
+
const [dropRole, setDropRole] = React.useState<ColorRole | null>(null)
|
|
87
|
+
const [feedback, setFeedback] = React.useState('')
|
|
88
|
+
const [resetVersion, setResetVersion] = React.useState(0)
|
|
89
|
+
const invalid = !normalizeHex(draft)
|
|
90
|
+
const hasManualColors = Object.keys(supportingDrafts).length > 0
|
|
91
|
+
const studio = React.useMemo(() => createColorStudio(color, neutral, actionShades, { harmony, ...supporting }), [color, neutral, actionShades, harmony, supporting])
|
|
92
|
+
|
|
93
|
+
React.useEffect(() => {
|
|
94
|
+
onStudioChange?.(studio, !invalid && Object.values(supportingDrafts).every(value => Boolean(normalizeHex(value))))
|
|
95
|
+
}, [studio, invalid, supportingDrafts, onStudioChange])
|
|
96
|
+
|
|
97
|
+
const matrixText = studio.scale.find(stop => stop.step === matrixForeground)!.hex
|
|
98
|
+
const matrixSurface = studio.scale.find(stop => stop.step === matrixBackground)!.hex
|
|
99
|
+
const matrixRatio = contrastRatio(matrixText, matrixSurface)
|
|
100
|
+
|
|
101
|
+
React.useEffect(() => { setHostTokens(readThemeTokens(document.styleSheets)) }, [])
|
|
102
|
+
|
|
103
|
+
function chooseColor(value: string) {
|
|
104
|
+
setDraft(value)
|
|
105
|
+
const normalized = normalizeHex(value)
|
|
106
|
+
if (normalized) setColor(normalized)
|
|
107
|
+
setFeedback('')
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
function swapColors(first: keyof ColorStudio['seeds'], second: keyof ColorStudio['seeds']) {
|
|
111
|
+
if (invalid || Object.values(supportingDrafts).some(value => !normalizeHex(value))) return
|
|
112
|
+
const next = { ...studio.seeds, [first]: studio.seeds[second], [second]: studio.seeds[first] }
|
|
113
|
+
setColor(next.primary)
|
|
114
|
+
setDraft(next.primary)
|
|
115
|
+
setSupporting({ secondary: next.secondary, tertiary: next.tertiary })
|
|
116
|
+
setSupportingDrafts({ secondary: next.secondary, tertiary: next.tertiary })
|
|
117
|
+
if (first === 'primary' || second === 'primary') setActionShades({})
|
|
118
|
+
setFeedback(t('design_system.colorStudio.swapped'))
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
function reset() {
|
|
122
|
+
chooseColor(INITIAL_COLOR)
|
|
123
|
+
setNeutral('neutral')
|
|
124
|
+
setHarmony('triadic')
|
|
125
|
+
setSupporting({})
|
|
126
|
+
setSupportingDrafts({})
|
|
127
|
+
setView(initialView)
|
|
128
|
+
setActionShades({})
|
|
129
|
+
setMatrixForeground(900)
|
|
130
|
+
setMatrixBackground(50)
|
|
131
|
+
setResetVersion(previous => previous + 1)
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
async function copy(value: string, message: string) {
|
|
135
|
+
try {
|
|
136
|
+
await navigator.clipboard.writeText(value)
|
|
137
|
+
setFeedback(message)
|
|
138
|
+
} catch { setFeedback(t('design_system.colorPreview.copyFailed')) }
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
const swapDisabled = invalid || Object.values(supportingDrafts).some(value => !normalizeHex(value))
|
|
142
|
+
function dragStart(role: ColorRole, event: React.DragEvent) {
|
|
143
|
+
if (swapDisabled) { event.preventDefault(); return }
|
|
144
|
+
event.dataTransfer.effectAllowed = 'move'
|
|
145
|
+
event.dataTransfer.setData('text/plain', role)
|
|
146
|
+
setDraggedRole(role)
|
|
147
|
+
}
|
|
148
|
+
function dragEnd() { setDraggedRole(null); setDropRole(null) }
|
|
149
|
+
function dropProps(role: ColorRole) {
|
|
150
|
+
return {
|
|
151
|
+
'data-color-role': role,
|
|
152
|
+
className: `space-y-2 rounded-lg transition-shadow ${dropRole === role ? 'ring-2 ring-ring' : ''}`,
|
|
153
|
+
onDragOver: (event: React.DragEvent) => {
|
|
154
|
+
if (!draggedRole || draggedRole === role || swapDisabled) return
|
|
155
|
+
event.preventDefault()
|
|
156
|
+
event.dataTransfer.dropEffect = 'move'
|
|
157
|
+
setDropRole(role)
|
|
158
|
+
},
|
|
159
|
+
onDragLeave: (event: React.DragEvent<HTMLDivElement>) => {
|
|
160
|
+
if (!event.currentTarget.contains(event.relatedTarget as Node | null)) setDropRole(null)
|
|
161
|
+
},
|
|
162
|
+
onDrop: (event: React.DragEvent) => {
|
|
163
|
+
event.preventDefault()
|
|
164
|
+
if (draggedRole && draggedRole !== role && !swapDisabled) swapColors(draggedRole, role)
|
|
165
|
+
dragEnd()
|
|
166
|
+
},
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
return <section className="space-y-8" aria-labelledby={`${id}-title`} data-example="color-playground">
|
|
171
|
+
<header className="flex flex-wrap items-start justify-between gap-4">
|
|
172
|
+
<div className="max-w-2xl space-y-2">
|
|
173
|
+
<h2 id={`${id}-title`} className="text-2xl font-medium tracking-tight">{t('design_system.colorStudio.title')}</h2>
|
|
174
|
+
<p className="text-sm leading-relaxed text-muted-foreground">{t('design_system.colorStudio.description')}</p>
|
|
175
|
+
</div>
|
|
176
|
+
<Button type="button" variant="ghost" onClick={reset}><RotateCcw aria-hidden />{t('design_system.colorPreview.reset')}</Button>
|
|
177
|
+
</header>
|
|
178
|
+
|
|
179
|
+
<div className="space-y-6 rounded-xl border border-border bg-card p-5 sm:p-6">
|
|
180
|
+
<div className="grid gap-5 sm:grid-cols-3">
|
|
181
|
+
<div {...dropProps('primary')}>
|
|
182
|
+
<div className="flex items-center justify-between gap-2">
|
|
183
|
+
<Label htmlFor={`${id}-hex`}>{t('design_system.colorStudio.primary')}</Label>
|
|
184
|
+
<ColorRoleSwap role="primary" disabled={swapDisabled} onSwap={swapColors} onDragStart={event => dragStart('primary', event)} onDragEnd={dragEnd} />
|
|
185
|
+
</div>
|
|
186
|
+
<div className="flex items-center gap-2">
|
|
187
|
+
<Input type="color" value={color} aria-label={t('design_system.colorPreview.color')} onChange={event => chooseColor(event.target.value)} className="w-12 shrink-0 p-1" inputClassName="h-full w-full cursor-pointer p-0" />
|
|
188
|
+
<Input id={`${id}-hex`} aria-label={t('design_system.colorPreview.hex')} value={draft} onChange={event => chooseColor(event.target.value)} onBlur={() => { const value = normalizeHex(draft); if (value) setDraft(value) }} inputClassName="font-mono" aria-invalid={invalid} aria-describedby={invalid ? `${id}-error` : undefined} spellCheck={false} maxLength={7} />
|
|
189
|
+
</div>
|
|
190
|
+
</div>
|
|
191
|
+
{(['secondary', 'tertiary'] as const).map(role => {
|
|
192
|
+
const value = supportingDrafts[role] ?? studio.seeds[role]
|
|
193
|
+
const valid = Boolean(normalizeHex(value))
|
|
194
|
+
const change = (next: string) => {
|
|
195
|
+
setSupportingDrafts(previous => ({ ...previous, [role]: next }))
|
|
196
|
+
const normalized = normalizeHex(next)
|
|
197
|
+
if (normalized) setSupporting(previous => ({ ...previous, [role]: normalized }))
|
|
198
|
+
setFeedback('')
|
|
199
|
+
}
|
|
200
|
+
return <div key={role} {...dropProps(role)}>
|
|
201
|
+
<div className="flex items-center justify-between gap-2">
|
|
202
|
+
<Label htmlFor={`${id}-${role}`}>{t(`design_system.colorStudio.${role}`)}</Label>
|
|
203
|
+
<ColorRoleSwap role={role} disabled={swapDisabled} onSwap={swapColors} onDragStart={event => dragStart(role, event)} onDragEnd={dragEnd} />
|
|
204
|
+
</div>
|
|
205
|
+
<div className="flex items-center gap-2">
|
|
206
|
+
<Input type="color" value={studio.seeds[role]} aria-label={t(`design_system.colorStudio.${role}`)} onChange={event => change(event.target.value)} className="w-12 shrink-0 p-1" inputClassName="h-full w-full cursor-pointer p-0" />
|
|
207
|
+
<Input id={`${id}-${role}`} value={value} aria-label={`${t(`design_system.colorStudio.${role}`)} HEX`} onChange={event => change(event.target.value)} inputClassName="font-mono" aria-invalid={!valid} aria-describedby={!valid ? `${id}-${role}-error` : undefined} maxLength={7} spellCheck={false} />
|
|
208
|
+
</div>
|
|
209
|
+
{!valid ? <p id={`${id}-${role}-error`} role="alert" className="text-xs text-status-error-text">{t(`design_system.colorStudio.${role}Invalid`)}</p> : null}
|
|
210
|
+
</div>
|
|
211
|
+
})}
|
|
212
|
+
</div>
|
|
213
|
+
<div className="grid gap-5 sm:grid-cols-2 xl:grid-cols-4">
|
|
214
|
+
<div className="space-y-2">
|
|
215
|
+
<Label htmlFor={`${id}-harmony`}>{t('design_system.colorStudio.harmony')}</Label>
|
|
216
|
+
<Select value={harmony} onValueChange={value => { if (value === 'analogous' || value === 'triadic' || value === 'split') { setHarmony(value); setSupporting({}); setSupportingDrafts({}); setFeedback('') } }}>
|
|
217
|
+
<SelectTrigger id={`${id}-harmony`} aria-label={t('design_system.colorStudio.harmony')} aria-describedby={`${id}-harmony-description`}><SelectValue /></SelectTrigger>
|
|
218
|
+
<SelectContent>{(['analogous', 'triadic', 'split'] as const).map(mode => <SelectItem key={mode} value={mode}>{t(`design_system.colorStudio.harmony.${mode}`)}</SelectItem>)}</SelectContent>
|
|
219
|
+
</Select>
|
|
220
|
+
<p id={`${id}-harmony-description`} className="text-xs leading-relaxed text-muted-foreground">{t(`design_system.colorStudio.harmonyDescription.${harmony}`)}</p>
|
|
221
|
+
</div>
|
|
222
|
+
<div className="space-y-2">
|
|
223
|
+
<Label htmlFor={`${id}-neutral`}>{t('design_system.colorStudio.neutral')}</Label>
|
|
224
|
+
<Select value={neutral} onValueChange={value => { if (value === 'neutral' || value === 'warm' || value === 'cool') { setNeutral(value); setFeedback('') } }}>
|
|
225
|
+
<SelectTrigger id={`${id}-neutral`} aria-label={t('design_system.colorStudio.neutral')}><SelectValue /></SelectTrigger>
|
|
226
|
+
<SelectContent>{(['neutral', 'warm', 'cool'] as const).map(tone => <SelectItem key={tone} value={tone}>{t(`design_system.colorStudio.neutral.${tone}`)}</SelectItem>)}</SelectContent>
|
|
227
|
+
</Select>
|
|
228
|
+
</div>
|
|
229
|
+
{THEMES.map(theme => <ShadeSelect key={theme} label={t(`design_system.colorStudio.${theme}Action`)} scale={studio.scale} value={studio[theme].actionStep} onChange={shade => { setActionShades(previous => ({ ...previous, [theme]: shade })); setFeedback('') }} />)}
|
|
230
|
+
</div>
|
|
231
|
+
<div className="flex flex-wrap items-center justify-between gap-4 border-t border-border pt-4">
|
|
232
|
+
<div className="max-w-2xl space-y-2">
|
|
233
|
+
<p className="text-sm leading-relaxed">{t('design_system.colorStudio.harmonyHint')}</p>
|
|
234
|
+
<p role="status" className="text-xs leading-relaxed text-muted-foreground">{t(`design_system.colorStudio.${hasManualColors ? 'harmonyManual' : 'harmonyAutomatic'}`)}</p>
|
|
235
|
+
</div>
|
|
236
|
+
{hasManualColors ? <Button type="button" size="sm" variant="secondary" onClick={() => { setSupporting({}); setSupportingDrafts({}); setFeedback(t('design_system.colorStudio.harmonyRestored')) }}><RotateCcw aria-hidden />{t('design_system.colorStudio.auto')}</Button> : null}
|
|
237
|
+
</div>
|
|
238
|
+
{invalid ? <p id={`${id}-error`} role="alert" className="text-sm text-status-error-text">{t('design_system.colorPreview.invalid')}</p> : null}
|
|
239
|
+
<div className="flex flex-wrap gap-2">{PRESETS.map(preset => <Button type="button" key={preset} variant="ghost" size="sm" onClick={() => chooseColor(preset)} aria-pressed={color === preset}>
|
|
240
|
+
<span aria-hidden className="size-4 shrink-0 rounded-full border border-border" style={{ backgroundColor: preset }} /><span className="font-mono text-xs">{preset}</span>{color === preset ? <Check className="size-3" aria-hidden /> : null}
|
|
241
|
+
</Button>)}</div>
|
|
242
|
+
<div className="space-y-3 border-t border-border pt-5">
|
|
243
|
+
<div className="space-y-1"><h3 className="text-sm font-medium">{t('design_system.colorStudio.scale')}</h3><p className="text-xs leading-relaxed text-muted-foreground">{t('design_system.colorStudio.scaleHint')}</p></div>
|
|
244
|
+
{([
|
|
245
|
+
{ role: 'primary', scale: studio.scale, seed: studio.seeds.primary },
|
|
246
|
+
{ role: 'secondary', scale: studio.secondaryScale, seed: studio.seeds.secondary },
|
|
247
|
+
{ role: 'tertiary', scale: studio.tertiaryScale, seed: studio.seeds.tertiary },
|
|
248
|
+
] as const).map(({ role, scale, seed }) => <div key={role} className="space-y-2">
|
|
249
|
+
<h4 className="text-xs font-medium text-muted-foreground">{t(`design_system.colorStudio.${role}`)}</h4>
|
|
250
|
+
<div className="grid grid-cols-3 gap-1 sm:grid-cols-6 xl:grid-cols-11">
|
|
251
|
+
{scale.map(stop => <Button type="button" key={stop.step} variant="ghost" className="h-24 min-w-0 flex-col items-start justify-end gap-1 rounded-lg px-2 py-3 text-left" style={{ backgroundColor: stop.hex, color: contrastRatio(stop.hex, '#FFFFFF') >= contrastRatio(stop.hex, '#000000') ? '#FFFFFF' : '#000000' }} aria-label={`${t('design_system.colorStudio.copyColor')} ${t(`design_system.colorStudio.${role}`)} ${stop.step} ${stop.hex}`} onClick={() => copy(stop.hex, `${t('design_system.colorStudio.copiedColor')}: ${stop.hex}`)}>
|
|
252
|
+
<span className="flex w-full items-center justify-between gap-1 text-xs"><span className="font-semibold">{stop.step}</span>{stop.hex === seed ? <span title={t('design_system.colorStudio.base')}><Paintbrush className="size-3" aria-hidden /><span className="sr-only">{t('design_system.colorStudio.base')}</span></span> : null}</span>
|
|
253
|
+
<code className="text-xs font-normal">{stop.hex}</code>
|
|
254
|
+
</Button>)}
|
|
255
|
+
</div>
|
|
256
|
+
</div>)}
|
|
257
|
+
</div>
|
|
258
|
+
</div>
|
|
259
|
+
|
|
260
|
+
<section className="space-y-4" aria-labelledby={`${id}-preview-title`}>
|
|
261
|
+
<div className="flex flex-wrap items-end justify-between gap-4">
|
|
262
|
+
<div className="max-w-xl space-y-1"><h3 id={`${id}-preview-title`} className="text-lg font-medium">{t('design_system.colorStudio.preview')}</h3><p className="text-xs leading-relaxed text-muted-foreground">{t('design_system.colorStudio.previewHint')}</p></div>
|
|
263
|
+
<SegmentedControl value={view} onValueChange={value => { if (value === 'light' || value === 'dark' || value === 'compare') setView(value) }} aria-label={t('design_system.colorPreview.theme')}>
|
|
264
|
+
<SegmentedControlItem value="light">{t('design_system.colorPreview.light')}</SegmentedControlItem>
|
|
265
|
+
<SegmentedControlItem value="dark">{t('design_system.colorPreview.dark')}</SegmentedControlItem>
|
|
266
|
+
<SegmentedControlItem value="compare">{t('design_system.colorStudio.compare')}</SegmentedControlItem>
|
|
267
|
+
</SegmentedControl>
|
|
268
|
+
</div>
|
|
269
|
+
<div className={view === 'compare' ? 'grid min-w-0 gap-5 xl:grid-cols-2' : 'min-w-0'}>
|
|
270
|
+
{THEMES.filter(theme => view === 'compare' || theme === view).map(theme => <div key={theme} className="min-w-0 space-y-3">
|
|
271
|
+
<p className="flex items-center gap-2 text-xs font-medium text-muted-foreground">{theme === 'light' ? <Sun className="size-4" aria-hidden /> : <Moon className="size-4" aria-hidden />}{t(`design_system.colorPreview.${theme}`)}</p>
|
|
272
|
+
<ColorStudioCompositions theme={theme} tokens={{ ...hostTokens?.[theme], ...studio[theme].tokens }} />
|
|
273
|
+
<ColorStudioSpecimen logo={logo} key={`${theme}-${resetVersion}`} theme={theme} tokens={{ ...hostTokens?.[theme], ...studio[theme].tokens }} />
|
|
274
|
+
</div>)}
|
|
275
|
+
</div>
|
|
276
|
+
</section>
|
|
277
|
+
|
|
278
|
+
<section className="space-y-4" aria-labelledby={`${id}-roles-title`}>
|
|
279
|
+
<h3 id={`${id}-roles-title`} className="text-lg font-medium">{t('design_system.colorStudio.roles')}</h3>
|
|
280
|
+
<div className="grid gap-5 sm:grid-cols-2 xl:grid-cols-4">{(Object.keys(ROLE_ICONS) as (keyof typeof ROLE_ICONS)[]).map(role => {
|
|
281
|
+
const Icon = ROLE_ICONS[role]
|
|
282
|
+
return <div key={role} className="space-y-3 border-t border-border pt-4"><Icon aria-hidden className="size-5 text-muted-foreground" /><h4 className="text-sm font-medium">{t(`design_system.colorStudio.roles.${role}`)}</h4><p className="text-xs leading-relaxed text-muted-foreground">{t(`design_system.colorStudio.roles.${role}Hint`)}</p></div>
|
|
283
|
+
})}</div>
|
|
284
|
+
</section>
|
|
285
|
+
|
|
286
|
+
<section className="space-y-4" aria-labelledby={`${id}-contrast-title`}>
|
|
287
|
+
<div className="max-w-3xl space-y-1"><h3 id={`${id}-contrast-title`} className="text-lg font-medium">{t('design_system.colorStudio.contrast')}</h3><p className="text-xs leading-relaxed text-muted-foreground">{t('design_system.colorStudio.contrastHint')}</p></div>
|
|
288
|
+
<div className="grid gap-5 rounded-xl border border-border p-5 lg:grid-cols-2">
|
|
289
|
+
<div className="space-y-4">
|
|
290
|
+
<div className="space-y-1"><h4 className="text-sm font-medium">{t('design_system.colorStudio.matrix')}</h4><p className="text-xs leading-relaxed text-muted-foreground">{t('design_system.colorStudio.matrixHint')}</p></div>
|
|
291
|
+
<div className="grid grid-cols-2 gap-3"><ShadeSelect label={t('design_system.colorStudio.foreground')} scale={studio.scale} value={matrixForeground} onChange={setMatrixForeground} /><ShadeSelect label={t('design_system.colorStudio.background')} scale={studio.scale} value={matrixBackground} onChange={setMatrixBackground} /></div>
|
|
292
|
+
</div>
|
|
293
|
+
<div className="flex items-center justify-between gap-4 rounded-lg p-6" style={{ backgroundColor: matrixSurface, color: matrixText }}>
|
|
294
|
+
<div className="space-y-2"><span className="text-4xl font-medium">Aa</span><p className="text-sm">{t('design_system.colorStudio.pair.body')}</p></div>
|
|
295
|
+
<div className="space-y-2 rounded-md bg-card px-3 py-2 text-right text-card-foreground"><p className="font-mono text-xl font-semibold">{matrixRatio.toFixed(2)}:1</p><p className="text-xs">{t(`design_system.colorStudio.${matrixRatio >= 4.5 ? 'pass' : 'fail'}`)}</p></div>
|
|
296
|
+
</div>
|
|
297
|
+
</div>
|
|
298
|
+
<div className="overflow-hidden rounded-xl border border-border">
|
|
299
|
+
<div className="grid grid-cols-3 gap-3 bg-muted px-4 py-3 text-xs font-medium text-muted-foreground"><span>{t('design_system.colorStudio.pair')}</span>{THEMES.map(theme => <span key={theme}>{t(`design_system.colorPreview.${theme}`)}</span>)}</div>
|
|
300
|
+
<dl className="divide-y divide-border">{studio.light.pairs.map(pair => <div key={pair.id} className="grid grid-cols-3 items-center gap-3 px-4 py-3">
|
|
301
|
+
<dt className="space-y-1"><p className="text-sm font-medium">{t(`design_system.colorStudio.pair.${pair.id}`)}</p><p className="text-xs text-muted-foreground">{t('design_system.colorStudio.minimum')}: {pair.minimum}:1</p></dt>
|
|
302
|
+
{THEMES.map(theme => {
|
|
303
|
+
const item = studio[theme].pairs.find(candidate => candidate.id === pair.id)!
|
|
304
|
+
const passing = item.ratio >= item.minimum
|
|
305
|
+
return <dd key={theme} className="flex flex-wrap items-center gap-3">
|
|
306
|
+
<span aria-hidden className="inline-flex size-10 shrink-0 items-center justify-center rounded-md border border-border font-semibold" style={{ backgroundColor: item.background, color: item.foreground }}>Aa</span>
|
|
307
|
+
<div className="space-y-1"><p className="font-mono text-sm">{item.ratio.toFixed(2)}:1</p><Badge variant={passing ? 'success' : 'error'}>{t(`design_system.colorStudio.${passing ? 'pass' : 'fail'}`)}</Badge><p className="font-mono text-xs text-muted-foreground">{item.foreground}<br />{item.background}</p></div>
|
|
308
|
+
</dd>
|
|
309
|
+
})}
|
|
310
|
+
</div>)}</dl>
|
|
311
|
+
</div>
|
|
312
|
+
</section>
|
|
313
|
+
|
|
314
|
+
<section className="space-y-4 border-t border-border pt-6" aria-labelledby={`${id}-export-title`}>
|
|
315
|
+
<div className="flex flex-wrap items-center justify-between gap-4">
|
|
316
|
+
<div className="max-w-xl space-y-1"><h3 id={`${id}-export-title`} className="text-lg font-medium">{t('design_system.colorStudio.export')}</h3><p className="text-xs leading-relaxed text-muted-foreground">{t('design_system.colorStudio.exportHint')}</p></div>
|
|
317
|
+
<div className="flex flex-wrap gap-2">{(['css', 'json'] as const).map(format => <Button type="button" key={format} variant="secondary" onClick={() => copy(exportColorStudio(studio, format), t('design_system.colorPreview.copied'))}><Copy aria-hidden />{t(`design_system.colorStudio.${format === 'css' ? 'copyCss' : 'copyJson'}`)}</Button>)}</div>
|
|
318
|
+
</div>
|
|
319
|
+
<div className="max-w-3xl space-y-1"><h4 className="text-sm font-medium">{t('design_system.colorStudio.algorithm')}</h4><p className="text-xs leading-relaxed text-muted-foreground">{t('design_system.colorStudio.algorithmHint')}</p></div>
|
|
320
|
+
<section className="rounded-lg border border-border"><h3 className="p-4 text-sm font-medium">{t('design_system.colorStudio.showTokens')}</h3><pre className="max-h-80 overflow-auto border-t border-border bg-muted p-4 text-xs leading-relaxed"><code>{exportColorStudio(studio, 'css')}</code></pre></section>
|
|
321
|
+
</section>
|
|
322
|
+
<p role="status" aria-live="polite" className="sticky bottom-4 z-10 w-fit rounded-lg bg-popover px-3 py-2 text-xs text-popover-foreground empty:hidden">{feedback}</p>
|
|
323
|
+
</section>
|
|
324
|
+
}
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
'use client'
|
|
2
|
+
|
|
3
|
+
import * as React from 'react'
|
|
4
|
+
import { ArrowUpRight, Check, Clock3, Heart, Layers, MoveUpRight, Palette, Sparkles } from 'lucide-react'
|
|
5
|
+
import { useT } from '@open-mercato/shared/lib/i18n/context'
|
|
6
|
+
import { Avatar, AvatarStack } from '@open-mercato/ui/primitives/avatar'
|
|
7
|
+
import { Button } from '@open-mercato/ui/primitives/button'
|
|
8
|
+
import { IconButton } from '@open-mercato/ui/primitives/icon-button'
|
|
9
|
+
import { Checkbox } from '@open-mercato/ui/primitives/checkbox'
|
|
10
|
+
import product from '../assets/style-studio-product.png'
|
|
11
|
+
import portrait from '../assets/landing-teams-01.png'
|
|
12
|
+
import creator from '../assets/contact-support-portrait.png'
|
|
13
|
+
import workspace from '../assets/blog-photo-0.png'
|
|
14
|
+
|
|
15
|
+
const assetSource = (asset: string | { readonly src: string }) => typeof asset === 'string' ? asset : asset.src
|
|
16
|
+
const CARD = 'flex min-h-112 min-w-0 flex-col overflow-hidden rounded-xl border border-border shadow-sm'
|
|
17
|
+
const TASKS = ['collect', 'explore', 'share'] as const
|
|
18
|
+
const CATEGORIES = ['identity', 'digital', 'editorial'] as const
|
|
19
|
+
|
|
20
|
+
export function ColorStudioCompositions({ theme, tokens }: { theme: 'light' | 'dark'; tokens: Record<string, string> }) {
|
|
21
|
+
const t = useT()
|
|
22
|
+
const id = React.useId()
|
|
23
|
+
const copy = (key: string) => t(`design_system.colorStudio.compositions.${key}`)
|
|
24
|
+
const [plan, setPlan] = React.useState('solo')
|
|
25
|
+
const [category, setCategory] = React.useState<typeof CATEGORIES[number]>('identity')
|
|
26
|
+
const [completed, setCompleted] = React.useState<string[]>(['collect'])
|
|
27
|
+
const [saved, setSaved] = React.useState(false)
|
|
28
|
+
const [following, setFollowing] = React.useState(false)
|
|
29
|
+
const [productSaved, setProductSaved] = React.useState(false)
|
|
30
|
+
const primary = { backgroundColor: tokens['--primary'], color: tokens['--primary-foreground'] }
|
|
31
|
+
const secondary = { backgroundColor: tokens['--studio-secondary-soft'], color: tokens['--studio-secondary-soft-foreground'] }
|
|
32
|
+
const tertiary = { backgroundColor: tokens['--studio-tertiary'], color: tokens['--studio-tertiary-foreground'] }
|
|
33
|
+
const gradient = `linear-gradient(145deg, ${tokens['--accent']}, ${tokens['--studio-secondary-soft']}, ${tokens['--studio-tertiary-soft']})`
|
|
34
|
+
const appointments = [
|
|
35
|
+
{ key: 'meeting', time: '09:00–10:00', background: tokens['--accent'], foreground: tokens['--accent-foreground'], accent: tokens['--primary'] },
|
|
36
|
+
{ key: 'research', time: '11:00–12:30', background: tokens['--studio-secondary-soft'], foreground: tokens['--studio-secondary-soft-foreground'], accent: tokens['--studio-secondary'] },
|
|
37
|
+
{ key: 'review', time: '14:00–15:00', background: tokens['--studio-tertiary-soft'], foreground: tokens['--studio-tertiary-soft-foreground'], accent: tokens['--studio-tertiary'] },
|
|
38
|
+
]
|
|
39
|
+
return <section aria-labelledby={`${id}-title`} data-composition-theme={theme} style={tokens as React.CSSProperties} className="@container/cards min-w-0 space-y-6 rounded-xl bg-background p-4 text-foreground sm:p-6">
|
|
40
|
+
<div className="flex items-end justify-between gap-4"><h4 id={`${id}-title`} className="text-2xl font-medium tracking-tight">{copy('galleryTitle')}</h4><p className="text-xs text-muted-foreground">{copy('live')}</p></div>
|
|
41
|
+
<div className="grid min-w-0 auto-rows-fr gap-6 @md/cards:grid-cols-2 @7xl/cards:grid-cols-4">
|
|
42
|
+
<article className={`${CARD} gap-6 bg-card p-6`} aria-label={t('design_system.colorStudio.agenda')}>
|
|
43
|
+
<div className="space-y-2"><Clock3 aria-hidden className="size-5 text-muted-foreground" /><h5 className="text-xl font-medium tracking-tight">{t('design_system.colorStudio.agenda')}</h5><p className="text-xs text-muted-foreground">{copy('today')}</p></div>
|
|
44
|
+
<div className="flex flex-1 flex-col gap-3">{appointments.map(item => <div key={item.key} className="flex-1 space-y-2 rounded-lg border-l-4 px-4 py-3" style={{ backgroundColor: item.background, color: item.foreground, borderLeftColor: item.accent }}>
|
|
45
|
+
<p className="text-sm font-semibold">{t(`design_system.colorStudio.${item.key}`)}</p><p className="text-xs">{item.time}</p>
|
|
46
|
+
</div>)}</div>
|
|
47
|
+
<AvatarStack size="xs"><Avatar label={copy('creatorName')} src={assetSource(creator)} /><Avatar label={copy('designerName')} src={assetSource(portrait)} /><Avatar label={copy('collaboratorName')} src={assetSource(workspace)} /></AvatarStack>
|
|
48
|
+
</article>
|
|
49
|
+
|
|
50
|
+
<article className={`${CARD} relative justify-between border-transparent p-6`} style={secondary} aria-label={copy('poster')}>
|
|
51
|
+
<div className="relative -mx-6 -mt-6 h-56 overflow-hidden" aria-hidden>
|
|
52
|
+
<div className="absolute inset-6 rounded-full border-8" style={{ borderColor: tokens['--studio-secondary'] }} />
|
|
53
|
+
<img src={assetSource(portrait)} alt="" className="relative mx-auto h-full w-full object-contain object-bottom" loading="lazy" />
|
|
54
|
+
<div className="absolute -right-8 -top-8 size-40 rounded-full border-8" style={{ borderColor: tokens['--studio-tertiary'] }} />
|
|
55
|
+
</div>
|
|
56
|
+
<div className="relative space-y-4 pt-6"><Sparkles aria-hidden className="size-6" /><h5 className="text-4xl font-semibold leading-tight tracking-tight">{copy('posterTitle')}</h5><p className="text-sm leading-relaxed">{copy('posterDescription')}</p></div>
|
|
57
|
+
</article>
|
|
58
|
+
|
|
59
|
+
<article className={`${CARD} border-transparent bg-card`} aria-label={copy('creator')}>
|
|
60
|
+
<div className="relative min-h-56 flex-1 overflow-hidden" style={{ background: gradient }}><img src={assetSource(creator)} alt={copy('creatorPhoto')} className="absolute inset-0 h-full w-full object-cover" loading="lazy" /><div className="absolute inset-x-0 bottom-0 h-24" style={{ background: `linear-gradient(transparent, ${tokens['--primary']})` }} aria-hidden /></div>
|
|
61
|
+
<div className="space-y-4 p-6" style={primary}><p className="text-xs font-medium uppercase tracking-wider">{copy('creator')}</p><h5 className="text-3xl font-medium leading-tight tracking-tight">{copy('creatorName')}</h5><div className="flex items-center justify-between gap-3"><p className="text-sm">{copy('creatorRole')}</p><IconButton type="button" variant="modifiable" size="lg" aria-label={copy(following ? 'unfollow' : 'follow')} aria-pressed={following} onClick={() => setFollowing(value => !value)} style={{ color: 'inherit' }}>{following ? <Check /> : <ArrowUpRight />}</IconButton></div><p role="status" className="text-xs empty:hidden">{following ? copy('following') : ''}</p></div>
|
|
62
|
+
</article>
|
|
63
|
+
|
|
64
|
+
<article className={`${CARD} justify-between gap-6 p-6`} style={{ background: gradient }} aria-label={copy('product')}>
|
|
65
|
+
<div className="flex items-center justify-between gap-3"><span className="rounded-md bg-card px-3 py-2 text-xs font-medium text-card-foreground">{copy('productLabel')}</span><IconButton type="button" variant="white" size="lg" aria-label={copy(productSaved ? 'removeProduct' : 'saveProduct')} aria-pressed={productSaved} onClick={() => setProductSaved(value => !value)}><Heart className={productSaved ? 'fill-current' : ''} /></IconButton></div>
|
|
66
|
+
<img src={assetSource(product)} alt={copy('productPhoto')} className="h-56 w-full rounded-lg object-contain" loading="lazy" />
|
|
67
|
+
<div className="space-y-2 rounded-lg bg-card p-4 text-card-foreground"><p className="text-xs text-muted-foreground">{copy('productCollection')}</p><h5 className="text-2xl font-medium tracking-tight">{copy('productTitle')}</h5><p role="status" className="text-sm text-muted-foreground">{copy(productSaved ? 'productSaved' : 'productHint')}</p></div>
|
|
68
|
+
</article>
|
|
69
|
+
|
|
70
|
+
<article className={`${CARD} gap-6 bg-card p-6`} aria-label={copy('plans')}>
|
|
71
|
+
<div className="space-y-3"><Layers aria-hidden className="size-6" style={{ color: tokens['--primary'] }} /><h5 className="text-2xl font-medium tracking-tight">{copy('plans')}</h5><p className="text-sm leading-relaxed text-muted-foreground">{copy('plansHint')}</p></div>
|
|
72
|
+
<div className="space-y-3">{['solo', 'team'].map(choice => <Button key={choice} type="button" variant="outline" aria-pressed={plan === choice} onClick={() => setPlan(choice)} className="h-auto w-full justify-between gap-3 whitespace-normal p-4 text-left" style={plan === choice ? { backgroundColor: tokens['--accent'], color: tokens['--accent-foreground'], borderColor: tokens['--primary'] } : undefined}><span className="space-y-1"><span className="block text-base font-medium">{copy(choice)}</span><span className="block text-xs font-normal">{copy(`${choice}Hint`)}</span></span>{plan === choice ? <Check aria-hidden className="size-5 shrink-0" /> : <span className="size-5 shrink-0 rounded-full border border-border" aria-hidden />}</Button>)}</div>
|
|
73
|
+
<div className="mt-auto space-y-3 border-t border-border pt-5"><p role="status" className="text-sm font-medium">{copy(plan === 'solo' ? 'soloSelected' : 'teamSelected')}</p><p className="text-xs leading-relaxed text-muted-foreground">{copy('planNote')}</p></div>
|
|
74
|
+
</article>
|
|
75
|
+
|
|
76
|
+
<article className={`${CARD} gap-6 bg-card p-6`} aria-label={copy('categories')}>
|
|
77
|
+
<div className="space-y-2"><Palette aria-hidden className="size-6 text-muted-foreground" /><h5 className="text-2xl font-medium tracking-tight">{copy('categories')}</h5></div>
|
|
78
|
+
<div className="flex flex-wrap gap-2">{CATEGORIES.map(choice => <Button key={choice} type="button" variant="outline" size="sm" aria-pressed={category === choice} onClick={() => setCategory(choice)} style={category === choice ? tertiary : undefined}>{copy(choice)}</Button>)}</div>
|
|
79
|
+
<div className="relative flex min-h-36 flex-1 items-center justify-center overflow-hidden rounded-lg p-6" style={{ background: gradient }} aria-hidden><div className={`flex size-28 items-center justify-center shadow-lg ${category === 'identity' ? 'rounded-full' : category === 'digital' ? 'rotate-12 rounded-xl' : '-rotate-6 rounded-md'}`} style={category === 'digital' ? secondary : tertiary}>{category === 'identity' ? <Sparkles className="size-12" /> : category === 'digital' ? <Layers className="size-12" /> : <Palette className="size-12" />}</div></div>
|
|
80
|
+
<div role="status" className="space-y-2"><h6 className="text-lg font-medium">{copy(`${category}Title`)}</h6><p className="text-sm leading-relaxed text-muted-foreground">{copy(`${category}Description`)}</p></div>
|
|
81
|
+
</article>
|
|
82
|
+
|
|
83
|
+
<article className={`${CARD} gap-6 bg-card p-6`} aria-label={copy('tasks')}>
|
|
84
|
+
<div className="space-y-2"><p className="text-xs font-medium uppercase tracking-wider text-muted-foreground">{copy('workspace')}</p><h5 className="text-2xl font-medium tracking-tight">{copy('tasks')}</h5></div>
|
|
85
|
+
<p className="text-5xl font-medium tracking-tight" style={{ color: tokens['--primary'] }}>{completed.length}<span className="text-xl text-muted-foreground"> / {TASKS.length}</span></p>
|
|
86
|
+
<div className="flex-1 divide-y divide-border">{TASKS.map(task => <label key={task} className="flex cursor-pointer items-start gap-3 py-4"><Checkbox size="md" checked={completed.includes(task)} onCheckedChange={checked => setCompleted(current => checked ? [...current.filter(item => item !== task), task] : current.filter(item => item !== task))} aria-label={copy(task)} /><span className={`text-sm leading-relaxed ${completed.includes(task) ? 'text-muted-foreground line-through' : ''}`}>{copy(task)}</span></label>)}</div>
|
|
87
|
+
<p role="status" className="rounded-lg p-4 text-sm" style={secondary}>{copy(completed.length === TASKS.length ? 'allDone' : 'keepGoing')}</p>
|
|
88
|
+
</article>
|
|
89
|
+
|
|
90
|
+
<article className={`${CARD} border-transparent`} aria-label={copy('inspiration')}>
|
|
91
|
+
<div className="relative min-h-56 flex-1"><img src={assetSource(workspace)} alt={copy('workspacePhoto')} className="absolute inset-0 h-full w-full object-cover" loading="lazy" /></div>
|
|
92
|
+
<div className="space-y-5 p-6" style={tertiary}><div className="flex items-center justify-between gap-3"><p className="text-xs font-medium uppercase tracking-wider">{copy('inspiration')}</p><MoveUpRight aria-hidden className="size-5" /></div><h5 className="text-3xl font-medium leading-tight tracking-tight">{copy('inspirationTitle')}</h5><Button type="button" variant="outline" aria-pressed={saved} onClick={() => setSaved(value => !value)} className="w-full" style={{ backgroundColor: tokens['--studio-tertiary-soft'], color: tokens['--studio-tertiary-soft-foreground'], borderColor: 'transparent' }}><Heart aria-hidden className={saved ? 'fill-current' : ''} />{copy(saved ? 'saved' : 'save')}</Button></div>
|
|
93
|
+
</article>
|
|
94
|
+
</div>
|
|
95
|
+
</section>
|
|
96
|
+
}
|
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
'use client'
|
|
2
|
+
|
|
3
|
+
import * as React from 'react'
|
|
4
|
+
import type { LegacyColumnDef as ColumnDef } from '@tanstack/react-table/legacy'
|
|
5
|
+
import { Check, FolderKanban, Plus, Search } from 'lucide-react'
|
|
6
|
+
import { useT } from '@open-mercato/shared/lib/i18n/context'
|
|
7
|
+
import { DataTable } from '@open-mercato/ui/backend/DataTable'
|
|
8
|
+
import { Badge } from '@open-mercato/ui/primitives/badge'
|
|
9
|
+
import { Button } from '@open-mercato/ui/primitives/button'
|
|
10
|
+
import { Checkbox } from '@open-mercato/ui/primitives/checkbox'
|
|
11
|
+
import { Input } from '@open-mercato/ui/primitives/input'
|
|
12
|
+
import { Label } from '@open-mercato/ui/primitives/label'
|
|
13
|
+
import { CircularProgress } from '@open-mercato/ui/primitives/progress'
|
|
14
|
+
import { SegmentedControl, SegmentedControlItem } from '@open-mercato/ui/primitives/segmented-control'
|
|
15
|
+
|
|
16
|
+
type Project = {
|
|
17
|
+
id: string
|
|
18
|
+
titleKey: string
|
|
19
|
+
ownerKey: string
|
|
20
|
+
status: 'confirmed' | 'pending' | 'error'
|
|
21
|
+
progress: number
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
const PROJECTS: Project[] = [
|
|
25
|
+
{ id: 'design', titleKey: 'project.design', ownerKey: 'owner.you', status: 'confirmed', progress: 72 },
|
|
26
|
+
{ id: 'website', titleKey: 'project.website', ownerKey: 'owner.team', status: 'pending', progress: 38 },
|
|
27
|
+
{ id: 'mobile', titleKey: 'project.mobile', ownerKey: 'owner.review', status: 'error', progress: 16 },
|
|
28
|
+
]
|
|
29
|
+
|
|
30
|
+
const STATUS_VARIANTS = { confirmed: 'success', pending: 'warning', error: 'error' } as const
|
|
31
|
+
|
|
32
|
+
export function ColorStudioSpecimen({ theme, tokens, logo }: { logo?: string | null; theme: 'light' | 'dark'; tokens: Record<string, string> }) {
|
|
33
|
+
const t = useT()
|
|
34
|
+
const id = React.useId()
|
|
35
|
+
const [view, setView] = React.useState('projects')
|
|
36
|
+
const [search, setSearch] = React.useState('')
|
|
37
|
+
const [projects, setProjects] = React.useState(PROJECTS)
|
|
38
|
+
const [selectedId, setSelectedId] = React.useState('design')
|
|
39
|
+
const [workspaceName, setWorkspaceName] = React.useState('')
|
|
40
|
+
const [savedName, setSavedName] = React.useState('')
|
|
41
|
+
const [notifications, setNotifications] = React.useState(true)
|
|
42
|
+
const [savedNotifications, setSavedNotifications] = React.useState(true)
|
|
43
|
+
const [feedback, setFeedback] = React.useState<'saved' | 'added' | null>(null)
|
|
44
|
+
const selectedProject = projects.find(project => project.id === selectedId) ?? projects[0]
|
|
45
|
+
const hasNewProject = projects.some(project => project.id === 'new')
|
|
46
|
+
const visibleProjects = projects.filter(project => `${t(`design_system.colorStudio.${project.titleKey}`)} ${t(`design_system.colorStudio.${project.ownerKey}`)}`.toLocaleLowerCase().includes(search.trim().toLocaleLowerCase()))
|
|
47
|
+
const columns = React.useMemo<ColumnDef<Project, unknown>[]>(() => [
|
|
48
|
+
{
|
|
49
|
+
id: 'project',
|
|
50
|
+
header: t('design_system.colorStudio.projectName'),
|
|
51
|
+
meta: { truncate: false },
|
|
52
|
+
cell: ({ row }) => <Button
|
|
53
|
+
type="button"
|
|
54
|
+
variant="secondary"
|
|
55
|
+
size="sm"
|
|
56
|
+
aria-pressed={row.original.id === selectedId}
|
|
57
|
+
onClick={() => setSelectedId(row.original.id)}
|
|
58
|
+
className="h-auto w-full justify-start gap-3 whitespace-normal rounded-md px-0 py-1 text-left shadow-none"
|
|
59
|
+
style={{ backgroundColor: 'transparent', color: tokens['--foreground'] }}
|
|
60
|
+
>
|
|
61
|
+
<span className="flex size-8 shrink-0 items-center justify-center rounded-md" style={{ backgroundColor: row.original.id === selectedId ? tokens['--accent'] : tokens['--muted'], color: row.original.id === selectedId ? tokens['--accent-foreground'] : tokens['--muted-foreground'] }}>{row.original.id === selectedId ? <Check className="size-4" aria-hidden /> : <FolderKanban className="size-4" aria-hidden />}</span>
|
|
62
|
+
<span className="min-w-0 space-y-1">
|
|
63
|
+
<span className="block text-sm font-medium">{t(`design_system.colorStudio.${row.original.titleKey}`)}</span>
|
|
64
|
+
<span className="block text-xs font-normal text-muted-foreground">{t(`design_system.colorStudio.${row.original.ownerKey}`)}</span>
|
|
65
|
+
</span>
|
|
66
|
+
</Button>,
|
|
67
|
+
},
|
|
68
|
+
{
|
|
69
|
+
id: 'status',
|
|
70
|
+
header: t('design_system.colorStudio.status'),
|
|
71
|
+
meta: { truncate: false },
|
|
72
|
+
cell: ({ row }) => <Badge variant={STATUS_VARIANTS[row.original.status]} dot>{t(`design_system.colorPreview.${row.original.status}`)}</Badge>,
|
|
73
|
+
},
|
|
74
|
+
], [selectedId, t, tokens])
|
|
75
|
+
|
|
76
|
+
function addProject() {
|
|
77
|
+
if (hasNewProject) return
|
|
78
|
+
setProjects(current => [...current, { id: 'new', titleKey: 'newProject', ownerKey: 'owner.you', status: 'pending', progress: 0 }])
|
|
79
|
+
setSelectedId('new')
|
|
80
|
+
setSearch('')
|
|
81
|
+
setFeedback('added')
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
function saveSettings() {
|
|
85
|
+
setSavedName(workspaceName)
|
|
86
|
+
setSavedNotifications(notifications)
|
|
87
|
+
setFeedback('saved')
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
return <section
|
|
91
|
+
role="region"
|
|
92
|
+
aria-label={`${t('design_system.colorStudio.preview')} — ${t(`design_system.colorPreview.${theme}`)}`}
|
|
93
|
+
data-preview-theme={theme}
|
|
94
|
+
style={tokens as React.CSSProperties}
|
|
95
|
+
className="min-w-0 overflow-hidden rounded-xl border border-border bg-card text-foreground"
|
|
96
|
+
>
|
|
97
|
+
<header className="flex flex-wrap items-center justify-between gap-3 border-b border-border bg-card px-5 py-4">
|
|
98
|
+
<div className="flex min-w-0 items-center gap-3">
|
|
99
|
+
{logo ? <img src={logo} alt={t('design_system.styleAgents.logoPreview')} className="h-9 w-28 shrink-0 object-contain object-left" /> : <span className="flex size-9 shrink-0 items-center justify-center rounded-lg bg-accent text-accent-foreground"><FolderKanban className="size-5" aria-hidden /></span>}
|
|
100
|
+
<div className="min-w-0">
|
|
101
|
+
<p className="truncate text-sm font-semibold">{savedName || t('design_system.colorStudio.workspace')}</p>
|
|
102
|
+
<p className="text-xs text-muted-foreground">{t(`design_system.colorPreview.${theme}`)}</p>
|
|
103
|
+
</div>
|
|
104
|
+
</div>
|
|
105
|
+
<SegmentedControl value={view} onValueChange={value => { setView(value); setFeedback(null) }} aria-label={t('design_system.colorStudio.overview')} size="sm">
|
|
106
|
+
<SegmentedControlItem value="projects">{t('design_system.colorStudio.projects')}</SegmentedControlItem>
|
|
107
|
+
<SegmentedControlItem value="settings">{t('design_system.colorStudio.settings')}</SegmentedControlItem>
|
|
108
|
+
</SegmentedControl>
|
|
109
|
+
</header>
|
|
110
|
+
<div className="space-y-5 p-5 sm:p-6">
|
|
111
|
+
{view === 'projects' ? <>
|
|
112
|
+
<div className="flex flex-wrap items-center justify-between gap-3">
|
|
113
|
+
<h3 className="text-lg font-semibold tracking-tight">{t('design_system.colorStudio.projects')}</h3>
|
|
114
|
+
<Button type="button" size="sm" onClick={addProject} disabled={hasNewProject}><Plus aria-hidden />{t('design_system.colorStudio.newProject')}</Button>
|
|
115
|
+
</div>
|
|
116
|
+
<div className="grid items-start gap-6" style={{ gridTemplateColumns: 'repeat(auto-fit, minmax(min(100%, 24rem), 1fr))' }}>
|
|
117
|
+
<div className="min-w-0 space-y-4">
|
|
118
|
+
<Input value={search} onChange={event => setSearch(event.target.value)} aria-label={t('design_system.colorStudio.search')} placeholder={t('design_system.colorStudio.search')} leading={<Search className="size-4" aria-hidden />} />
|
|
119
|
+
<DataTable<Project> data={visibleProjects} columns={columns} embedded sortable={false} exporter={false} onRowClick={project => setSelectedId(project.id)} emptyState={<p className="text-sm text-muted-foreground">{t('design_system.colorStudio.noResults')}</p>} />
|
|
120
|
+
</div>
|
|
121
|
+
<aside className="space-y-6 rounded-xl border border-border bg-background p-6" aria-label={t('design_system.colorStudio.selected')}>
|
|
122
|
+
<div className="space-y-3">
|
|
123
|
+
<Badge variant={STATUS_VARIANTS[selectedProject.status]} dot>{t(`design_system.colorPreview.${selectedProject.status}`)}</Badge>
|
|
124
|
+
<h4 className="text-xl font-medium tracking-tight">{t(`design_system.colorStudio.${selectedProject.titleKey}`)}</h4>
|
|
125
|
+
<p className="text-xs text-muted-foreground">{t(`design_system.colorStudio.${selectedProject.ownerKey}`)}</p>
|
|
126
|
+
</div>
|
|
127
|
+
<div className="flex items-center gap-5 border-t border-border pt-5">
|
|
128
|
+
<CircularProgress value={selectedProject.progress} size="lg" showValue aria-label={t('design_system.colorStudio.progress')} />
|
|
129
|
+
<div className="min-w-0 space-y-2"><p className="text-sm font-medium">{t('design_system.colorStudio.progress')}</p><p className="text-xs leading-relaxed text-muted-foreground">{t('design_system.colorStudio.progressHint')}</p></div>
|
|
130
|
+
</div>
|
|
131
|
+
</aside>
|
|
132
|
+
</div>
|
|
133
|
+
</> : <form className="max-w-xl space-y-5" onSubmit={event => { event.preventDefault(); saveSettings() }}>
|
|
134
|
+
<div className="space-y-1">
|
|
135
|
+
<h3 className="text-lg font-semibold tracking-tight">{t('design_system.colorStudio.settings')}</h3>
|
|
136
|
+
<p className="text-sm text-muted-foreground">{t('design_system.colorStudio.detailsHint')}</p>
|
|
137
|
+
</div>
|
|
138
|
+
<div className="space-y-2">
|
|
139
|
+
<Label htmlFor={`${id}-workspace`}>{t('design_system.colorPreview.name')}</Label>
|
|
140
|
+
<Input id={`${id}-workspace`} value={workspaceName} placeholder={t('design_system.colorStudio.workspace')} onChange={event => { setWorkspaceName(event.target.value); setFeedback(null) }} />
|
|
141
|
+
</div>
|
|
142
|
+
<div className="flex items-start gap-3 rounded-lg border border-border bg-card p-4">
|
|
143
|
+
<Checkbox id={`${id}-notifications`} checked={notifications} onCheckedChange={checked => { setNotifications(checked === true); setFeedback(null) }} />
|
|
144
|
+
<Label htmlFor={`${id}-notifications`} className="leading-5">{t('design_system.colorStudio.notifications')}</Label>
|
|
145
|
+
</div>
|
|
146
|
+
<div className="flex flex-wrap gap-2">
|
|
147
|
+
<Button type="submit">{t('design_system.colorPreview.save')}</Button>
|
|
148
|
+
<Button type="button" variant="secondary" onClick={() => { setWorkspaceName(savedName); setNotifications(savedNotifications); setFeedback(null) }}>{t('design_system.colorPreview.cancel')}</Button>
|
|
149
|
+
</div>
|
|
150
|
+
</form>}
|
|
151
|
+
<p role="status" className="text-xs text-status-success-text empty:hidden">{feedback ? t(feedback === 'saved' ? 'design_system.colorPreview.saved' : 'design_system.colorStudio.added') : null}</p>
|
|
152
|
+
</div>
|
|
153
|
+
<footer className="flex flex-wrap items-center justify-between gap-4 border-t border-border bg-background px-6 py-4">
|
|
154
|
+
<p className="text-xs font-medium text-muted-foreground">{t('design_system.colorStudio.states')}</p>
|
|
155
|
+
<div className="flex flex-wrap gap-3">
|
|
156
|
+
<Button type="button" size="sm">{t('design_system.colorStudio.default')}</Button>
|
|
157
|
+
<Button type="button" size="sm" style={{ backgroundColor: tokens['--primary-hover'], color: tokens['--primary-foreground'] }}>{t('design_system.colorStudio.hover')}</Button>
|
|
158
|
+
<Button type="button" size="sm" style={{ boxShadow: `0 0 0 2px ${tokens['--background']}, 0 0 0 4px ${tokens['--focus-ring-outer']}` }}>{t('design_system.colorStudio.focus')}</Button>
|
|
159
|
+
<Button type="button" size="sm" disabled>{t('design_system.colorStudio.disabled')}</Button>
|
|
160
|
+
</div>
|
|
161
|
+
|
|
162
|
+
</footer>
|
|
163
|
+
</section>
|
|
164
|
+
}
|