Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
cf183a1
wip
duncanmcclean Dec 15, 2025
d0cc323
convert stack to composition api
duncanmcclean Dec 15, 2025
0958c66
wip
duncanmcclean Dec 15, 2025
2b660db
refactor open states
duncanmcclean Dec 15, 2025
1066aac
add beforeClose prop for modals
duncanmcclean Dec 15, 2025
b816f60
Move the stack's close callback logic into the stack component
duncanmcclean Dec 15, 2025
1ffe942
wip
duncanmcclean Dec 15, 2025
71c4853
only emit an event if the state actually changes
duncanmcclean Dec 15, 2025
24e8c5b
replace `close` slot prop with StackClose component
duncanmcclean Dec 15, 2025
cf2b0d2
add `opened` event to modals & stacks to allow parent components to f…
duncanmcclean Dec 15, 2025
7fe29f5
wip
duncanmcclean Dec 15, 2025
dbfc4fc
update stack usage
duncanmcclean Dec 15, 2025
e61903e
merge `narrow`/`half`/`full` props into new `size` prop
duncanmcclean Dec 15, 2025
4064c36
wip
duncanmcclean Dec 15, 2025
b1a2e9f
stacks shouldn't be full by default
duncanmcclean Dec 15, 2025
e60e828
Wire up stack title prop
duncanmcclean Dec 15, 2025
12dcd82
don't think we're using this anywhere. remove.
duncanmcclean Dec 15, 2025
4ebf86d
add data attribute
duncanmcclean Dec 15, 2025
e433e20
add stacks page to storybook docs
duncanmcclean Dec 15, 2025
1e7f6cf
add stack example to the playground
duncanmcclean Dec 15, 2025
fc39433
ensure portal & esc binding are properly destroyed when stack is closed
duncanmcclean Dec 15, 2025
70a94d8
wip
duncanmcclean Dec 15, 2025
fb45533
storybook docs
duncanmcclean Dec 16, 2025
5b06bb9
Merge branch 'master' into unify-modals-and-stacks
duncanmcclean Dec 17, 2025
46e4408
Merge branch 'master' into unify-modals-and-stacks
duncanmcclean Dec 19, 2025
25caba0
attempt to avoid merge conflicts when storybook pr is merged in
duncanmcclean Dec 19, 2025
c1fbba5
Merge branch 'master' into unify-modals-and-stacks
duncanmcclean Dec 19, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions packages/cms/src/ui.js
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,8 @@ export const {
Switch,
TabContent,
Stack,
StackClose,
StackTitle,
Table,
TableCell,
TableColumn,
Expand Down
2 changes: 2 additions & 0 deletions resources/js/bootstrap/cms/ui.js
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,8 @@ export {
Switch,
TabContent,
Stack,
StackClose,
StackTitle,
Table,
TableCell,
TableColumn,
Expand Down
10 changes: 6 additions & 4 deletions resources/js/components/assets/Editor/Editor.vue
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<template>
<ui-stack name="asset-editor" :before-close="shouldClose" :full="true" @closed="$emit('closed')" v-slot="{ close }">
<Stack size="full" open ref="stack" :before-close="shouldClose" @update:open="$emit('closed')">
<div
class="asset-editor relative flex h-full flex-col rounded-sm bg-gray-100 dark:bg-dark-800"
:class="isImage ? 'is-image' : 'is-file'"
Expand All @@ -22,7 +22,7 @@
{{ asset.path }}
</span>
</button>
<ui-button variant="ghost" icon="x" class="absolute top-1.5 end-1.5" round @click="confirmClose(close)" :aria-label="__('Close Editor')" />
<ui-button variant="ghost" icon="x" class="absolute top-1.5 end-1.5" round @click="confirmClose()" :aria-label="__('Close Editor')" />
</header>

<div class="flex flex-1 grow flex-col overflow-auto md:flex-row md:justify-between">
Expand Down Expand Up @@ -174,7 +174,7 @@
@cancel="closingWithChanges = false"
/>
</div>
</ui-stack>
</Stack>
</template>

<script>
Expand All @@ -188,6 +188,7 @@ import {
PublishContainer,
PublishTabs,
Icon,
Stack,
} from '@ui';
import ItemActions from '@/components/actions/ItemActions.vue';

Expand All @@ -204,6 +205,7 @@ export default {
PublishContainer,
PublishTabs,
Icon,
Stack,
},

props: {
Expand Down Expand Up @@ -435,7 +437,7 @@ export default {
},

confirmClose(close) {
if (this.shouldClose()) close();
if (this.shouldClose()) this.$refs.stack.close();
},

confirmCloseWithChanges() {
Expand Down
26 changes: 9 additions & 17 deletions resources/js/components/blueprints/Fields.vue
Original file line number Diff line number Diff line change
Expand Up @@ -33,21 +33,11 @@
<ui-button icon="add-circle" :text="__('Create Field')" @click="createField" />
</div>

<ui-stack
name="fieldtype-selector"
v-if="isSelectingNewFieldtype"
@closed="isSelectingNewFieldtype = false"
v-slot="{ close }"
>
<fieldtype-selector @closed="close" @selected="fieldtypeSelected" />
</ui-stack>

<ui-stack
name="field-settings"
v-if="pendingCreatedField != null"
@closed="pendingCreatedField = null"
v-slot="{ close }"
>
<Stack v-model:open="isSelectingNewFieldtype">
<fieldtype-selector @closed="isSelectingNewFieldtype = false" @selected="fieldtypeSelected" />
</Stack>

<Stack :open="pendingCreatedField != null" @update:open="pendingCreatedField = null">
<field-settings
ref="settings"
:type="pendingCreatedField.config.type"
Expand All @@ -57,9 +47,9 @@
:suggestable-condition-fields="suggestableConditionFields"
:is-inside-set="isInsideSet"
@committed="fieldCreated"
@closed="close"
@closed="pendingCreatedField = null"
/>
</ui-stack>
</Stack>
</div>
</template>

Expand All @@ -71,6 +61,7 @@ import LinkFields from './LinkFields.vue';
import FieldtypeSelector from '../fields/FieldtypeSelector.vue';
import FieldSettings from '../fields/Settings.vue';
import CanDefineLocalizable from '../fields/CanDefineLocalizable';
import { Stack } from '@/components/ui';

export default {
mixins: [CanDefineLocalizable],
Expand All @@ -81,6 +72,7 @@ export default {
LinkFields,
FieldtypeSelector,
FieldSettings,
Stack,
},

props: {
Expand Down
2 changes: 1 addition & 1 deletion resources/js/components/blueprints/ImportField.vue
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
</div>
<div class="flex items-center gap-2">
<ui-button size="sm" icon="trash" variant="subtle" @click.prevent="$emit('deleted')" v-tooltip="__('Remove')" />
<ui-stack name="field-settings" v-if="isEditing" @closed="editorClosed">
<ui-stack :open="isEditing" @update:open="editorClosed">
<field-settings
ref="settings"
:id="field._id"
Expand Down
16 changes: 10 additions & 6 deletions resources/js/components/blueprints/LinkFields.vue
Original file line number Diff line number Diff line change
@@ -1,12 +1,16 @@
<template>
<div>
<ui-button icon="link" @click="open = true" :text="__('Link Existing')" />
<Stack size="narrow" v-model:open="open">
<template #trigger>
<Button icon="link" :text="__('Link Existing')" />
</template>

<ui-stack narrow v-if="open" @closed="open = false" name="field-linker" v-slot="{ close }">
<div class="h-full overflow-auto bg-white dark:bg-gray-800 p-3 rounded-l-xl">
<header class="flex items-center justify-between pl-3">
<Heading :text="__('Link Fields')" size="lg" icon="fieldsets" />
<Button type="button" icon="x" variant="subtle" @click="close" />
<StackClose>
<Button type="button" icon="x" variant="subtle" />
</StackClose>
</header>

<div class="flex-1 overflow-auto px-3 py-4">
Expand Down Expand Up @@ -90,17 +94,17 @@
/>
</div>
</div>
</ui-stack>
</Stack>
</div>
</template>

<script>
import uniqid from 'uniqid';
import { Combobox, Button, Input, Heading, Field } from '@/components/ui';
import { Combobox, Button, Input, Heading, Field, Stack, StackClose } from '@/components/ui';
import { usePage } from '@inertiajs/vue3';

export default {
components: { Heading, Combobox, Button, Input, Field },
components: { Heading, Combobox, Button, Input, Field, Stack, StackClose },

props: {
excludeFieldset: String,
Expand Down
7 changes: 4 additions & 3 deletions resources/js/components/blueprints/RegularField.vue
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@
<ui-button inset size="sm" icon="trash" variant="subtle" @click.prevent="$emit('deleted')" v-tooltip="__('Remove')" />
</div>

<ui-stack name="field-settings" v-if="isEditing" @closed="editorClosed">
<Stack :open="isEditing" @update:open="editorClosed">
<field-settings
ref="settings"
:id="field._id"
Expand All @@ -45,7 +45,7 @@
@committed="settingsUpdated"
@closed="editorClosed"
/>
</ui-stack>
</Stack>
</div>
</div>
</div>
Expand All @@ -59,7 +59,7 @@ import WidthSelector from '../fields/WidthSelector.vue';
import CanDefineLocalizable from '../fields/CanDefineLocalizable';
import titleize from '../../util/titleize';
import deslugify from '../../util/deslugify';
import { Icon } from '@/components/ui';
import { Icon, Stack } from '@/components/ui';

export default {
mixins: [Field, CanDefineLocalizable],
Expand All @@ -68,6 +68,7 @@ export default {
FieldSettings,
WidthSelector,
Icon,
Stack,
},

props: ['suggestableConditionFields'],
Expand Down
8 changes: 4 additions & 4 deletions resources/js/components/blueprints/Section.vue
Original file line number Diff line number Diff line change
Expand Up @@ -35,10 +35,10 @@
</ui-panel>

<ui-stack
narrow
v-if="editingSection"
size="narrow"
:open="editingSection !== false"
@update:open="editCancelled"
@opened="() => $nextTick(() => $refs.displayInput.focus())"
@closed="editCancelled"
>
<div class="h-full overflow-scroll overflow-x-auto bg-white px-6 dark:bg-dark-800">
<header class="py-2 -mx-6 px-6 border-b border-gray-200 dark:border-gray-700 mb-5">
Expand Down Expand Up @@ -182,7 +182,7 @@ export default {

isSoloNarrowStack() {
const stacks = this.$stacks.stacks();
return stacks.length === 1 && stacks[0]?.data?.vm?.narrow === true;
return stacks.length === 1 && stacks[0]?.data?.vm?.size === 'narrow';
},
},

Expand Down
20 changes: 11 additions & 9 deletions resources/js/components/blueprints/Tab.vue
Original file line number Diff line number Diff line change
Expand Up @@ -24,19 +24,21 @@
</DropdownMenu>
</Dropdown>

<ui-stack
narrow
v-if="editing"
<Stack
size="narrow"
:open="editing"
@opened="() => $nextTick(() => $refs.title.focus())"
@closed="editCancelled"
@update:open="editCancelled"
>
<div class="h-full overflow-scroll overflow-x-auto bg-white px-6 dark:bg-dark-800">
<header class="py-2 -mx-6 px-6 border-b border-gray-200 dark:border-gray-700 mb-5">
<div class="flex items-center justify-between">
<ui-heading size="lg">
{{ editText }}
</ui-heading>
<ui-button icon="x" variant="ghost" class="-me-2" @click="editCancelled" />
<StackClose>
<ui-button icon="x" variant="ghost" class="-me-2" />
</StackClose>
</div>
</header>
<div class="space-y-6">
Expand Down Expand Up @@ -75,15 +77,15 @@
</div>
</div>
</div>
</ui-stack>
</Stack>
</TabTrigger>
</template>

<script>
import { TabTrigger, Dropdown, DropdownMenu, DropdownItem, Icon, Field, Input } from '@/components/ui';
import { TabTrigger, Dropdown, DropdownMenu, DropdownItem, Icon, Field, Input, Stack, StackClose } from '@/components/ui';

export default {
components: { TabTrigger, Dropdown, DropdownMenu, DropdownItem, Icon, Field, Input },
components: { TabTrigger, Dropdown, DropdownMenu, DropdownItem, Icon, Field, Input, Stack, StackClose },

props: {
tab: {
Expand Down Expand Up @@ -134,7 +136,7 @@ export default {

isSoloNarrowStack() {
const stacks = this.$stacks.stacks();
return stacks.length === 1 && stacks[0]?.data?.vm?.narrow === true;
return stacks.length === 1 && stacks[0]?.data?.vm?.size === 'narrow';
},
},

Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<template>
<Modal :title="__('Delete')" :open="true" @update:open="$emit('cancel')">
<Modal :title="__('Delete')" open @update:open="$emit('cancel')">
<p>Are you sure you want to delete this?</p>

<Field
Expand Down
17 changes: 5 additions & 12 deletions resources/js/components/entries/PublishActions.vue
Original file line number Diff line number Diff line change
@@ -1,13 +1,6 @@
<template>
<ui-stack narrow name="publish-options" @closed="$emit('closed')" v-slot="{ close }">
<div class="m-2 flex h-full flex-col rounded-xl bg-white dark:bg-gray-800">
<header
class="flex items-center justify-between rounded-t-xl border-b border-gray-300 px-4 mb-3 py-2 dark:border-gray-950 dark:bg-gray-800"
>
<Heading size="lg">{{ __('Publish') }}</Heading>
<Button icon="x" variant="ghost" class="-me-2" @click="close" />
</header>

<Stack size="narrow" :title="__('Publish')" open @update:open="$emit('closed')">
<div class="m-2 flex h-full flex-col rounded-xl bg-white dark:bg-gray-800">
<div class="flex-1 overflow-auto">
<div class="loading flex h-full items-center justify-center" v-if="saving">
<Icon name="loading" />
Expand Down Expand Up @@ -45,14 +38,14 @@
</div>
</div>
</div>
</ui-stack>
</Stack>
</template>

<script>
import { Heading, Button, Select, DatePicker, Textarea, Icon, Subheading } from '@/components/ui';
import { Heading, Button, Select, DatePicker, Textarea, Icon, Subheading, Stack } from '@/components/ui';

export default {
components: { Heading, Button, Select, DatePicker, Textarea, Icon, Subheading },
components: { Heading, Button, Select, DatePicker, Textarea, Icon, Subheading, Stack },

props: {
actions: Object,
Expand Down
17 changes: 9 additions & 8 deletions resources/js/components/entries/PublishForm.vue
Original file line number Diff line number Diff line change
Expand Up @@ -190,21 +190,20 @@
</LivePreview>
</PublishContainer>

<ui-stack
name="revision-history"
v-if="showRevisionHistory"
@closed="showRevisionHistory = false"
:narrow="true"
v-slot="{ close }"
<Stack
ref="revisionHistoryStack"
size="narrow"
:title="__('Revision History')"
v-model:open="showRevisionHistory"
>
<revision-history
:index-url="actions.revisions"
:restore-url="actions.restore"
:reference="initialReference"
:can-restore-revisions="!readOnly"
@closed="close"
@closed="$refs.revisionHistoryStack.close()"
/>
</ui-stack>
</Stack>

<publish-actions
v-if="confirmingPublish"
Expand Down Expand Up @@ -277,6 +276,7 @@ import {
PublishComponents,
PublishLocalizations as LocalizationsCard,
LivePreview,
Stack,
} from '@ui';
import resetValuesFromResponse from '@/util/resetValuesFromResponse.js';
import { computed, ref } from 'vue';
Expand Down Expand Up @@ -312,6 +312,7 @@ export default {
Subheading,
Switch,
Select,
Stack,
},

props: {
Expand Down
Loading