This commit is contained in:
Calum H. (IMB11)
2026-08-24 13:11:11 +01:00
parent b0c77bb395
commit 0748b8732e
9 changed files with 138 additions and 70 deletions
+15 -7
View File
@@ -3,12 +3,12 @@
:id="id" :id="id"
type="button" type="button"
role="switch" role="switch"
:aria-checked="modelValue" :aria-checked="indeterminate ? 'mixed' : modelValue"
:disabled="disabled" :disabled="disabled"
class="group inline-flex shrink-0 touch-manipulation items-center rounded-full m-0 p-1 transition-all duration-200 cursor-pointer border-none" class="group inline-flex shrink-0 touch-manipulation items-center rounded-full m-0 p-1 transition-all duration-200 cursor-pointer border-none"
:class="[ :class="[
small ? 'h-5 !w-[40px]' : 'h-6 !w-[48px]', small ? 'h-5 !w-[40px]' : 'h-6 !w-[48px]',
modelValue ? 'bg-brand' : 'bg-button-bg', indeterminate || modelValue ? 'bg-brand' : 'bg-button-bg',
disabled ? 'opacity-50 cursor-not-allowed' : '', disabled ? 'opacity-50 cursor-not-allowed' : '',
]" ]"
@click="toggle" @click="toggle"
@@ -16,14 +16,21 @@
<span <span
class="rounded-full transition-all duration-200" class="rounded-full transition-all duration-200"
:class="[ :class="[
small ? 'w-3 h-3' : 'w-4 h-4', indeterminate
modelValue
? small ? small
? 'translate-x-[20px] bg-black/90' ? 'h-0.5 w-2.5 translate-x-[11px] bg-black/90'
: 'translate-x-[24px] bg-black/90' : 'h-0.5 w-3 translate-x-[14px] bg-black/90'
: 'bg-gray', : modelValue
? small
? 'h-3 w-3 translate-x-[20px] bg-black/90'
: 'h-4 w-4 translate-x-[24px] bg-black/90'
: small
? 'h-3 w-3 bg-gray'
: 'h-4 w-4 bg-gray',
disabled disabled
? '' ? ''
: indeterminate
? 'group-hover:scale-x-110 group-active:scale-x-75'
: small : small
? 'group-hover:w-[14px] group-hover:h-[14px] group-hover:m-[-1px] group-active:w-[10px] group-active:h-[10px] group-active:m-[1px]' ? 'group-hover:w-[14px] group-hover:h-[14px] group-hover:m-[-1px] group-active:w-[10px] group-active:h-[10px] group-active:m-[1px]'
: 'group-hover:w-[18px] group-hover:h-[18px] group-hover:m-[-1px] group-active:w-[14px] group-active:h-[14px] group-active:m-[1px]', : 'group-hover:w-[18px] group-hover:h-[18px] group-hover:m-[-1px] group-active:w-[14px] group-active:h-[14px] group-active:m-[1px]',
@@ -37,6 +44,7 @@ const props = defineProps<{
id?: string id?: string
disabled?: boolean disabled?: boolean
small?: boolean small?: boolean
indeterminate?: boolean
}>() }>()
const modelValue = defineModel<boolean>() const modelValue = defineModel<boolean>()
@@ -54,6 +54,10 @@ const messages = defineMessages({
id: 'content.card.frozen', id: 'content.card.frozen',
defaultMessage: 'This project is locked to its current version until unfrozen.', defaultMessage: 'This project is locked to its current version until unfrozen.',
}, },
useEnabledForControls: {
id: 'content.card.use-enabled-for-controls',
defaultMessage: 'Use the Enabled for controls to enable or disable this content.',
},
}) })
interface Props { interface Props {
@@ -137,7 +141,20 @@ const versionNumberRef = ref<HTMLElement | null>(null)
const fileNameRef = ref<HTMLElement | null>(null) const fileNameRef = ref<HTMLElement | null>(null)
const isDisabled = computed(() => props.disabled || props.installing) const isDisabled = computed(() => props.disabled || props.installing)
const isToggleDisabled = computed(() => isDisabled.value || props.toggleDisabled) const isToggleIndeterminate = computed(
() =>
props.enabledFor !== undefined && props.enabledFor.server !== props.enabledFor.player,
)
const isToggleDisabled = computed(
() => isDisabled.value || props.toggleDisabled || isToggleIndeterminate.value,
)
const toggleTooltip = computed(() => {
if (isToggleIndeterminate.value) {
return formatMessage(messages.useEnabledForControls)
}
if (!isToggleDisabled.value) return undefined
return props.toggleDisabledTooltip ?? props.disabledTooltip ?? undefined
})
const isEnabledForDisabled = computed(() => !props.enabledFor?.server && !props.enabledFor?.player) const isEnabledForDisabled = computed(() => !props.enabledFor?.server && !props.enabledFor?.player)
const clientWarningMessage = computed(() => { const clientWarningMessage = computed(() => {
@@ -368,7 +385,7 @@ const installTooltip = computed(() => {
<div <div
v-if="!hideActions" v-if="!hideActions"
class="flex shrink-0 items-center justify-end gap-2 transition-colors duration-200" class="flex shrink-0 items-center justify-end gap-2 transition-colors duration-200"
:class="enabledFor ? 'w-[112px]' : 'min-w-[160px]'" :class="enabledFor ? 'w-[168px]' : 'min-w-[160px]'"
> >
<slot name="additionalButtonsLeft" /> <slot name="additionalButtonsLeft" />
@@ -431,14 +448,11 @@ const installTooltip = computed(() => {
</div> </div>
<Toggle <Toggle
v-if="enabled !== undefined && !hideToggle && !enabledFor" v-if="enabled !== undefined && !hideToggle"
v-tooltip=" v-tooltip="toggleTooltip"
isToggleDisabled && (toggleDisabledTooltip || disabledTooltip)
? (toggleDisabledTooltip ?? disabledTooltip)
: undefined
"
:model-value="enabled" :model-value="enabled"
:disabled="isToggleDisabled" :disabled="isToggleDisabled"
:indeterminate="isToggleIndeterminate"
:aria-label="project.title" :aria-label="project.title"
class="my-auto" class="my-auto"
@update:model-value="(val) => emit('update:enabled', val as boolean)" @update:model-value="(val) => emit('update:enabled', val as boolean)"
@@ -24,8 +24,7 @@ const messages = defineMessages({
}, },
enabledForDescription: { enabledForDescription: {
id: 'content.enabled-for.description', id: 'content.enabled-for.description',
defaultMessage: defaultMessage: 'Choose where this content is enabled. Turn both off to disable it.',
'Choose whether this content runs on the server, is sent to players, or is disabled in both places.',
}, },
}) })
@@ -300,7 +299,7 @@ function handleSort(column: ContentCardTableSortColumn) {
v-if="hasAnyActions" v-if="hasAnyActions"
role="columnheader" role="columnheader"
class="shrink-0 text-right" class="shrink-0 text-right"
:class="hasEnabledForColumn ? 'w-[112px]' : 'min-w-[160px]'" :class="hasEnabledForColumn ? 'w-[168px]' : 'min-w-[160px]'"
> >
<span class="font-semibold text-secondary">{{ <span class="font-semibold text-secondary">{{
formatMessage(commonMessages.actionsLabel) formatMessage(commonMessages.actionsLabel)
@@ -17,7 +17,15 @@ const messages = defineMessages({
}, },
locked: { locked: {
id: 'content.enabled-for.locked', id: 'content.enabled-for.locked',
defaultMessage: 'Unlock the environment controls from the row menu to change this selection.', defaultMessage: 'These choices are locked. Select Unlock from the row menu to change them.',
},
requiredHere: {
id: 'content.enabled-for.required-here',
defaultMessage: 'This content needs to be enabled here to work correctly.',
},
notSupportedHere: {
id: 'content.enabled-for.not-supported-here',
defaultMessage: "This content isn't designed to be enabled here.",
}, },
}) })
@@ -50,6 +58,12 @@ function isDisabled(side: ContentSide) {
return props.disabled || disabledSides.value.has(side) return props.disabled || disabledSides.value.has(side)
} }
function getDisabledTooltip(side: ContentSide) {
if (props.disabled) return props.disabledTooltip
if (!disabledSides.value.has(side)) return undefined
return formatMessage(isSelected(side) ? messages.requiredHere : messages.notSupportedHere)
}
function toggle(side: ContentSide) { function toggle(side: ContentSide) {
if (isDisabled(side)) return if (isDisabled(side)) return
emit('update:model-value', side, !isSelected(side)) emit('update:model-value', side, !isSelected(side))
@@ -61,11 +75,7 @@ function toggle(side: ContentSide) {
<button <button
v-for="side in sides" v-for="side in sides"
:key="side" :key="side"
v-tooltip=" v-tooltip="getDisabledTooltip(side)"
isDisabled(side)
? (disabledTooltip ?? modelValue.disabledTooltip ?? formatMessage(messages.locked))
: undefined
"
type="button" type="button"
class="flex h-8 items-center rounded-xl border border-solid px-3 text-sm font-medium transition-[background-color,border-color,color,opacity,transform] duration-100 focus-visible:outline-none focus-visible:ring-4 focus-visible:ring-brand-shadow" class="flex h-8 items-center rounded-xl border border-solid px-3 text-sm font-medium transition-[background-color,border-color,color,opacity,transform] duration-100 focus-visible:outline-none focus-visible:ring-4 focus-visible:ring-brand-shadow"
:class="[ :class="[
@@ -119,8 +119,7 @@ const messages = defineMessages({
}, },
enabledForDescription: { enabledForDescription: {
id: 'content.enabled-for.description', id: 'content.enabled-for.description',
defaultMessage: defaultMessage: 'Choose where this content is enabled. Turn both off to disable it.',
'Choose whether this content runs on the server, is sent to players, or is disabled in both places.',
}, },
pleaseWait: { pleaseWait: {
id: 'content.enabled-for.please-wait', id: 'content.enabled-for.please-wait',
@@ -300,7 +299,7 @@ const tableItems = computed<ContentCardTableItem[]>(() =>
link: item.source.link ?? sourceProjectLink(item.source.project), link: item.source.link ?? sourceProjectLink(item.source.project),
} }
: undefined, : undefined,
...(props.enableToggle ? { enabled: item.enabled } : {}), ...(props.enableToggle || props.enableEnabledFor ? { enabled: item.enabled } : {}),
...(props.enableEnabledFor ? { enabledFor: item.enabledFor } : {}), ...(props.enableEnabledFor ? { enabledFor: item.enabledFor } : {}),
locked: item.locked, locked: item.locked,
installing: item.installing === true, installing: item.installing === true,
@@ -638,7 +637,7 @@ defineExpose({ show, showLoading, hide, getState, restore, updateItem, setItems
<div <div
v-if="showTableActions" v-if="showTableActions"
class="shrink-0 text-right" class="shrink-0 text-right"
:class="props.enableEnabledFor ? 'w-[112px]' : 'min-w-[160px]'" :class="props.enableEnabledFor ? 'w-[168px]' : 'min-w-[160px]'"
> >
<span class="font-semibold text-secondary">{{ <span class="font-semibold text-secondary">{{
formatMessage(commonMessages.actionsLabel) formatMessage(commonMessages.actionsLabel)
@@ -656,7 +655,7 @@ defineExpose({ show, showLoading, hide, getState, restore, updateItem, setItems
hide-header hide-header
flat flat
v-on=" v-on="
props.enableToggle props.enableToggle || props.enableEnabledFor
? { 'update:enabled': (id: string, val: boolean) => handleEnabledChange(id, val) } ? { 'update:enabled': (id: string, val: boolean) => handleEnabledChange(id, val) }
: {} : {}
" "
@@ -754,7 +754,7 @@ async function confirmDisable() {
if (itemsToDisable.length === 1) { if (itemsToDisable.length === 1) {
const item = itemsToDisable[0] const item = itemsToDisable[0]
const id = getItemId(item) const id = getItemId(item)
markChanging(id) if (!item.enabledFor) markChanging(id)
try { try {
if (ctx.bulkDisableItems) { if (ctx.bulkDisableItems) {
await ctx.bulkDisableItems(itemsToDisable) await ctx.bulkDisableItems(itemsToDisable)
@@ -762,7 +762,7 @@ async function confirmDisable() {
await ctx.toggleEnabled?.(item) await ctx.toggleEnabled?.(item)
} }
} finally { } finally {
unmarkChanging(id) if (!item.enabledFor) unmarkChanging(id)
} }
return return
} }
@@ -781,11 +781,11 @@ async function handleToggleEnabledById(id: string, _value: boolean) {
await promptDisableItems([item]) await promptDisableItems([item])
return return
} }
markChanging(id) if (!item.enabledFor) markChanging(id)
try { try {
await ctx.toggleEnabled(item) await ctx.toggleEnabled(item)
} finally { } finally {
unmarkChanging(id) if (!item.enabledFor) unmarkChanging(id)
} }
} }
@@ -844,12 +844,7 @@ async function handleSetEnabledForById(
const item = ctx.items.value.find((candidate) => getItemId(candidate) === id) const item = ctx.items.value.find((candidate) => getItemId(candidate) === id)
if (!item) return if (!item) return
markChanging(id) await ctx.setEnabledFor(item, side, enabled)
try {
await ctx.setEnabledFor(item, side, enabled)
} finally {
unmarkChanging(id)
}
} }
// Bulk updating // Bulk updating
@@ -57,7 +57,6 @@ export interface ContentEnabledForState {
player: boolean player: boolean
locked: boolean locked: boolean
disabledSides?: ContentSide[] disabledSides?: ContentSide[]
disabledTooltip?: string | null
warningTooltip?: string | null warningTooltip?: string | null
} }
@@ -104,14 +104,10 @@ const messages = defineMessages({
id: 'hosting.content.failed-to-install', id: 'hosting.content.failed-to-install',
defaultMessage: 'Failed to install content', defaultMessage: 'Failed to install content',
}, },
sideLocked: {
id: 'hosting.content.enabled-for.side-locked',
defaultMessage: 'This option conflicts with the detected environment.',
},
unknownEnvironment: { unknownEnvironment: {
id: 'hosting.content.enabled-for.unknown-environment', id: 'hosting.content.enabled-for.unknown-environment',
defaultMessage: defaultMessage:
'We could not determine where this content should run. Check that the selected environments are correct.', "We couldn't tell where this content should be enabled. Review the Server and Player choices.",
}, },
lockEnvironment: { lockEnvironment: {
id: 'hosting.content.enabled-for.lock-environment', id: 'hosting.content.enabled-for.lock-environment',
@@ -331,21 +327,31 @@ function hasDetectedEnvironment(addon: Archon.Content.v1.Addon) {
return environment !== undefined && environment !== 'unknown' return environment !== undefined && environment !== 'unknown'
} }
function getLockedSides(addon: Archon.Content.v1.Addon): ContentSide[] { function getEnvironmentLockedSides(addon: Archon.Content.v1.Addon): ContentSide[] {
if (addon.side_toggle_unlocked || !hasDetectedEnvironment(addon)) return []
switch (getAddonEnvironment(addon)) { switch (getAddonEnvironment(addon)) {
case 'client_and_server':
case 'client_only': case 'client_only':
case 'singleplayer_only': case 'singleplayer_only':
return ['server']
case 'server_only': case 'server_only':
case 'dedicated_server_only': case 'dedicated_server_only':
return ['server', 'player']
case 'client_only_server_optional':
return ['player'] return ['player']
case 'server_only_client_optional':
return ['server']
default: default:
return [] return []
} }
} }
function getLockedSides(addon: Archon.Content.v1.Addon): ContentSide[] {
return addon.side_toggle_unlocked ? [] : getEnvironmentLockedSides(addon)
}
function hasEnvironmentLock(addon: Archon.Content.v1.Addon) {
return getEnvironmentLockedSides(addon).length > 0
}
function getEnabledForWarning(addon: Archon.Content.v1.Addon) { function getEnabledForWarning(addon: Archon.Content.v1.Addon) {
if (addon.pack_client_retained) return formatMessage(commonMessages.clientRetainedWarning) if (addon.pack_client_retained) return formatMessage(commonMessages.clientRetainedWarning)
if (addon.pack_client_depends) return formatMessage(commonMessages.clientDependsWarning) if (addon.pack_client_depends) return formatMessage(commonMessages.clientDependsWarning)
@@ -618,24 +624,26 @@ const deleteMutation = useMutation({
type SetEnabledForVariables = { type SetEnabledForVariables = {
addon: Archon.Content.v1.Addon addon: Archon.Content.v1.Addon
side: ContentSide sides: ContentSide[]
enabled: boolean enabled: boolean
} }
const setEnabledForMutation = useMutation({ const setEnabledForMutation = useMutation({
mutationFn: async ({ addon, side, enabled }: SetEnabledForVariables) => { mutationFn: async ({ addon, sides, enabled }: SetEnabledForVariables) => {
const request: Archon.Content.v1.SetAddonEnabledRequest = { const request: Archon.Content.v1.SetAddonEnabledRequest = {
filename: addon.filename, filename: addon.filename,
kind: addon.kind, kind: addon.kind,
enabled, enabled,
} }
if (side === 'server') { await Promise.all(
await client.archon.content_v1.setAddonEnabledServer(serverId, worldId.value!, request) sides.map((side) =>
} else { side === 'server'
await client.archon.content_v1.setAddonEnabledPlayer(serverId, worldId.value!, request) ? client.archon.content_v1.setAddonEnabledServer(serverId, worldId.value!, request)
} : client.archon.content_v1.setAddonEnabledPlayer(serverId, worldId.value!, request),
),
)
}, },
onMutate: async ({ addon, side, enabled }) => { onMutate: async ({ addon, sides, enabled }) => {
const targetQueryKey = getAddonQueryKey(addon) const targetQueryKey = getAddonQueryKey(addon)
await queryClient.cancelQueries({ queryKey: targetQueryKey }) await queryClient.cancelQueries({ queryKey: targetQueryKey })
const previousData = queryClient.getQueryData<Archon.Content.v1.Addons>(targetQueryKey) const previousData = queryClient.getQueryData<Archon.Content.v1.Addons>(targetQueryKey)
@@ -645,9 +653,11 @@ const setEnabledForMutation = useMutation({
...oldData, ...oldData,
addons: (oldData.addons ?? []).map((candidate) => { addons: (oldData.addons ?? []).map((candidate) => {
if (candidate.filename !== addon.filename || candidate.kind !== addon.kind) return candidate if (candidate.filename !== addon.filename || candidate.kind !== addon.kind) return candidate
return side === 'server' return {
? { ...candidate, disabled_server: !enabled } ...candidate,
: { ...candidate, disabled_player: !enabled } ...(sides.includes('server') ? { disabled_server: !enabled } : {}),
...(sides.includes('player') ? { disabled_player: !enabled } : {}),
}
}), }),
} }
}) })
@@ -718,7 +728,19 @@ async function handleSetEnabledFor(item: ContentItem, side: ContentSide, enabled
if (contentActionDisabled.value) return if (contentActionDisabled.value) return
const addon = getAddonForItem(item) const addon = getAddonForItem(item)
if (!addon) return if (!addon) return
await setEnabledForMutation.mutateAsync({ addon, side, enabled }) await setEnabledForMutation.mutateAsync({ addon, sides: [side], enabled })
}
async function handleToggleEnabled(item: ContentItem) {
if (contentActionDisabled.value || !item.enabledFor) return
if (item.enabledFor.server !== item.enabledFor.player) return
const addon = getAddonForItem(item)
if (!addon) return
await setEnabledForMutation.mutateAsync({
addon,
sides: ['server', 'player'],
enabled: !item.enabledFor.server,
})
} }
async function handleModpackSetEnabledFor( async function handleModpackSetEnabledFor(
@@ -726,13 +748,18 @@ async function handleModpackSetEnabledFor(
side: ContentSide, side: ContentSide,
enabled: boolean, enabled: boolean,
) { ) {
modpackContentModal.value?.updateItem(item.file_name, { disabled: true })
try { try {
await handleSetEnabledFor(item, side, enabled) await handleSetEnabledFor(item, side, enabled)
} catch { } catch {
return return
} finally { }
modpackContentModal.value?.updateItem(item.file_name, { disabled: false }) }
async function handleModpackToggleEnabled(item: ContentItem) {
try {
await handleToggleEnabled(item)
} catch {
return
} }
} }
@@ -946,6 +973,7 @@ function addonToContentItem(addon: AddonWithUiState): ContentItem {
: undefined : undefined
const serverEnabled = !addon.disabled_server const serverEnabled = !addon.disabled_server
const playerEnabled = !addon.disabled_player const playerEnabled = !addon.disabled_player
const lockedSides = getLockedSides(addon)
return { return {
project: { project: {
...(projectMetadata ?? {}), ...(projectMetadata ?? {}),
@@ -976,9 +1004,8 @@ function addonToContentItem(addon: AddonWithUiState): ContentItem {
enabledFor: { enabledFor: {
server: serverEnabled, server: serverEnabled,
player: playerEnabled, player: playerEnabled,
locked: hasDetectedEnvironment(addon) && !addon.side_toggle_unlocked, locked: lockedSides.length > 0,
disabledSides: getLockedSides(addon), disabledSides: lockedSides,
disabledTooltip: formatMessage(messages.sideLocked),
warningTooltip: getEnabledForWarning(addon), warningTooltip: getEnabledForWarning(addon),
}, },
embeddedIcon, embeddedIcon,
@@ -1265,7 +1292,7 @@ function getOverflowOptions(item: ContentItem): OverflowMenuOption[] {
}) })
} }
if (addon && hasDetectedEnvironment(addon)) { if (addon && hasEnvironmentLock(addon)) {
if (options.length > 0) options.push({ type: 'divider' }) if (options.length > 0) options.push({ type: 'divider' })
options.push({ options.push({
id: 'toggle-side-lock', id: 'toggle-side-lock',
@@ -1291,7 +1318,10 @@ provideContentManager({
disableAddContent: computed(() => !canSetup.value), disableAddContent: computed(() => !canSetup.value),
disableAddContentTooltip: permissionDeniedMessage.value, disableAddContentTooltip: permissionDeniedMessage.value,
contentTypeLabel: type, contentTypeLabel: type,
toggleEnabled: handleToggleEnabled,
setEnabledFor: handleSetEnabledFor, setEnabledFor: handleSetEnabledFor,
canToggleItem: (item) =>
item.enabledFor !== undefined && item.enabledFor.server === item.enabledFor.player,
deleteItem: handleDeleteItem, deleteItem: handleDeleteItem,
bulkDeleteItems: handleBulkDelete, bulkDeleteItems: handleBulkDelete,
refresh: async () => { refresh: async () => {
@@ -1332,7 +1362,6 @@ provideContentManager({
enabled: item.enabled, enabled: item.enabled,
enabledFor: item.enabledFor, enabledFor: item.enabledFor,
embeddedIcon: item.embeddedIcon, embeddedIcon: item.embeddedIcon,
hideToggle: true,
} }
}, },
filterPersistKey: `server:${serverId}:${worldId.value}`, filterPersistKey: `server:${serverId}:${worldId.value}`,
@@ -1367,6 +1396,7 @@ provideContentManager({
:get-overflow-options="getOverflowOptions" :get-overflow-options="getOverflowOptions"
:action-disabled="contentActionDisabled" :action-disabled="contentActionDisabled"
:action-disabled-tooltip="contentActionBusyMessage ?? undefined" :action-disabled-tooltip="contentActionBusyMessage ?? undefined"
@update:enabled="handleModpackToggleEnabled"
@update:enabled-for="handleModpackSetEnabledFor" @update:enabled-for="handleModpackSetEnabledFor"
@hide="isModpackContentModalOpen = false" @hide="isModpackContentModalOpen = false"
/> />
+15 -1
View File
@@ -8,7 +8,8 @@ const meta = {
parameters: { parameters: {
docs: { docs: {
description: { description: {
component: 'Toggle uses touch manipulation so double tapping on mobile does not zoom.', component:
'Toggle uses touch manipulation so double tapping on mobile does not zoom. Set indeterminate to show a line between its on and off states.',
}, },
}, },
}, },
@@ -30,6 +31,13 @@ export const Checked: Story = {
}, },
} }
export const Indeterminate: Story = {
args: {
modelValue: false,
indeterminate: true,
},
}
export const Disabled: Story = { export const Disabled: Story = {
args: { args: {
modelValue: false, modelValue: false,
@@ -63,6 +71,9 @@ export const AllStates: Story = {
<div style="display: flex; align-items: center; gap: 0.5rem;"> <div style="display: flex; align-items: center; gap: 0.5rem;">
<Toggle :model-value="true" /> On <Toggle :model-value="true" /> On
</div> </div>
<div style="display: flex; align-items: center; gap: 0.5rem;">
<Toggle :model-value="false" :indeterminate="true" /> Indeterminate
</div>
<div style="display: flex; align-items: center; gap: 0.5rem;"> <div style="display: flex; align-items: center; gap: 0.5rem;">
<Toggle :model-value="false" :disabled="true" /> Disabled <Toggle :model-value="false" :disabled="true" /> Disabled
</div> </div>
@@ -73,6 +84,9 @@ export const AllStates: Story = {
<div style="display: flex; align-items: center; gap: 0.5rem;"> <div style="display: flex; align-items: center; gap: 0.5rem;">
<Toggle :model-value="true" :small="true" /> On <Toggle :model-value="true" :small="true" /> On
</div> </div>
<div style="display: flex; align-items: center; gap: 0.5rem;">
<Toggle :model-value="false" :indeterminate="true" :small="true" /> Indeterminate
</div>
<div style="display: flex; align-items: center; gap: 0.5rem;"> <div style="display: flex; align-items: center; gap: 0.5rem;">
<Toggle :model-value="false" :small="true" :disabled="true" /> Disabled <Toggle :model-value="false" :small="true" :disabled="true" /> Disabled
</div> </div>