@livestorm/ui-vue 2.0.5 → 4.0.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/README.md +9 -6
- package/components-vite.js +2 -2
- package/dist/UiButton-uyW8EHRJ.mjs +602 -0
- package/dist/UiTimelineItem-EiOEi1yF.mjs +7293 -0
- package/dist/UiTooltip-DZcPD6_N.mjs +158 -0
- package/dist/a11y-CaZxRWzo.mjs +6 -0
- package/dist/breakpoints.mjs +4 -9
- package/dist/bus.mjs +5 -5
- package/dist/components.mjs +236 -222
- package/dist/i18n-B8QOMuFD.mjs +46 -0
- package/dist/index.mjs +4 -87
- package/dist/integrations.mjs +3 -14
- package/dist/layouts.mjs +228 -236
- package/dist/responsive.mjs +31 -36
- package/dist/slot.mjs +11 -17
- package/dist/toastDSV2-D3xDZPe5.mjs +105 -0
- package/dist/types/index.d.ts +23 -1
- package/dist/types/src/components/Toast/ToastMessage.vue.d.ts +17 -7
- package/dist/types/src/components/Toast/toastDSV2.d.ts +89 -64
- package/dist/types/src/components/UiActionBar/UiActionBar.vue.d.ts +3 -1
- package/dist/types/src/components/UiActionBar/UiActionBarItem.vue.d.ts +18 -3
- package/dist/types/src/components/UiActionBar/types.d.ts +5 -1
- package/dist/types/src/components/UiAvatar/UiAvatar.vue.d.ts +4 -2
- package/dist/types/src/components/UiAvatarList/UiAvatarList.vue.d.ts +3 -2
- package/dist/types/src/components/UiBanner/UiBanner.vue.d.ts +13 -1
- package/dist/types/src/components/UiButton/UiButton.vue.d.ts +102 -38
- package/dist/types/src/components/UiButtonGroup/UiButtonGroup.vue.d.ts +8 -2
- package/dist/types/src/components/UiButtonGroup/UiButtonGroupItem.vue.d.ts +3 -0
- package/dist/types/src/components/UiButtonGroup/types.d.ts +5 -1
- package/dist/types/src/components/UiChoice/UiChoice.vue.d.ts +71 -0
- package/dist/types/src/components/UiChoice/index.d.ts +2 -0
- package/dist/types/src/components/UiChoice/types.d.ts +7 -0
- package/dist/types/src/components/UiColorField/UiColorField.vue.d.ts +1 -1
- package/dist/types/src/components/UiColorPicker/UiColorPicker.vue.d.ts +2 -2
- package/dist/types/src/components/UiCombobox/UiCombobox.types.d.ts +47 -0
- package/dist/types/src/components/UiCombobox/UiCombobox.vue.d.ts +40 -0
- package/dist/types/src/components/UiCombobox/index.d.ts +2 -0
- package/dist/types/src/components/UiDataTable/UiDataTable.types.d.ts +46 -0
- package/dist/types/src/components/UiDataTable/UiDataTable.vue.d.ts +136 -0
- package/dist/types/src/components/UiDataTable/index.d.ts +5 -0
- package/dist/types/src/components/UiDrawer/UiDrawer.vue.d.ts +28 -0
- package/dist/types/src/components/UiDrawer/UiDrawerContent.vue.d.ts +18 -0
- package/dist/types/src/components/UiDrawer/UiDrawerTrigger.vue.d.ts +14 -0
- package/dist/types/src/components/UiDropdown/DropdownItemCheckbox.vue.d.ts +3 -0
- package/dist/types/src/components/UiDropdown/UiDropdownContent.vue.d.ts +10 -0
- package/dist/types/src/components/UiDropdown/UiDropdownGroup.vue.d.ts +17 -0
- package/dist/types/src/components/UiDropdown/UiDropdownItem.vue.d.ts +9 -4
- package/dist/types/src/components/UiDropdown/UiDropdownTrigger.vue.d.ts +21 -5
- package/dist/types/src/components/UiDropdown/index.d.ts +1 -0
- package/dist/types/src/components/UiEmojiPickerList/UiEmojiPickerList.vue.d.ts +22 -0
- package/dist/types/src/components/UiEmptyState/UiEmptyState.vue.d.ts +8 -3
- package/dist/types/src/components/UiFieldWrapper/UiFieldWrapper.vue.d.ts +2 -2
- package/dist/types/src/components/UiInputCheckbox/CheckboxGlyph.vue.d.ts +17 -0
- package/dist/types/src/components/UiInputCheckbox/UiInputCheckbox.vue.d.ts +35 -4
- package/dist/types/src/components/UiInputEmoji/UiInputEmoji.vue.d.ts +45 -0
- package/dist/types/src/components/UiInputRadio/RadioGlyph.vue.d.ts +19 -0
- package/dist/types/src/components/UiInputRadio/UiInputRadio.vue.d.ts +80 -0
- package/dist/types/src/components/UiInputRadio/UiInputRadioGroup.vue.d.ts +86 -0
- package/dist/types/src/components/UiInputRadio/types.d.ts +19 -0
- package/dist/types/src/components/UiInputSelect/UiInputSelect.vue.d.ts +5 -5
- package/dist/types/src/components/UiInputTextarea/UiInputTextarea.vue.d.ts +5 -5
- package/dist/types/src/components/UiListItem/UiListItem.vue.d.ts +18 -4
- package/dist/types/src/components/UiLoader/UiLoader.vue.d.ts +6 -5
- package/dist/types/src/components/UiLoader/UiSpinnerIcon.vue.d.ts +3 -0
- package/dist/types/src/components/UiLogo/UiLogo.vue.d.ts +9 -3
- package/dist/types/src/components/UiMenu/UiMenuContent.vue.d.ts +15 -2
- package/dist/types/src/components/UiMenu/UiMenuItem.vue.d.ts +9 -3
- package/dist/types/src/components/UiNestedOptions/UiNestedOptions.vue.d.ts +13 -0
- package/dist/types/src/components/UiPage/UiPage.vue.d.ts +106 -8
- package/dist/types/src/components/UiPagination/UiPagination.vue.d.ts +36 -0
- package/dist/types/src/components/UiPagination/index.d.ts +2 -0
- package/dist/types/src/components/UiPagination/types.d.ts +4 -0
- package/dist/types/src/components/UiPane/UiPane.vue.d.ts +22 -0
- package/dist/types/src/components/UiPane/UiPaneContent.vue.d.ts +75 -0
- package/dist/types/src/components/UiPane/UiPaneTrigger.vue.d.ts +14 -0
- package/dist/types/src/components/UiPane/index.d.ts +5 -0
- package/dist/types/src/components/UiProgress/UiProgress.vue.d.ts +1 -1
- package/dist/types/src/components/UiRating/UiRating.vue.d.ts +62 -0
- package/dist/types/src/components/UiRating/types.d.ts +1 -0
- package/dist/types/src/components/UiRoomButton/UiRoomButton.vue.d.ts +5 -3
- package/dist/types/src/components/UiRoomCountdown/UiRoomCountdown.vue.d.ts +32 -0
- package/dist/types/src/components/UiShell/UiShell.vue.d.ts +3 -0
- package/dist/types/src/components/UiSideMenu/UiSideMenu.vue.d.ts +22 -1
- package/dist/types/src/components/UiSideMenu/UiSideMenuItemContent.vue.d.ts +2 -2
- package/dist/types/src/components/UiSwitch/UiSwitch.vue.d.ts +114 -0
- package/dist/types/src/components/UiSwitch/types.d.ts +3 -0
- package/dist/types/src/components/UiTable/UiTable.vue.d.ts +13 -4
- package/dist/types/src/components/UiTable/UiTableActionsCell.vue.d.ts +49 -0
- package/dist/types/src/components/UiTable/UiTableActionsHeaderCell.vue.d.ts +3 -0
- package/dist/types/src/components/UiTable/UiTableBody.vue.d.ts +11 -1
- package/dist/types/src/components/UiTable/UiTableCell.vue.d.ts +16 -1
- package/dist/types/src/components/UiTable/UiTableHeaderCell.vue.d.ts +31 -2
- package/dist/types/src/components/UiTable/UiTableRow.vue.d.ts +32 -2
- package/dist/types/src/components/UiTable/context.d.ts +3 -0
- package/dist/types/src/components/UiTable/index.d.ts +4 -0
- package/dist/types/src/components/UiTabs/UiTabs.vue.d.ts +8 -2
- package/dist/types/src/components/UiTag/UiTag.types.d.ts +25 -0
- package/dist/types/src/components/UiTag/UiTag.vue.d.ts +5 -26
- package/dist/types/src/components/UiTag/index.d.ts +2 -1
- package/dist/types/src/components/UiTextField/UiTextField.vue.d.ts +10 -5
- package/dist/types/src/components/UiTooltip/UiTooltipProvider.vue.d.ts +1 -0
- package/dist/types/src/components/UiTooltip/UiTooltipTrigger.vue.d.ts +2 -2
- package/dist/types/src/composables/storedThemingApplies.d.ts +17 -0
- package/dist/types/src/composables/useButtonStyles.d.ts +1 -0
- package/dist/types/src/composables/useChoiceboxSurface.d.ts +11 -0
- package/dist/types/src/composables/useContentMode.d.ts +23 -0
- package/dist/types/src/composables/useControlModes.d.ts +7 -0
- package/dist/types/src/composables/useControlShape.d.ts +24 -0
- package/dist/types/src/composables/useFieldFocusEvents.d.ts +12 -0
- package/dist/types/src/composables/useIconSize.d.ts +13 -0
- package/dist/types/src/composables/usePullGesture.d.ts +42 -0
- package/dist/types/src/composables/useThemeAndModes.d.ts +4 -0
- package/dist/types/src/constants/sizes.d.ts +1 -1
- package/dist/types/src/theming/colorScale.d.ts +165 -0
- package/dist/types/src/theming/legacyColorScale.d.ts +27 -0
- package/dist/types/src/theming/presetColors.d.ts +11 -0
- package/dist/types/src/theming/primitiveTokens.d.ts +2 -0
- package/dist/types/src/utils/effectiveOpacity.d.ts +2 -0
- package/dist/types/src/utils/numbers.d.ts +1 -2
- package/dist/types/src/utils/tokenColor.d.ts +2 -0
- package/dist/ui-vue.css +2 -1
- package/dist/utils.mjs +165 -194
- package/index.d.ts +22 -0
- package/index.js +44 -0
- package/package.json +28 -20
- package/src/components/Toast/LayoutToasts.vue +19 -11
- package/src/components/Toast/ToastMessage.vue +130 -73
- package/src/components/Toast/toastDSV2.ts +238 -67
- package/src/components/UiActionBar/UiActionBar.vue +15 -5
- package/src/components/UiActionBar/UiActionBarItem.vue +65 -38
- package/src/components/UiActionBar/types.ts +16 -1
- package/src/components/UiAvatar/UiAvatar.vue +9 -3
- package/src/components/UiAvatarList/UiAvatarList.vue +51 -20
- package/src/components/UiBadge/UiBadge.vue +1 -1
- package/src/components/UiBanner/UiBanner.vue +77 -27
- package/src/components/UiBreadcrumb/UiBreadcrumb.vue +1 -1
- package/src/components/UiButton/UiButton.vue +96 -29
- package/src/components/UiButtonGroup/UiButtonGroup.vue +22 -16
- package/src/components/UiButtonGroup/UiButtonGroupItem.vue +44 -14
- package/src/components/UiButtonGroup/types.ts +20 -1
- package/src/components/UiCard/UiCard.vue +20 -23
- package/src/components/UiCarousel/UiCarousel.vue +2 -2
- package/src/components/UiChoice/UiChoice.vue +187 -0
- package/src/components/UiChoice/index.ts +2 -0
- package/src/components/UiChoice/types.ts +11 -0
- package/src/components/UiColorField/UiColorField.vue +2 -2
- package/src/components/UiColorPicker/UiColorPicker.vue +16 -13
- package/src/components/UiCombobox/UiCombobox.types.ts +52 -0
- package/src/components/UiCombobox/UiCombobox.vue +296 -0
- package/src/components/UiCombobox/index.ts +2 -0
- package/src/components/UiDataTable/UiDataTable.types.ts +47 -0
- package/src/components/UiDataTable/UiDataTable.vue +422 -0
- package/src/components/UiDataTable/index.ts +12 -0
- package/src/components/UiDialog/UiDialogClose.vue +1 -1
- package/src/components/UiDialog/UiDialogContent.vue +22 -7
- package/src/components/UiDrawer/UiDrawer.vue +16 -0
- package/src/components/UiDrawer/UiDrawerContent.vue +42 -0
- package/src/components/UiDrawer/UiDrawerTrigger.vue +17 -0
- package/src/components/UiDropdown/DropdownItemCheckbox.vue +10 -0
- package/src/components/UiDropdown/UiDropdownContent.vue +32 -21
- package/src/components/UiDropdown/UiDropdownGroup.vue +23 -0
- package/src/components/UiDropdown/UiDropdownItem.vue +45 -18
- package/src/components/UiDropdown/UiDropdownSeparator.vue +1 -1
- package/src/components/UiDropdown/UiDropdownTrigger.vue +139 -45
- package/src/components/UiDropdown/index.ts +1 -0
- package/src/components/UiEmojiPickerList/UiEmojiPickerList.vue +122 -0
- package/src/components/UiEmptyState/UiEmptyState.vue +16 -4
- package/src/components/UiFieldWrapper/UiFieldWrapper.vue +10 -4
- package/src/components/UiHorizontalSeparator/UiHorizontalSeparator.vue +2 -2
- package/src/components/UiInputCheckbox/CheckboxGlyph.vue +78 -0
- package/src/components/UiInputCheckbox/UiInputCheckbox.vue +213 -177
- package/src/components/UiInputEmoji/UiInputEmoji.vue +96 -0
- package/src/components/UiInputRadio/RadioGlyph.vue +45 -0
- package/src/components/UiInputRadio/UiInputRadio.vue +382 -0
- package/src/components/UiInputRadio/UiInputRadioGroup.vue +166 -0
- package/src/components/UiInputRadio/types.ts +24 -0
- package/src/components/UiInputSelect/UiInputSelect.vue +9 -7
- package/src/components/UiInputTextarea/UiInputTextarea.vue +20 -35
- package/src/components/UiLabel/UiLabel.vue +1 -1
- package/src/components/UiListItem/UiListItem.vue +55 -9
- package/src/components/UiLoader/UiLoader.vue +11 -29
- package/src/components/UiLoader/UiSpinnerIcon.vue +21 -0
- package/src/components/UiLogo/UiLogo.vue +41 -7
- package/src/components/UiMenu/UiMenuContent.vue +24 -3
- package/src/components/UiMenu/UiMenuGroup.vue +1 -1
- package/src/components/UiMenu/UiMenuItem.vue +14 -3
- package/src/components/UiMenu/UiMenuSubContent.vue +1 -1
- package/src/components/UiMenu/UiMenuSubTrigger.vue +2 -2
- package/src/components/UiNestedOptions/UiNestedOptions.vue +6 -0
- package/src/components/UiPage/UiPage.vue +273 -47
- package/src/components/UiPageHeader/UiPageHeader.vue +29 -9
- package/src/components/UiPagination/UiPagination.vue +99 -0
- package/src/components/UiPagination/index.ts +2 -0
- package/src/components/UiPagination/types.ts +28 -0
- package/src/components/UiPane/UiPane.vue +20 -0
- package/src/components/UiPane/UiPaneContent.vue +232 -0
- package/src/components/UiPane/UiPaneTrigger.vue +14 -0
- package/src/components/UiPane/index.ts +12 -0
- package/src/components/UiPopover/UiPopoverContent.vue +21 -1
- package/src/components/UiProgress/UiProgress.vue +2 -2
- package/src/components/UiRating/UiRating.vue +258 -0
- package/src/components/UiRating/types.ts +1 -0
- package/src/components/UiRoomButton/UiRoomButton.vue +14 -45
- package/src/components/UiRoomCountdown/UiRoomCountdown.vue +376 -0
- package/src/components/UiShell/UiShell.vue +10 -4
- package/src/components/UiSideMenu/UiSideMenu.vue +158 -18
- package/src/components/UiSideMenu/UiSideMenuItemContent.vue +22 -18
- package/src/components/UiStepper/UiStepper.vue +3 -16
- package/src/components/UiStepper/UiStepperDescription.vue +1 -13
- package/src/components/UiStepper/UiStepperIndicator.vue +17 -57
- package/src/components/UiStepper/UiStepperItem.vue +1 -8
- package/src/components/UiStepper/UiStepperSeparator.vue +5 -16
- package/src/components/UiStepper/UiStepperTitle.vue +1 -10
- package/src/components/UiSwitch/UiSwitch.vue +375 -0
- package/src/components/UiSwitch/types.ts +5 -0
- package/src/components/UiTable/UiTable.vue +98 -8
- package/src/components/UiTable/UiTableActionsCell.vue +135 -0
- package/src/components/UiTable/UiTableActionsHeaderCell.vue +19 -0
- package/src/components/UiTable/UiTableBody.vue +16 -1
- package/src/components/UiTable/UiTableCell.vue +33 -16
- package/src/components/UiTable/UiTableHeader.vue +6 -5
- package/src/components/UiTable/UiTableHeaderCell.vue +61 -9
- package/src/components/UiTable/UiTableRow.vue +46 -10
- package/src/components/UiTable/context.ts +5 -0
- package/src/components/UiTable/index.ts +4 -0
- package/src/components/UiTabs/UiTabs.vue +61 -63
- package/src/components/UiTag/UiTag.types.ts +28 -0
- package/src/components/UiTag/UiTag.vue +24 -42
- package/src/components/UiTag/index.ts +2 -0
- package/src/components/UiTextField/UiTextField.vue +27 -47
- package/src/components/UiTimeline/README.md +6 -6
- package/src/components/UiTimeline/UiTimelineItem.vue +3 -3
- package/src/components/UiTooltip/UiTooltipContent.vue +1 -1
- package/src/components/UiTooltip/UiTooltipProvider.vue +3 -0
- package/src/components/UiTooltip/UiTooltipTrigger.vue +16 -1
- package/src/composables/storedThemingApplies.ts +19 -0
- package/src/composables/useButtonStyles.ts +9 -13
- package/src/composables/useChoiceboxSurface.ts +36 -0
- package/src/composables/useContentMode.ts +66 -0
- package/src/composables/useControlModes.ts +11 -0
- package/src/composables/useControlShape.ts +34 -0
- package/src/composables/useFieldFocusEvents.ts +30 -0
- package/src/composables/useIconSize.ts +43 -0
- package/src/composables/usePullGesture.ts +142 -0
- package/src/composables/useThemeAndModes.ts +28 -1
- package/src/constants/sizes.ts +1 -1
- package/src/css/body.css +16 -1
- package/src/css/fonts.css +39 -5
- package/src/global.d.ts +44 -0
- package/src/theming/colorScale.ts +173 -0
- package/src/theming/legacyColorScale.ts +58 -0
- package/src/theming/presetColors.ts +33 -0
- package/src/theming/primitiveTokens.ts +71 -0
- package/src/utils/effectiveOpacity.ts +8 -0
- package/src/utils/i18n.ts +6 -0
- package/src/utils/numbers.ts +1 -1
- package/src/utils/responsive.d.ts +14 -0
- package/src/utils/tokenColor.ts +9 -0
- package/dist/UiTimelineItem.vue_vue_type_style_index_0_lang-DJFdCn2R.mjs +0 -5456
- package/dist/a11y-BhF_nl7L.mjs +0 -159
- package/dist/i18n-Cktw9hIu.mjs +0 -49
- package/dist/toastDSV2-wMh-UMzW.mjs +0 -74
- package/dist/types/src/components/UiAvatar/index.d.ts +0 -1
- package/dist/types/src/components/UiAvatarList/index.d.ts +0 -1
- package/dist/types/src/components/UiBadge/index.d.ts +0 -1
- package/dist/types/src/components/UiBanner/index.d.ts +0 -2
- package/dist/types/src/components/UiButtonGroup/index.d.ts +0 -3
- package/dist/types/src/components/UiCarousel/index.d.ts +0 -2
- package/dist/types/src/components/UiColorField/index.d.ts +0 -2
- package/dist/types/src/components/UiDialog/MarketplaceHeaderExample.vue.d.ts +0 -28
- package/dist/types/src/components/UiDialog/index.d.ts +0 -7
- package/dist/types/src/components/UiFieldWrapper/index.d.ts +0 -1
- package/dist/types/src/components/UiInputCheckbox/index.d.ts +0 -2
- package/dist/types/src/components/UiInputSelect/index.d.ts +0 -2
- package/dist/types/src/components/UiInputTextarea/index.d.ts +0 -2
- package/dist/types/src/components/UiMenu/index.d.ts +0 -10
- package/dist/types/src/components/UiProgress/index.d.ts +0 -2
- package/dist/types/src/components/UiTabs/index.d.ts +0 -2
- package/dist/types/src/components/UiTimeline/index.d.ts +0 -2
- package/dist/types/src/components/UiTooltip/index.d.ts +0 -4
- package/dist/types/src/mixins/DynamicAction.d.ts +0 -30
- package/dist/useThemeAndModes-BzYwSOEv.mjs +0 -108
- package/src/components/UiAvatar/index.js +0 -1
- package/src/components/UiAvatarList/index.js +0 -1
- package/src/components/UiBadge/index.ts +0 -2
- package/src/components/UiBanner/index.ts +0 -2
- package/src/components/UiButtonGroup/index.ts +0 -3
- package/src/components/UiCarousel/index.ts +0 -2
- package/src/components/UiColorField/index.ts +0 -2
- package/src/components/UiDialog/MarketplaceHeaderExample.vue +0 -217
- package/src/components/UiDialog/index.ts +0 -18
- package/src/components/UiFieldWrapper/index.ts +0 -1
- package/src/components/UiInputCheckbox/index.ts +0 -2
- package/src/components/UiInputSelect/index.ts +0 -2
- package/src/components/UiInputTextarea/index.ts +0 -2
- package/src/components/UiMenu/index.ts +0 -10
- package/src/components/UiProgress/index.ts +0 -2
- package/src/components/UiTabs/index.ts +0 -5
- package/src/components/UiTag/index.js +0 -1
- package/src/components/UiTimeline/index.ts +0 -2
- package/src/components/UiTooltip/index.js +0 -4
- package/src/mixins/DynamicAction.js +0 -61
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
|
-
import { computed, onBeforeUnmount,
|
|
2
|
+
import { computed, onBeforeUnmount, ref, watch, getCurrentInstance, type Slot } from 'vue'
|
|
3
3
|
import { BREAKPOINT_LG } from '../../utils/breakpoints'
|
|
4
4
|
import { useResponsive } from '../../utils/responsive'
|
|
5
|
-
import
|
|
5
|
+
import { useContentMode } from '../../composables/useContentMode'
|
|
6
6
|
import { useMobileMenu } from '../../composables/useMobileMenu'
|
|
7
7
|
import UiLoader from '../UiLoader/UiLoader.vue'
|
|
8
8
|
|
|
@@ -36,35 +36,176 @@ import UiLoader from '../UiLoader/UiLoader.vue'
|
|
|
36
36
|
// │ │ └─────────────────────────────────────────────────┘ │
|
|
37
37
|
// └─┴─────────────────────────────────────────────────────┘
|
|
38
38
|
|
|
39
|
+
/**
|
|
40
|
+
* Headroom sizes, in px: the gap above the content card that lets the shell
|
|
41
|
+
* background show through. One map so the page frame has a single source of
|
|
42
|
+
* truth — retune here rather than at call sites.
|
|
43
|
+
*/
|
|
44
|
+
const HEADROOM_GAP_PX = {
|
|
45
|
+
none: 0,
|
|
46
|
+
sm: 4,
|
|
47
|
+
md: 10,
|
|
48
|
+
lg: 16,
|
|
49
|
+
xl: 24,
|
|
50
|
+
} as const
|
|
51
|
+
|
|
52
|
+
type HeadroomGap = keyof typeof HEADROOM_GAP_PX
|
|
53
|
+
|
|
54
|
+
/** Used when the card sits flush at the top and no size is given. */
|
|
55
|
+
const DEFAULT_HEADROOM_GAP: HeadroomGap = 'md'
|
|
56
|
+
|
|
39
57
|
const props = withDefaults(defineProps<{
|
|
40
|
-
/**
|
|
41
|
-
* Extra classes for the inner content `<section>`. Use to override the
|
|
42
|
-
* default surface, e.g. `bg-surface-neutral-300` instead of the standard
|
|
43
|
-
* `bg-surface-neutral-bg-main`. Appended after the defaults so it wins on
|
|
44
|
-
* conflicting utilities.
|
|
45
|
-
*/
|
|
46
|
-
pageContentClasses?: string
|
|
47
58
|
/**
|
|
48
59
|
* Page title surfaced as the label of UiShell's fallback mobile menu topbar
|
|
49
60
|
* (shown on a headerless page that has a side menu). Lets a page name its
|
|
50
61
|
* menu row — e.g. "Home" — without reaching into useMobileMenu directly.
|
|
51
62
|
*/
|
|
52
63
|
title?: string
|
|
64
|
+
/**
|
|
65
|
+
* The shape of the page inside the content card. Padded shapes use the
|
|
66
|
+
* page padding P (16px below md, 24px from md), which the page exposes to
|
|
67
|
+
* its content as `var(--ui-page-padding)`. P applies inside the card only:
|
|
68
|
+
* the page header and the tabs above it keep their own spacing.
|
|
69
|
+
* - `document` (default): the card scrolls as a single column and pads
|
|
70
|
+
* its content with P on every side. Forms, settings, content blocks.
|
|
71
|
+
* - `list`: like `document`, but the content is padded with P at the top
|
|
72
|
+
* only: toolbars, tables and pagination run from card edge to card edge
|
|
73
|
+
* and pad their sides with `--ui-page-inline`. The content is held to the
|
|
74
|
+
* card height, so a child that may shrink (`flex-1 min-h-0`) becomes the
|
|
75
|
+
* scroll region and a table header in it stays in view; anything taller
|
|
76
|
+
* still scrolls the card.
|
|
77
|
+
* - `app`: the card fills the available height and never scrolls itself;
|
|
78
|
+
* the page owns its scroll regions and layout, so the content is not
|
|
79
|
+
* padded. Each region pads itself with `var(--ui-page-padding)`.
|
|
80
|
+
*
|
|
81
|
+
* Edge-to-edge bands (toolbars, pagination) read `--ui-page-inline` for
|
|
82
|
+
* their side padding. UiDataTable pads its scroll region with it, so a
|
|
83
|
+
* wide table scrolls from card edge to card edge. It is set to P on a
|
|
84
|
+
* `list` page only and left unset everywhere else, so a band
|
|
85
|
+
* picks its inset with one class:
|
|
86
|
+
* - `px-(--ui-page-inline)`: P on a list page, 0 elsewhere.
|
|
87
|
+
* - `px-(--ui-page-inline,var(--spacing-space-4))`: P on a list page, the
|
|
88
|
+
* band's own inset elsewhere.
|
|
89
|
+
*
|
|
90
|
+
* A page with a `leftSlot` keeps the split layout insets instead of P: its
|
|
91
|
+
* header slot aligns with the aside, not with the content.
|
|
92
|
+
*/
|
|
93
|
+
variant?: 'document' | 'list' | 'app'
|
|
94
|
+
/**
|
|
95
|
+
* Column split when a leftSlot is present: `third` (default) gives the
|
|
96
|
+
* aside 1/3 and the card 2/3; `half` splits them evenly; `fill` lets the
|
|
97
|
+
* aside take all remaining width while the card hugs its content — for
|
|
98
|
+
* app pages whose card is a fixed-width side panel (e.g. the room's
|
|
99
|
+
* interactions sidepane).
|
|
100
|
+
*/
|
|
101
|
+
leftSlotRatio?: 'third' | 'half' | 'fill'
|
|
102
|
+
/**
|
|
103
|
+
* How the content card sits in the frame.
|
|
104
|
+
* - `anchored` (default): the dashboard page card — flush to the bottom and
|
|
105
|
+
* right edges, rounded top-left, page-card chrome (level-3 elevation).
|
|
106
|
+
* - `floating`: a detached card with a thin gutter on every side, rounded
|
|
107
|
+
* all around, elevated-card chrome (level-2 elevation + inner glow +
|
|
108
|
+
* container border). The room's panel treatment.
|
|
109
|
+
*/
|
|
110
|
+
frame?: 'anchored' | 'floating'
|
|
111
|
+
/**
|
|
112
|
+
* Viewport gate for the leftSlot: `lg` (default) renders the aside only
|
|
113
|
+
* from the lg breakpoint — the dashboard behavior, where the aside is
|
|
114
|
+
* secondary. `none` keeps the aside at every width, for app pages whose
|
|
115
|
+
* aside is primary content (the room's stage, the lobby's session
|
|
116
|
+
* information) and must never unmount with the viewport.
|
|
117
|
+
*/
|
|
118
|
+
leftSlotBreakpoint?: 'lg' | 'none'
|
|
119
|
+
/**
|
|
120
|
+
* Headroom above the content card, letting the shell background show
|
|
121
|
+
* through. Sizes map to px via `HEADROOM_GAP_PX`.
|
|
122
|
+
*
|
|
123
|
+
* Left unset, the card gets `DEFAULT_HEADROOM_GAP` only when it sits flush
|
|
124
|
+
* at the top — i.e. there is no page header or left slot already offsetting
|
|
125
|
+
* it. Naming a size applies it in either case, and `none` removes the gap
|
|
126
|
+
* on a page that would otherwise get the default.
|
|
127
|
+
*/
|
|
128
|
+
headroomGap?: HeadroomGap
|
|
129
|
+
/**
|
|
130
|
+
* Drops the content padding a `document` or `list` page gets by default, so the
|
|
131
|
+
* page's content reaches the card edges. Only for pages whose layout is
|
|
132
|
+
* edge-to-edge by design: full-bleed bands, or scroll regions that must
|
|
133
|
+
* sit against the card edge. Has no effect on `app` pages, which never get
|
|
134
|
+
* the padding.
|
|
135
|
+
*/
|
|
136
|
+
flush?: boolean
|
|
137
|
+
/**
|
|
138
|
+
* The fill of an anchored content card.
|
|
139
|
+
* - `container` (default): the white content surface.
|
|
140
|
+
* - `background`: the grey page background, for screens whose content is
|
|
141
|
+
* already cards or regions, so they do not sit in a second white box.
|
|
142
|
+
* A floating card keeps its elevated fill.
|
|
143
|
+
*/
|
|
144
|
+
surface?: 'container' | 'background'
|
|
53
145
|
}>(), {
|
|
54
|
-
pageContentClasses: '',
|
|
55
146
|
title: undefined,
|
|
147
|
+
variant: 'document',
|
|
148
|
+
leftSlotRatio: 'third',
|
|
149
|
+
frame: 'anchored',
|
|
150
|
+
leftSlotBreakpoint: 'lg',
|
|
151
|
+
headroomGap: undefined,
|
|
152
|
+
flush: false,
|
|
153
|
+
surface: 'container',
|
|
56
154
|
})
|
|
57
155
|
|
|
58
156
|
const slots = defineSlots<{
|
|
59
157
|
banner?(): Slot
|
|
60
158
|
pageHeader?(): Slot
|
|
61
159
|
pageTabs?(): Slot
|
|
160
|
+
/**
|
|
161
|
+
* Summary on the page background, between the page header (and tabs) and
|
|
162
|
+
* the content card, e.g. stat cards. It shares the page header's edges.
|
|
163
|
+
*/
|
|
164
|
+
pageSummary?(): Slot
|
|
62
165
|
leftSlot?(): Slot
|
|
63
166
|
header?(): Slot
|
|
64
167
|
footer?(): Slot
|
|
65
168
|
default?(): Slot
|
|
66
169
|
}>()
|
|
67
170
|
|
|
171
|
+
// An `app` page manages its own scrolling: the card clips and its row is
|
|
172
|
+
// pinned to the viewport, otherwise a split-pane editor gets a second, outer scrollbar.
|
|
173
|
+
const isApp = computed(() => props.variant === 'app')
|
|
174
|
+
const scrollClass = computed(() => (isApp.value ? 'overflow-hidden' : 'overflow-y-auto'))
|
|
175
|
+
const fillsCard = computed(() => isApp.value || props.variant === 'list')
|
|
176
|
+
|
|
177
|
+
// Spelled out as literals: Tailwind only generates classes it can see as
|
|
178
|
+
// complete strings in the source.
|
|
179
|
+
const pageColumnsClass = computed(() => {
|
|
180
|
+
const gated = props.leftSlotBreakpoint === 'lg'
|
|
181
|
+
if (props.leftSlotRatio === 'half') {
|
|
182
|
+
return gated ? 'has-[>:nth-child(2)]:lg:grid-cols-[1fr_1fr]' : 'has-[>:nth-child(2)]:grid-cols-[1fr_1fr]'
|
|
183
|
+
}
|
|
184
|
+
if (props.leftSlotRatio === 'fill') {
|
|
185
|
+
return gated ? 'has-[>:nth-child(2)]:lg:grid-cols-[minmax(0,1fr)_auto]' : 'has-[>:nth-child(2)]:grid-cols-[minmax(0,1fr)_auto]'
|
|
186
|
+
}
|
|
187
|
+
return gated ? 'has-[>:nth-child(2)]:lg:grid-cols-[1fr_2fr]' : 'has-[>:nth-child(2)]:grid-cols-[1fr_2fr]'
|
|
188
|
+
})
|
|
189
|
+
|
|
190
|
+
// Frame treatment. The chrome pairs are exclusive per frame (never merged),
|
|
191
|
+
// so conflicting utilities can't race on stylesheet order.
|
|
192
|
+
const isFloating = computed(() => props.frame === 'floating')
|
|
193
|
+
const frameGutterClass = computed(() => (isFloating.value ? 'p-1 gap-x-1' : 'lg:gap-x-8'))
|
|
194
|
+
const cardRadiusClass = computed(() => (isFloating.value ? 'rounded-container' : 'rounded-tl-container'))
|
|
195
|
+
// Anchored keeps the page-card chrome: shadow-elevation-level-3 keeps
|
|
196
|
+
// drop-shadow-2xl's exact geometry and light-mode colors, but its colors
|
|
197
|
+
// resolve to transparent in dark content mode — the card no longer draws a
|
|
198
|
+
// dark ring on the dark shell. Floating takes the elevated-card treatment
|
|
199
|
+
// from the elevation proposal (level 2 + inner glow + container border).
|
|
200
|
+
const anchoredFillClass = computed(() => (
|
|
201
|
+
props.surface === 'background' ? 'bg-surface-neutral-300' : 'bg-surface-neutral-bg-main'
|
|
202
|
+
))
|
|
203
|
+
const cardChromeClass = computed(() => (
|
|
204
|
+
isFloating.value
|
|
205
|
+
? 'bg-elevation-2 shadow-elevation-level-2 inset-shadow-inner-glow border border-container-border'
|
|
206
|
+
: `${anchoredFillClass.value} shadow-elevation-level-3`
|
|
207
|
+
))
|
|
208
|
+
|
|
68
209
|
// Check for slot presence
|
|
69
210
|
const bannerSlot = computed(() => !!slots.banner)
|
|
70
211
|
const pageHeaderSlot = computed(() => !!slots.pageHeader)
|
|
@@ -73,6 +214,80 @@ const leftSlot = computed(() => !!slots.leftSlot)
|
|
|
73
214
|
const headerSlot = computed(() => !!slots.header)
|
|
74
215
|
const footerSlot = computed(() => !!slots.footer)
|
|
75
216
|
|
|
217
|
+
const PAGE_INSETS = {
|
|
218
|
+
header: 'px-(--ui-page-padding) pt-(--ui-page-padding)',
|
|
219
|
+
headerBottom: 'pb-(--ui-page-padding)',
|
|
220
|
+
content: 'p-(--ui-page-padding)',
|
|
221
|
+
}
|
|
222
|
+
// A split page aligns its header with the aside rather than with the content.
|
|
223
|
+
const SPLIT_INSETS = {
|
|
224
|
+
header: 'px-4 pt-4 lg:px-10 lg:pt-8',
|
|
225
|
+
headerBottom: 'pb-4 lg:pb-8',
|
|
226
|
+
content: 'px-4 py-4 lg:px-10 lg:pt-8 lg:pb-10',
|
|
227
|
+
}
|
|
228
|
+
const isSplitLayout = computed(() => leftSlot.value)
|
|
229
|
+
// The header slot and the content share one side inset so their left edges align.
|
|
230
|
+
const insets = computed(() => (isSplitLayout.value ? SPLIT_INSETS : PAGE_INSETS))
|
|
231
|
+
|
|
232
|
+
const pageInlineClass = computed(() => (props.variant === 'list' ? '[--ui-page-inline:var(--ui-page-padding)]' : ''))
|
|
233
|
+
|
|
234
|
+
const isContentPadded = computed(() => !isApp.value && !props.flush)
|
|
235
|
+
const contentPaddingClass = computed(() => {
|
|
236
|
+
if (!isContentPadded.value) return ''
|
|
237
|
+
return props.variant === 'list' ? 'pt-(--ui-page-padding)' : insets.value.content
|
|
238
|
+
})
|
|
239
|
+
// The padded content's top padding already spaces it from the header.
|
|
240
|
+
const headerBottomPaddingClass = computed(() => (isContentPadded.value ? '' : insets.value.headerBottom))
|
|
241
|
+
|
|
242
|
+
// Who scrolls: the page-content row, or the card inside it?
|
|
243
|
+
// - `app`: neither — the page owns its scroll regions. The row is pinned to the
|
|
244
|
+
// viewport but left unclipped, or it would eat the card's elevation shadow.
|
|
245
|
+
// - leftSlot layout: the row. The aside can be taller than the viewport and
|
|
246
|
+
// scrolls together with the card (historical auth-page behavior). The 1px
|
|
247
|
+
// top padding keeps the card's elevation ring inside the scrollport.
|
|
248
|
+
// - plain `document`: the card. Its row is pinned to the viewport height
|
|
249
|
+
// (minmax(0,1fr)) so the card's own overflow-y-auto engages, and the row
|
|
250
|
+
// stays unclipped — a scrollport would clip the card's elevation shadow
|
|
251
|
+
// at the flush edges.
|
|
252
|
+
const pageContentScrollClass = computed(() => {
|
|
253
|
+
if (isApp.value) return 'overflow-visible grid-rows-[minmax(0,1fr)]'
|
|
254
|
+
return leftSlot.value
|
|
255
|
+
? 'overflow-y-auto pt-px'
|
|
256
|
+
: 'overflow-visible grid-rows-[minmax(0,1fr)]'
|
|
257
|
+
})
|
|
258
|
+
|
|
259
|
+
// Row track for the card grid. An `app` or `list` page needs its main row to take the
|
|
260
|
+
// leftover height and be allowed to shrink (minmax(0,1fr)), so nested
|
|
261
|
+
// overflow containers resolve against a real height instead of growing.
|
|
262
|
+
// Spelled out as literals: Tailwind only generates classes it can see as
|
|
263
|
+
// complete strings in the source.
|
|
264
|
+
const cardRowsClass = computed(() => {
|
|
265
|
+
if (!fillsCard.value) { return headerSlot.value ? 'grid-rows-[min-content]' : '' }
|
|
266
|
+
if (headerSlot.value && footerSlot.value) { return 'grid-rows-[min-content_minmax(0,1fr)_min-content]' }
|
|
267
|
+
if (headerSlot.value) { return 'grid-rows-[min-content_minmax(0,1fr)]' }
|
|
268
|
+
if (footerSlot.value) { return 'grid-rows-[minmax(0,1fr)_min-content]' }
|
|
269
|
+
return 'grid-rows-[minmax(0,1fr)]'
|
|
270
|
+
})
|
|
271
|
+
|
|
272
|
+
// A named size always wins; otherwise fall back to the default, and only when
|
|
273
|
+
// the card is flush at the top (a page header or left slot already provides
|
|
274
|
+
// the offset). Applied as an inline style rather than a utility so the value
|
|
275
|
+
// does not depend on Tailwind's `--spacing` fallback resolving to 0.25rem.
|
|
276
|
+
// A zero gap emits no style at all, so `none` and "already offset" produce
|
|
277
|
+
// identical markup.
|
|
278
|
+
//
|
|
279
|
+
// Padding on the clipping wrapper rather than a margin on the card: the card
|
|
280
|
+
// is `h-full`, so a margin would push a full-height box down inside a fixed
|
|
281
|
+
// height, overflowing by exactly the gap and clipping that much off the
|
|
282
|
+
// bottom. As padding it shrinks the box the card measures itself against.
|
|
283
|
+
const headroomGapStyle = computed(() => {
|
|
284
|
+
const size = props.headroomGap ?? (
|
|
285
|
+
!pageHeaderSlot.value && !leftSlot.value ? DEFAULT_HEADROOM_GAP : 'none'
|
|
286
|
+
)
|
|
287
|
+
const px = HEADROOM_GAP_PX[size]
|
|
288
|
+
return px ? { paddingTop: `${px}px` } : undefined
|
|
289
|
+
})
|
|
290
|
+
|
|
76
291
|
// Feed the page title to UiShell's fallback mobile menu topbar. Watched so a
|
|
77
292
|
// reactive title updates live, and cleared on unmount so it doesn't leak to
|
|
78
293
|
// the next route.
|
|
@@ -87,33 +302,16 @@ if (!app) {
|
|
|
87
302
|
// Prefer the app-installed $responsive (it may carry custom flags); fall
|
|
88
303
|
// back to the shared singleton so no plugin registration is required.
|
|
89
304
|
const responsive = computed(() => app.appContext.config.globalProperties.$responsive ?? useResponsive())
|
|
90
|
-
const displayLeftSlot = computed(() =>
|
|
305
|
+
const displayLeftSlot = computed(() => (
|
|
306
|
+
props.leftSlotBreakpoint === 'none' || responsive.value.width >= BREAKPOINT_LG
|
|
307
|
+
))
|
|
91
308
|
|
|
92
309
|
const UiPage = ref<HTMLElement | undefined>()
|
|
93
310
|
const PageContent = ref<HTMLElement | undefined>()
|
|
94
311
|
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
// the internal theming tooling is enabled, so the stored content mode
|
|
99
|
-
// (driven by the ThemePicker) can apply; for everyone else the content
|
|
100
|
-
// stays light regardless of what localStorage may hold.
|
|
101
|
-
// Read in onMounted, not at setup: setup also runs during SSR (no
|
|
102
|
-
// localStorage), and server + client-hydration markup must be identical —
|
|
103
|
-
// the pin lifts right after mount for the internal tooling instead.
|
|
104
|
-
const isInternalThemingEnabled = ref(false)
|
|
105
|
-
|
|
106
|
-
onMounted(() => {
|
|
107
|
-
isInternalThemingEnabled.value = localStorage.getItem('ls-internal-theming') === 'true'
|
|
108
|
-
|
|
109
|
-
addElementToWatch(
|
|
110
|
-
'content',
|
|
111
|
-
{
|
|
112
|
-
selector: '[data-name="ui-page-content"]',
|
|
113
|
-
element: PageContent.value!,
|
|
114
|
-
...(isInternalThemingEnabled.value ? {} : { modeOverride: 'light' }),
|
|
115
|
-
})
|
|
116
|
-
})
|
|
312
|
+
// Follow the stored content mode via the shared composable (also used by the
|
|
313
|
+
// settings shell), so the page content's data-colors-semantic stays in sync.
|
|
314
|
+
const { contentMode } = useContentMode(PageContent, '[data-name="ui-page-content"]')
|
|
117
315
|
|
|
118
316
|
</script>
|
|
119
317
|
|
|
@@ -121,7 +319,10 @@ onMounted(() => {
|
|
|
121
319
|
<main
|
|
122
320
|
ref="UiPage"
|
|
123
321
|
data-name="ui-page-wrapper"
|
|
124
|
-
class="
|
|
322
|
+
:class="[
|
|
323
|
+
'ui-page h-full flex flex-col [--ui-page-padding:var(--spacing-space-4)] md:[--ui-page-padding:var(--spacing-space-6)]',
|
|
324
|
+
pageInlineClass,
|
|
325
|
+
]"
|
|
125
326
|
>
|
|
126
327
|
<section
|
|
127
328
|
v-if="bannerSlot"
|
|
@@ -137,7 +338,7 @@ onMounted(() => {
|
|
|
137
338
|
v-if="pageHeaderSlot"
|
|
138
339
|
ref="pageHeader"
|
|
139
340
|
data-name="pageHeader"
|
|
140
|
-
class="page-header p-4 md:
|
|
341
|
+
class="page-header p-4 md:px-8 md:py-6 md:pl-0 relative"
|
|
141
342
|
>
|
|
142
343
|
<slot name="pageHeader" />
|
|
143
344
|
</section>
|
|
@@ -153,10 +354,21 @@ onMounted(() => {
|
|
|
153
354
|
</section>
|
|
154
355
|
|
|
155
356
|
<section
|
|
156
|
-
|
|
357
|
+
v-if="$slots.pageSummary"
|
|
358
|
+
data-name="pageSummary"
|
|
359
|
+
class="px-4 pb-(--ui-page-padding) md:pl-0 md:pr-8"
|
|
360
|
+
>
|
|
361
|
+
<slot name="pageSummary" />
|
|
362
|
+
</section>
|
|
363
|
+
|
|
364
|
+
<section
|
|
157
365
|
data-name="pageContent"
|
|
158
|
-
class="
|
|
159
|
-
|
|
366
|
+
:class="[
|
|
367
|
+
'page-content flex-1 w-full grid min-h-0',
|
|
368
|
+
frameGutterClass,
|
|
369
|
+
pageColumnsClass,
|
|
370
|
+
pageContentScrollClass,
|
|
371
|
+
]"
|
|
160
372
|
>
|
|
161
373
|
<aside
|
|
162
374
|
v-if="leftSlot && displayLeftSlot"
|
|
@@ -166,29 +378,43 @@ onMounted(() => {
|
|
|
166
378
|
<slot name="leftSlot" />
|
|
167
379
|
</aside>
|
|
168
380
|
|
|
169
|
-
|
|
381
|
+
<!-- No overflow-hidden here: the card handles its own content
|
|
382
|
+
clipping via scrollClass, and clipping this wrapper would eat
|
|
383
|
+
the card's elevation shadow on every side but the top. -->
|
|
384
|
+
<div
|
|
385
|
+
ref="PageContent"
|
|
386
|
+
data-name="ui-page-content"
|
|
387
|
+
:class="['min-w-0', cardRadiusClass]"
|
|
388
|
+
:style="headroomGapStyle"
|
|
389
|
+
:data-colors-semantic="contentMode"
|
|
390
|
+
>
|
|
170
391
|
<section
|
|
171
392
|
:class="[
|
|
172
|
-
'content min-w-0
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
props.pageContentClasses,
|
|
393
|
+
'content min-w-0 h-full grid [view-transition-name:ui-page-card]',
|
|
394
|
+
cardChromeClass,
|
|
395
|
+
cardRadiusClass,
|
|
396
|
+
scrollClass,
|
|
397
|
+
cardRowsClass,
|
|
178
398
|
]"
|
|
179
399
|
data-name="ui-shell-content"
|
|
180
400
|
>
|
|
181
401
|
<section
|
|
182
402
|
v-if="headerSlot"
|
|
183
403
|
data-name="header"
|
|
184
|
-
class="content-header
|
|
404
|
+
:class="['content-header', insets.header, headerBottomPaddingClass]"
|
|
185
405
|
>
|
|
186
406
|
<slot name="header" />
|
|
187
407
|
</section>
|
|
188
408
|
|
|
189
409
|
<section
|
|
190
410
|
data-name="main-content"
|
|
191
|
-
class="
|
|
411
|
+
:class="[
|
|
412
|
+
'route-content w-full min-w-0 flex flex-col',
|
|
413
|
+
contentPaddingClass,
|
|
414
|
+
// Let the row shrink so an editor's own overflow containers
|
|
415
|
+
// resolve against a real height. Document pages keep growing.
|
|
416
|
+
fillsCard ? 'min-h-0' : '',
|
|
417
|
+
]"
|
|
192
418
|
>
|
|
193
419
|
<slot>
|
|
194
420
|
<UiLoader />
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
2
|
import { computed, onBeforeUnmount, useSlots, watch, type Slot } from 'vue'
|
|
3
|
-
import LivestormLogo from '@livestorm/ui-tokens/output/vectors/logos/livestorm-white.svg'
|
|
4
3
|
import UiButton from '../UiButton/UiButton.vue'
|
|
4
|
+
import UiLogo from '../UiLogo/UiLogo.vue'
|
|
5
5
|
import { useMobileMenu } from '../../composables/useMobileMenu'
|
|
6
6
|
import type { RouteLocationRaw } from 'vue-router'
|
|
7
7
|
|
|
@@ -65,8 +65,15 @@ onBeforeUnmount(() => syncTrigger(false))
|
|
|
65
65
|
</script>
|
|
66
66
|
|
|
67
67
|
<template>
|
|
68
|
+
<!--
|
|
69
|
+
min-h-10 keeps the header a stable height whether or not a page fills the
|
|
70
|
+
action slots. Without it the row is sized by its tallest child — a bare
|
|
71
|
+
title is 32px but an action button is 40px — so the header, and the content
|
|
72
|
+
card below it, shifted by 8px between pages like Events and People.
|
|
73
|
+
A floor, not a fixed height: taller or wrapped content still grows.
|
|
74
|
+
-->
|
|
68
75
|
<div
|
|
69
|
-
class="flex flex-1 min-w-0 justify-between gap-5 md:gap-8 items-center"
|
|
76
|
+
class="flex flex-1 min-w-0 justify-between gap-5 md:gap-8 items-center min-h-10"
|
|
70
77
|
>
|
|
71
78
|
<div class="flex min-w-0 items-center gap-2">
|
|
72
79
|
<div v-if="showLogo">
|
|
@@ -74,11 +81,13 @@ onBeforeUnmount(() => syncTrigger(false))
|
|
|
74
81
|
:to="logoTo ? logoTo : '#'"
|
|
75
82
|
class="grid items-center w-8 h-8"
|
|
76
83
|
>
|
|
77
|
-
<
|
|
78
|
-
:
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
84
|
+
<UiLogo
|
|
85
|
+
:height="1.5"
|
|
86
|
+
:width="1.5"
|
|
87
|
+
:themed="false"
|
|
88
|
+
:animated="false"
|
|
89
|
+
inverted="auto"
|
|
90
|
+
/>
|
|
82
91
|
</router-link>
|
|
83
92
|
</div>
|
|
84
93
|
<div
|
|
@@ -88,7 +97,7 @@ onBeforeUnmount(() => syncTrigger(false))
|
|
|
88
97
|
<UiButton
|
|
89
98
|
color="neutral"
|
|
90
99
|
variant="tertiary"
|
|
91
|
-
icon="i-
|
|
100
|
+
icon="i-ph-list"
|
|
92
101
|
@click="toggle"
|
|
93
102
|
/>
|
|
94
103
|
</div>
|
|
@@ -96,7 +105,7 @@ onBeforeUnmount(() => syncTrigger(false))
|
|
|
96
105
|
<UiButton
|
|
97
106
|
color="neutral"
|
|
98
107
|
variant="tertiary"
|
|
99
|
-
icon="i-
|
|
108
|
+
icon="i-ph-arrow-left"
|
|
100
109
|
/>
|
|
101
110
|
</div>
|
|
102
111
|
<div
|
|
@@ -124,5 +133,16 @@ onBeforeUnmount(() => syncTrigger(false))
|
|
|
124
133
|
>
|
|
125
134
|
<slot name="slotB" />
|
|
126
135
|
</div>
|
|
136
|
+
|
|
137
|
+
<!--
|
|
138
|
+
Teleport target for page-level actions, so a component deep inside the
|
|
139
|
+
page content (e.g. a settings section) can surface its primary action
|
|
140
|
+
here without threading props up. `contents` keeps the empty target out
|
|
141
|
+
of the flex layout, so pages that don't use it are unaffected.
|
|
142
|
+
-->
|
|
143
|
+
<div
|
|
144
|
+
id="ui-page-header-actions-teleport-target"
|
|
145
|
+
class="contents"
|
|
146
|
+
/>
|
|
127
147
|
</div>
|
|
128
148
|
</template>
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import { computed } from 'vue'
|
|
3
|
+
import { PaginationEllipsis, PaginationList, PaginationListItem, PaginationNext, PaginationPrev, PaginationRoot } from 'reka-ui'
|
|
4
|
+
import { PAGINATION_ACTIVE_CLASSES, PAGINATION_IDLE_CLASSES, PAGINATION_RAIL_CLASSES } from './types'
|
|
5
|
+
import type { UiPaginationColor } from './types'
|
|
6
|
+
|
|
7
|
+
interface UiPaginationProps {
|
|
8
|
+
/** Controlled current page. Bind with `v-model:page`. */
|
|
9
|
+
page?: number
|
|
10
|
+
/** Initial page when uncontrolled. */
|
|
11
|
+
defaultPage?: number
|
|
12
|
+
/** Number of items per page. */
|
|
13
|
+
itemsPerPage?: number
|
|
14
|
+
/** Total number of items across all pages. */
|
|
15
|
+
total: number
|
|
16
|
+
/** Number of sibling pages shown around the current page. */
|
|
17
|
+
siblingCount?: number
|
|
18
|
+
disabled?: boolean
|
|
19
|
+
/** Always show the first and last page, with an ellipsis in between when needed. */
|
|
20
|
+
showEdges?: boolean
|
|
21
|
+
color?: UiPaginationColor
|
|
22
|
+
previousLabel?: string
|
|
23
|
+
nextLabel?: string
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
const props = withDefaults(defineProps<UiPaginationProps>(), {
|
|
27
|
+
page: undefined,
|
|
28
|
+
defaultPage: 1,
|
|
29
|
+
itemsPerPage: 10,
|
|
30
|
+
siblingCount: 1,
|
|
31
|
+
disabled: false,
|
|
32
|
+
showEdges: true,
|
|
33
|
+
color: 'neutral',
|
|
34
|
+
previousLabel: 'Previous page',
|
|
35
|
+
nextLabel: 'Next page',
|
|
36
|
+
})
|
|
37
|
+
|
|
38
|
+
defineEmits<{
|
|
39
|
+
'update:page': [value: number]
|
|
40
|
+
}>()
|
|
41
|
+
|
|
42
|
+
const idleClasses = computed(() => PAGINATION_IDLE_CLASSES[props.color])
|
|
43
|
+
const activeClasses = computed(() => PAGINATION_ACTIVE_CLASSES[props.color])
|
|
44
|
+
const railClasses = computed(() => PAGINATION_RAIL_CLASSES[props.color])
|
|
45
|
+
</script>
|
|
46
|
+
|
|
47
|
+
<template>
|
|
48
|
+
<PaginationRoot
|
|
49
|
+
:page="page"
|
|
50
|
+
:default-page="defaultPage"
|
|
51
|
+
:items-per-page="itemsPerPage"
|
|
52
|
+
:total="total"
|
|
53
|
+
:sibling-count="siblingCount"
|
|
54
|
+
:disabled="disabled"
|
|
55
|
+
:show-edges="showEdges"
|
|
56
|
+
class="inline-flex items-center gap-2"
|
|
57
|
+
@update:page="$emit('update:page', $event)"
|
|
58
|
+
>
|
|
59
|
+
<PaginationPrev
|
|
60
|
+
class="flex h-10 shrink-0 items-center justify-center rounded-button-lg px-2.5 outline-none transition-colors focus-visible:focus-ring disabled:pointer-events-none disabled:opacity-50"
|
|
61
|
+
:class="idleClasses"
|
|
62
|
+
:aria-label="previousLabel"
|
|
63
|
+
>
|
|
64
|
+
<span class="i-ph-caret-left size-4" />
|
|
65
|
+
</PaginationPrev>
|
|
66
|
+
|
|
67
|
+
<PaginationList
|
|
68
|
+
v-slot="{ items }"
|
|
69
|
+
class="flex h-10 shrink-0 items-center gap-1 rounded-button-lg p-0.5"
|
|
70
|
+
:class="railClasses"
|
|
71
|
+
>
|
|
72
|
+
<template
|
|
73
|
+
v-for="(item, index) in items"
|
|
74
|
+
:key="item.type === 'page' ? `page-${item.value}` : `ellipsis-${index}`"
|
|
75
|
+
>
|
|
76
|
+
<PaginationListItem
|
|
77
|
+
v-if="item.type === 'page'"
|
|
78
|
+
:value="item.value"
|
|
79
|
+
class="flex h-full items-center justify-center rounded-[calc(var(--radius-button-lg)_-_2px)] px-3 text-action-button-md outline-none transition-colors focus-visible:focus-ring"
|
|
80
|
+
:class="[idleClasses, activeClasses]"
|
|
81
|
+
>
|
|
82
|
+
{{ item.value }}
|
|
83
|
+
</PaginationListItem>
|
|
84
|
+
<PaginationEllipsis
|
|
85
|
+
v-else
|
|
86
|
+
class="flex h-full items-center justify-center px-3 text-action-button-md text-actions-neutral-idle"
|
|
87
|
+
/>
|
|
88
|
+
</template>
|
|
89
|
+
</PaginationList>
|
|
90
|
+
|
|
91
|
+
<PaginationNext
|
|
92
|
+
class="flex h-10 shrink-0 items-center justify-center rounded-button-lg px-2.5 outline-none transition-colors focus-visible:focus-ring disabled:pointer-events-none disabled:opacity-50"
|
|
93
|
+
:class="idleClasses"
|
|
94
|
+
:aria-label="nextLabel"
|
|
95
|
+
>
|
|
96
|
+
<span class="i-ph-caret-right size-4" />
|
|
97
|
+
</PaginationNext>
|
|
98
|
+
</PaginationRoot>
|
|
99
|
+
</template>
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
export type UiPaginationColor = 'neutral' | 'neutral-invert' | 'primary' | 'success' | 'warning' | 'error'
|
|
2
|
+
|
|
3
|
+
export const PAGINATION_IDLE_CLASSES: Record<UiPaginationColor, string> = {
|
|
4
|
+
'neutral': 'text-actions-neutral-idle hover:bg-actions-neutral-hover-overlay',
|
|
5
|
+
'neutral-invert': 'text-actions-neutral-invert-idle-alpha-strong hover:bg-actions-neutral-invert-hover-overlay hover:text-actions-neutral-invert-idle',
|
|
6
|
+
'primary': 'text-actions-primary-idle-alpha-strong hover:bg-actions-primary-hover-overlay hover:text-actions-primary-idle',
|
|
7
|
+
'success': 'text-actions-success-idle-alpha-strong hover:bg-actions-success-hover-overlay hover:text-actions-success-idle',
|
|
8
|
+
'warning': 'text-actions-warning-idle-alpha-strong hover:bg-actions-warning-hover-overlay hover:text-actions-warning-idle',
|
|
9
|
+
'error': 'text-actions-danger-idle-alpha-strong hover:bg-actions-danger-hover-overlay hover:text-actions-danger-idle',
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export const PAGINATION_ACTIVE_CLASSES: Record<UiPaginationColor, string> = {
|
|
13
|
+
'neutral': 'data-[selected=true]:bg-actions-neutral-idle data-[selected=true]:text-actions-neutral-invert-idle data-[selected=true]:hover:bg-actions-neutral-idle',
|
|
14
|
+
'neutral-invert': 'data-[selected=true]:bg-actions-neutral-invert-idle data-[selected=true]:text-actions-neutral-idle data-[selected=true]:hover:bg-actions-neutral-invert-idle',
|
|
15
|
+
'primary': 'data-[selected=true]:bg-actions-primary-idle data-[selected=true]:text-actions-neutral-invert-idle data-[selected=true]:hover:bg-actions-primary-idle',
|
|
16
|
+
'success': 'data-[selected=true]:bg-actions-success-idle data-[selected=true]:text-actions-neutral-invert-idle data-[selected=true]:hover:bg-actions-success-idle',
|
|
17
|
+
'warning': 'data-[selected=true]:bg-actions-warning-idle data-[selected=true]:text-actions-neutral-invert-idle data-[selected=true]:hover:bg-actions-warning-idle',
|
|
18
|
+
'error': 'data-[selected=true]:bg-actions-danger-idle data-[selected=true]:text-actions-neutral-invert-idle data-[selected=true]:hover:bg-actions-danger-idle',
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export const PAGINATION_RAIL_CLASSES: Record<UiPaginationColor, string> = {
|
|
22
|
+
'neutral': 'bg-actions-neutral-idle-alpha-light',
|
|
23
|
+
'neutral-invert': 'bg-actions-neutral-invert-idle-alpha-light',
|
|
24
|
+
'primary': 'bg-actions-primary-idle-alpha-light',
|
|
25
|
+
'success': 'bg-actions-success-idle-alpha-light',
|
|
26
|
+
'warning': 'bg-actions-warning-idle-alpha-light',
|
|
27
|
+
'error': 'bg-actions-danger-idle-alpha-light',
|
|
28
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import type { DialogRootEmits, DialogRootProps } from 'reka-ui'
|
|
3
|
+
import { DialogRoot, useForwardPropsEmits } from 'reka-ui'
|
|
4
|
+
|
|
5
|
+
// A pane floats above the interface without an overlay, so it is non-modal by default.
|
|
6
|
+
const props = withDefaults(defineProps<DialogRootProps>(), {
|
|
7
|
+
open: undefined,
|
|
8
|
+
defaultOpen: false,
|
|
9
|
+
modal: false,
|
|
10
|
+
})
|
|
11
|
+
const emits = defineEmits<DialogRootEmits>()
|
|
12
|
+
|
|
13
|
+
const forwarded = useForwardPropsEmits(props, emits)
|
|
14
|
+
</script>
|
|
15
|
+
|
|
16
|
+
<template>
|
|
17
|
+
<DialogRoot v-bind="{ ...forwarded, ...$attrs }">
|
|
18
|
+
<slot />
|
|
19
|
+
</DialogRoot>
|
|
20
|
+
</template>
|