diff --git a/.storybook/manager-head.html b/.storybook/manager-head.html index bd449a2b929..4c8add49e4f 100644 --- a/.storybook/manager-head.html +++ b/.storybook/manager-head.html @@ -1,3 +1,8 @@ + + + + + diff --git a/.storybook/preview-head.html b/.storybook/preview-head.html index 1d628bd10f2..c9452f63a7b 100644 --- a/.storybook/preview-head.html +++ b/.storybook/preview-head.html @@ -1,3 +1,8 @@ + + + + + diff --git a/.storybook/preview.ts b/.storybook/preview.ts index e82412c2a70..30a91ad2a55 100644 --- a/.storybook/preview.ts +++ b/.storybook/preview.ts @@ -1,14 +1,18 @@ -import type { Preview } from '@storybook/vue3-vite'; -import { setup } from '@storybook/vue3'; -import { create as createTheme } from 'storybook/theming'; -import { router } from '@inertiajs/vue3'; -import { action } from 'storybook/actions'; +import type {Preview} from '@storybook/vue3-vite'; +import {setup} from '@storybook/vue3'; +import {create as createTheme} from 'storybook/theming'; +import {router} from '@inertiajs/vue3'; +import {action} from 'storybook/actions'; import './storybook.css'; import './theme.css'; -import { translate } from '@/translations/translator'; +import {translate} from '@/translations/translator'; import registerUiComponents from '@/bootstrap/ui'; import DateFormatter from '@/components/DateFormatter'; import cleanCodeSnippet from './clean-code-snippet'; +import PortalVue from 'portal-vue'; +import FullscreenHeader from '@/components/publish/FullscreenHeader.vue'; +import Portal from '@/components/portals/Portal.vue'; +import PortalTargets from '@/components/portals/PortalTargets.vue'; // Intercept Inertia navigation and log to Actions tab. router.on('before', (event) => { @@ -16,10 +20,48 @@ router.on('before', (event) => { return false; }); +// const portals = markRaw(new Portals()); +// const stacks = new Stacks(portals); + setup(async (app) => { window.__ = translate; + + window.Statamic = { + $config: { + get(key) { + const config = { + linkToDocs: true, + paginationSize: 50, + paginationSizeOptions: [10, 25, 50, 100, 500], + }; + + return config[key] ?? null; + } + }, + $commandPalette: { + add(command) { + // + } + }, + $progress: { + loading(name, loading) { + // + } + } + }; + app.config.globalProperties.__ = translate; app.config.globalProperties.$date = new DateFormatter; + app.config.globalProperties.cp_url = (url) => url; + // app.config.globalProperties.$portals = portals; + // app.config.globalProperties.$stacks = stacks; + + app.use(PortalVue, { portalName: 'v-portal' }); + + app.component('portal', Portal); + app.component('PortalTargets', PortalTargets); + app.component('publish-field-fullscreen-header', FullscreenHeader); + await registerUiComponents(app); }); @@ -50,11 +92,11 @@ const preview: Preview = { options: { storySort: { order: [ - // 'Getting Started', - // 'Installation', - '*', - 'Components' + 'Overview', + 'Components', + '*' ], + method: 'alphabetical', }, }, }, diff --git a/.storybook/public/apple-touch-icon.png b/.storybook/public/apple-touch-icon.png new file mode 100644 index 00000000000..366b78c6f56 Binary files /dev/null and b/.storybook/public/apple-touch-icon.png differ diff --git a/.storybook/public/favicon-16x16.png b/.storybook/public/favicon-16x16.png new file mode 100644 index 00000000000..24a535d32cc Binary files /dev/null and b/.storybook/public/favicon-16x16.png differ diff --git a/.storybook/public/favicon-32x32.png b/.storybook/public/favicon-32x32.png new file mode 100644 index 00000000000..5aba603484e Binary files /dev/null and b/.storybook/public/favicon-32x32.png differ diff --git a/.storybook/public/favicon.ico b/.storybook/public/favicon.ico new file mode 100644 index 00000000000..6eefee54763 Binary files /dev/null and b/.storybook/public/favicon.ico differ diff --git a/.storybook/storybook.css b/.storybook/storybook.css index 727a89f0678..53ca043986f 100644 --- a/.storybook/storybook.css +++ b/.storybook/storybook.css @@ -1,31 +1,33 @@ @import 'tailwindcss'; @import '../resources/css/ui.css'; +@import '../resources/css/core/utilities.css'; +@import '../resources/css/elements/tables.css'; @custom-variant dark (&:where(.dark, .dark *)); -.sbdocs h1, -.sbdocs h2, -.sbdocs h3, -.sbdocs h4, -.sbdocs h5, -.sbdocs h6 { +.sbdocs h1:not(.sbdocs-preview *, .sbdocs-preview h1), +.sbdocs h2:not(.sbdocs-preview *, .sbdocs-preview h2), +.sbdocs h3:not(.sbdocs-preview *, .sbdocs-preview h3), +.sbdocs h4:not(.sbdocs-preview *, .sbdocs-preview h4), +.sbdocs h5:not(.sbdocs-preview *, .sbdocs-preview h5), +.sbdocs h6:not(.sbdocs-preview *, .sbdocs-preview h6) { font-family: p22-mackinac-pro, serif; } -.sbdocs h1 { +.sbdocs h1:not(.sbdocs-preview *, .sbdocs-preview h1) { font-size: 2.5rem; } -.sbdocs h2 { +.sbdocs h2:not(.sbdocs-preview *, .sbdocs-preview h2) { font-size: 1.75rem; font-weight: 600; } -.sbdocs h3 { +.sbdocs h3:not(.sbdocs-preview *, .sbdocs-preview h3) { font-size: 1.25rem; } -.sbdocs p, -.sbdocs li { +.sbdocs p:not(.sbdocs-preview *, .sbdocs-preview p), +.sbdocs li:not(.sbdocs-preview *, .sbdocs-preview li) { font-size: 1rem; line-height: 1.6; font-weight: 350; diff --git a/.storybook/theme.css b/.storybook/theme.css index 7ad25f68a0e..d3e3ab47cd9 100644 --- a/.storybook/theme.css +++ b/.storybook/theme.css @@ -40,3 +40,4 @@ manually copy them here. --theme-color-ui-accent-text: oklch(0.673 0.182 276.935); } } + diff --git a/packages/cms/src/ui.js b/packages/cms/src/ui.js index 79cc9f3dab0..926f3b2446e 100644 --- a/packages/cms/src/ui.js +++ b/packages/cms/src/ui.js @@ -17,6 +17,7 @@ export const { CommandPaletteItem, Context, ContextFooter, + ContextHeader, ContextItem, ContextLabel, ContextMenu, diff --git a/resources/js/bootstrap/cms/ui.js b/resources/js/bootstrap/cms/ui.js index a7f761a726a..84c0483c152 100644 --- a/resources/js/bootstrap/cms/ui.js +++ b/resources/js/bootstrap/cms/ui.js @@ -17,6 +17,7 @@ export { CommandPaletteItem, Context, ContextFooter, + ContextHeader, ContextItem, ContextLabel, ContextMenu, diff --git a/resources/js/components/ui/Avatar.vue b/resources/js/components/ui/Avatar.vue index 3f4712cb359..4fb8384c736 100644 --- a/resources/js/components/ui/Avatar.vue +++ b/resources/js/components/ui/Avatar.vue @@ -8,12 +8,16 @@ interface Asset { } interface User { + /** Custom initials to display */ initials?: string; + /** Avatar URL or Asset object */ avatar?: string | Asset; + /** User's name (used to generate initials if not provided) */ name?: string; } interface Props { + /** Object with optional properties: `name`, `initials`, `avatar` */ user: User; } diff --git a/resources/js/components/ui/Badge.vue b/resources/js/components/ui/Badge.vue index cf2a7694761..7a7d131f516 100644 --- a/resources/js/components/ui/Badge.vue +++ b/resources/js/components/ui/Badge.vue @@ -6,16 +6,27 @@ import Icon from './Icon/Icon.vue'; import { Link } from '@inertiajs/vue3'; const props = defineProps({ + /** Appended text */ append: { type: [String, Number, Boolean, null], default: null }, + /** The element or component this component should render as */ as: { type: String, default: 'div' }, + /** Controls the color of the badge.

Options: `default`, `amber`, `black`, `blue`, `cyan`, `emerald`, `fuchsia`, `green`, `indigo`, `lime`, `orange`, `pink`, `purple`, `red`, `rose`, `sky`, `teal`, `violet`, `white`, `yellow` */ color: { type: String, default: 'default' }, + /** The URL to link to */ href: { type: String, default: null }, + /** When `href` is provided, this prop controls the link's `target` attribute */ target: { type: String, default: null }, + /** Icon name. [Browse available icons](/?path=/story/components-icon--all-icons) */ icon: { type: String, default: null }, + /** Icon name. Will display after the text. [Browse available icons](/?path=/story/components-icon--all-icons) */ iconAppend: { type: String, default: null }, + /** When `true`, the badge will be displayed as a pill */ pill: { type: Boolean, default: false }, + /** Prepended text */ prepend: { type: [String, Number, Boolean, null], default: null }, + /** Controls the size of the badge. Options: `sm`, `default`, `lg` */ size: { type: String, default: 'default' }, + /** Text to display in the badge */ text: { type: [String, Number, Boolean, null], default: null }, }); diff --git a/resources/js/components/ui/Button/Button.vue b/resources/js/components/ui/Button/Button.vue index c42dbc9dec5..43eb77ee407 100644 --- a/resources/js/components/ui/Button/Button.vue +++ b/resources/js/components/ui/Button/Button.vue @@ -6,18 +6,31 @@ import Icon from '../Icon/Icon.vue'; import { Link } from '@inertiajs/vue3'; const props = defineProps({ + /** The element or component this component should render as */ as: { type: String, default: null }, + /** The URL to link to */ href: { type: String, default: null }, + /** When `href` is provided, this prop controls the link's `target` attribute */ target: { type: String, default: null }, + /** Icon name. [Browse available icons](/?path=/story/components-icon--all-icons) */ icon: { type: String, default: null }, + /** Icon name. Will display after the text. [Browse available icons](/?path=/story/components-icon--all-icons) */ iconAppend: { type: String, default: null }, + /** When `true`, the button's padding will be adjusted to account for no text */ iconOnly: { type: Boolean, default: false }, + /** When using `ghost` or `subtle` button variants, you can use the `inset` prop to remove any invisible padding for better alignment */ inset: { type: Boolean, default: false }, + /** When `true`, the button shows an animated loading icon */ loading: { type: Boolean, default: false }, + /** When `true`, the button will be rounded */ round: { type: Boolean, default: false }, + /** Controls the size of the button. Options: `2xs`, `xs`, `sm`, `base`, `lg` */ size: { type: String, default: 'base' }, + /** Text to display in the button */ text: { type: [String, Number, Boolean, null], default: null }, + /** Unless `href` is provided, this component defaults to a `