diff --git a/packages/api-generator/src/locale/en/VFab.json b/packages/api-generator/src/locale/en/VFab.json index d40545ee82b..e62d1349ff4 100644 --- a/packages/api-generator/src/locale/en/VFab.json +++ b/packages/api-generator/src/locale/en/VFab.json @@ -1,6 +1,7 @@ { "props": { "app": "If true, attaches to the closest layout and positions according to the value of **location**.", + "layout": "If true, will effect layout dimensions based on size and position.", "appear": "Used to control the animation of the FAB.", "extended": "An alternate style for the FAB that expects text.", "location": "The location of the fab relative to the layout. Only works when using **app**.", diff --git a/packages/api-generator/src/locale/en/VSnackbarQueue.json b/packages/api-generator/src/locale/en/VSnackbarQueue.json new file mode 100644 index 00000000000..79a68190bc7 --- /dev/null +++ b/packages/api-generator/src/locale/en/VSnackbarQueue.json @@ -0,0 +1,6 @@ +{ + "props": { + "closable": "Adds a dismiss button that closes the active snackbar.", + "closeText": "The text used in the close button when using the **closable** prop." + } +} diff --git a/packages/docs/components.d.ts b/packages/docs/components.d.ts index 1f0087215d5..f34eac93cda 100644 --- a/packages/docs/components.d.ts +++ b/packages/docs/components.d.ts @@ -24,8 +24,6 @@ declare module 'vue' { ApiSection: typeof import('./src/components/api/Section.vue')['default'] ApiSlotsTable: typeof import('./src/components/api/SlotsTable.vue')['default'] AppBackToTop: typeof import('./src/components/app/BackToTop.vue')['default'] - AppBanner: typeof import('./src/components/app/Banner.vue')['default'] - AppBarAuthDialog: typeof import('./src/components/app/bar/AuthDialog.vue')['default'] AppBarBar: typeof import('./src/components/app/bar/Bar.vue')['default'] AppBarEcosystemMenu: typeof import('./src/components/app/bar/EcosystemMenu.vue')['default'] AppBarEnterpriseLink: typeof import('./src/components/app/bar/EnterpriseLink.vue')['default'] @@ -79,12 +77,10 @@ declare module 'vue' { AppSettingsOptionsQuickbarOption: typeof import('./src/components/app/settings/options/QuickbarOption.vue')['default'] AppSettingsOptionsRailDrawerOption: typeof import('./src/components/app/settings/options/RailDrawerOption.vue')['default'] AppSettingsOptionsSlashSearchOption: typeof import('./src/components/app/settings/options/SlashSearchOption.vue')['default'] - AppSettingsOptionsSyncOption: typeof import('./src/components/app/settings/options/SyncOption.vue')['default'] AppSettingsOptionsThemeOption: typeof import('./src/components/app/settings/options/ThemeOption.vue')['default'] AppSettingsPerksOptions: typeof import('./src/components/app/settings/PerksOptions.vue')['default'] AppSettingsSettingsHeader: typeof import('./src/components/app/settings/SettingsHeader.vue')['default'] AppSheet: typeof import('./src/components/app/Sheet.vue')['default'] - AppSnackbarQueue: typeof import('./src/components/app/SnackbarQueue.vue')['default'] AppTable: typeof import('./src/components/app/Table.vue')['default'] AppTextField: typeof import('./src/components/app/TextField.vue')['default'] AppTitle: typeof import('./src/components/app/Title.vue')['default'] @@ -153,16 +149,10 @@ declare module 'vue' { SponsorCard: typeof import('./src/components/sponsor/Card.vue')['default'] SponsorLink: typeof import('./src/components/sponsor/Link.vue')['default'] SponsorSponsors: typeof import('./src/components/sponsor/Sponsors.vue')['default'] - UserAccountConnectedAccounts: typeof import('./src/components/user/account/ConnectedAccounts.vue')['default'] - UserAccountOneSubscription: typeof import('./src/components/user/account/OneSubscription.vue')['default'] UserBadgesUserAdminBadge: typeof import('./src/components/user/badges/UserAdminBadge.vue')['default'] UserBadgesUserOneBadge: typeof import('./src/components/user/badges/UserOneBadge.vue')['default'] UserBadgesUserSponsorBadge: typeof import('./src/components/user/badges/UserSponsorBadge.vue')['default'] - UserDiscordLogin: typeof import('./src/components/user/DiscordLogin.vue')['default'] - UserGithubLogin: typeof import('./src/components/user/GithubLogin.vue')['default'] UserOneSubCard: typeof import('./src/components/user/OneSubCard.vue')['default'] - UserUserBadges: typeof import('./src/components/user/UserBadges.vue')['default'] - UserUserProfile: typeof import('./src/components/user/UserProfile.vue')['default'] UserUserTabs: typeof import('./src/components/user/UserTabs.vue')['default'] } } diff --git a/packages/docs/package.json b/packages/docs/package.json index a4c69c1a5f2..a658b7ccb43 100644 --- a/packages/docs/package.json +++ b/packages/docs/package.json @@ -23,7 +23,7 @@ "@cosmicjs/sdk": "^1.0.11", "@vuelidate/core": "^2.0.3", "@vuelidate/validators": "^2.0.4", - "@vuetify/one": "^1.7.0", + "@vuetify/one": "^1.7.2", "algoliasearch": "^4.23.2", "fflate": "^0.8.2", "isomorphic-fetch": "^3.0.0", diff --git a/packages/docs/src/components/app/BackToTop.vue b/packages/docs/src/components/app/BackToTop.vue index 5865a6309b8..22a9f831643 100644 --- a/packages/docs/src/components/app/BackToTop.vue +++ b/packages/docs/src/components/app/BackToTop.vue @@ -1,25 +1,14 @@ - - diff --git a/packages/docs/src/components/app/SnackbarQueue.vue b/packages/docs/src/components/app/SnackbarQueue.vue deleted file mode 100644 index 94558df38d0..00000000000 --- a/packages/docs/src/components/app/SnackbarQueue.vue +++ /dev/null @@ -1,47 +0,0 @@ - - - diff --git a/packages/docs/src/data/nav.json b/packages/docs/src/data/nav.json index 14cacacc52e..097e219856a 100644 --- a/packages/docs/src/data/nav.json +++ b/packages/docs/src/data/nav.json @@ -243,6 +243,10 @@ "title": "number-inputs", "subfolder": "components" }, + { + "title": "snackbar-queue", + "subfolder": "components" + }, { "title": "sparklines", "subfolder": "components" diff --git a/packages/docs/src/data/page-to-api.json b/packages/docs/src/data/page-to-api.json index e4a010c26c2..be62a3cf190 100644 --- a/packages/docs/src/data/page-to-api.json +++ b/packages/docs/src/data/page-to-api.json @@ -125,6 +125,7 @@ "components/slide-groups": ["VSlideGroup", "VSlideGroupItem"], "components/sliders": ["VRangeSlider", "VSlider"], "components/snackbars": ["VSnackbar"], + "components/snackbar-queue": ["VSnackbarQueue", "VSnackbar"], "components/sparklines": ["VSparkline"], "components/steppers": [ "VStepper", diff --git a/packages/docs/src/examples/v-card/prop-variant.vue b/packages/docs/src/examples/v-card/prop-variant.vue index eacd275296f..d32f16307db 100644 --- a/packages/docs/src/examples/v-card/prop-variant.vue +++ b/packages/docs/src/examples/v-card/prop-variant.vue @@ -10,6 +10,7 @@ :variant="variant" class="mx-auto" color="surface-variant" + max-width="344" subtitle="Greyhound divisely hello coldly fonwderfully" title="Headline" > diff --git a/packages/docs/src/examples/v-navigation-drawer/prop-bottom-drawer.vue b/packages/docs/src/examples/v-navigation-drawer/prop-bottom-drawer.vue index 38a2a43af7d..6043ddcd14e 100644 --- a/packages/docs/src/examples/v-navigation-drawer/prop-bottom-drawer.vue +++ b/packages/docs/src/examples/v-navigation-drawer/prop-bottom-drawer.vue @@ -1,8 +1,6 @@ + + diff --git a/packages/docs/src/layouts/default.vue b/packages/docs/src/layouts/default.vue index 14175b6f4f0..50f5540a07c 100644 --- a/packages/docs/src/layouts/default.vue +++ b/packages/docs/src/layouts/default.vue @@ -12,8 +12,6 @@ - - - - diff --git a/packages/docs/src/layouts/user.vue b/packages/docs/src/layouts/user.vue index b95a41e725f..d4034c8e830 100644 --- a/packages/docs/src/layouts/user.vue +++ b/packages/docs/src/layouts/user.vue @@ -8,8 +8,6 @@ - - diff --git a/packages/docs/src/pages/en/components/infinite-scroller.md b/packages/docs/src/pages/en/components/infinite-scroller.md index 1fb69f6467b..42ce7cd6769 100644 --- a/packages/docs/src/pages/en/components/infinite-scroller.md +++ b/packages/docs/src/pages/en/components/infinite-scroller.md @@ -34,7 +34,7 @@ When scrolling towards the bottom, new items will be rendered either automatical A **load** event will be emitted when the component needs to load more content. The argument passed is an object with two properties. -- `side` tells you at which side new content should be added, either at the `'start'` or `'end'`. The return value of the function is a string which describes if the new content was loaded successfully or not. +- `side` tells you on which side new content should be added, either at the `'start'` or `'end'`. The return value of the function is a string that describes if the new content was loaded successfully or not. - `done` is a callback function that should be called when the loading of new content is done. It takes a single parameter `status` that describes if the load was successful or not. See the table below for the possible values. |Status|Description| @@ -42,7 +42,7 @@ A **load** event will be emitted when the component needs to load more content. |`'ok'`|Content was added succesfully| |`'error'`|Something went wrong when adding content. This will display the `error` slot| |`'empty'`|There is no more content to fetch. This will display the `empty` slot| -|`'loading'`|Content is currently loading. This will display a message that content is loading. This status is only set internally by the component and should not be used with the **done** function| +|`'loading'`|Content is currently loading. This will display a message that the content is loading. This status is only set internally by the component and should not be used with the **done** function| @@ -67,29 +67,29 @@ The `v-infinite-scroll` works with any content in its default slot. ## Guide -The `v-infinite-scroll` component is a container that allows you to react to a user reaching the end of content area. It is useful when you need to display an unknown but large number of items, and you don't want to load them all at once. +The `v-infinite-scroll` component is a container that allows you to react to a user reaching the end of the content area. It is useful when you need to display an unknown but large number of items, and you don't want to load them all at once. ### Props -The `v-infinite-scroll` component has a number of props that can be used to customize its behaviour. +The `v-infinite-scroll` component has several props that can be used to customize its behavior. #### Mode -The default behaviour of the component is to try to load more content automatically when the scrollbar gets close to the end. However a manual mode is also supported, where the user needs to do some interaction to load the content. By default this a button, but it can be customized with a [slot](#load-more) +The default behavior of the component is to try to load more content automatically when the scrollbar gets close to the end. However, a manual mode is also supported, where the user needs to do some interaction to load the content. By default this is a button, but it can be customized with a [slot](#load-more) #### Direction -The `v-infinite-scroll` component can be used with either vertical and horizontal scrolling. +The `v-infinite-scroll` component can be used with either vertical or horizontal scrolling. #### Side -By default the `v-infinite-scroll` component assumes that new content will be appearing at the end of existing content. But it also supports content being added to the start, and content appearing both at the start and the end. +By default, the `v-infinite-scroll` component assumes that new content will appear at the end of existing content. But it also supports content being added to the start and appearing both at the beginning and the end. -When using the **start** side for content, the scrolllbar will start at the bottom of the content. +When using the **start** side for content, the scrollbar will start at the bottom of the content. @@ -105,17 +105,17 @@ The default load more button and loading spinner can be colored with the **color ### Slots -The `v-infinite-scroll` component exposes a number of slots that allow you to further customize its behaviour. +The `v-infinite-scroll` component exposes several slots that allow you to further customize its behaviour. ![Infinite scroll Slots](https://cdn.vuetifyjs.com/docs/images/components/v-infinite-scroll/v-infinite-scroll-slots.png) | Element / Area | Description | | - | - | | 1. Container | The default slot | -| 2. Load-more | The slot shown when mode is set to `manual` and status is not `loading` | -| 3. Loading | The slot shown when mode is set to `manual` and status is `loading` | -| 4. Empty | The slot shown when status is `empty` | -| 5. Error | The slot shown when status is `error` | +| 2. Load-more | The slot shown when the mode is set to `manual` and the status is not `loading` | +| 3. Loading | The slot is shown when the mode is set to `manual` and status is `loading` | +| 4. Empty | The slot shown when the status is `empty` | +| 5. Error | The slot is shown when the status is `error` | #### Loading @@ -143,10 +143,10 @@ The **error** slot is shown if the status `'error'` is returned from the `done` ### Examples -The following are a collection of examples that demonstrate more advanced and real world use of the `v-infinite-scroll` component. +The following is a collection of examples that demonstrate more advanced and real-world use of the `v-infinite-scroll` component. #### Virtualized infinite scroller -If the items in your infinite list are of a uniform size, you can quite easily virtualize the list to only render a small amount of items regardless of how far you scroll in either direction. +If the items in your infinite list are of a uniform size, you can quite easily virtualize the list to only render a small number of items regardless of how far you scroll in either direction. diff --git a/packages/docs/src/pages/en/components/snackbar-queue.md b/packages/docs/src/pages/en/components/snackbar-queue.md new file mode 100644 index 00000000000..5037b56c290 --- /dev/null +++ b/packages/docs/src/pages/en/components/snackbar-queue.md @@ -0,0 +1,58 @@ +--- +emphasized: true +meta: + nav: Snackbar Queue + title: Snackbar Queue component + description: test + keywords: test +related: + - /components/buttons/ + - /components/snackbars/ + - /components/defaults-providers/ +features: + github: /labs/VSnackbarQueue/ + label: 'C: VSnackbarQueue' + report: true + spec: https://m2.material.io/components/snackbars +--- + +# Snackbar Queue + +The `v-snackbar-queue` component is used to queue up multiple snackbar messages to be displayed to the user. Snackbars support positioning, removal delay, and callbacks. + + + +::: warning + +This feature requires [v3.6.0](/getting-started/release-notes/?version=v3.6.0) + +::: + +## Installation + +Labs components require a manual import and installation of the component. + +```js { resource="src/plugins/vuetify.js" } +import { VSnackbarQueue } from 'vuetify/labs/VSnackbarQueue' + +export default createVuetify({ + components: { + VSnackbarQueue, + }, +}) +``` + +## Usage + + + + + +## API + +| Component | Description | +| - | - | +| [v-snackbar-queue](/api/v-snackbar-queue/) | Primary Component | +| [v-snackbar](/api/v-snackbar/) | The actual Snackbar Component | + + diff --git a/packages/docs/src/pages/en/labs/introduction.md b/packages/docs/src/pages/en/labs/introduction.md index c2619742c16..532e8cac8eb 100644 --- a/packages/docs/src/pages/en/labs/introduction.md +++ b/packages/docs/src/pages/en/labs/introduction.md @@ -81,6 +81,7 @@ The following is a list of available and up-and-coming components for use with L | [v-empty-state](/components/empty-states/) | A component for displaying empty states | [v3.5.7](/getting-started/release-notes/?version=v3.5.7) | | [v-fab](/components/floating-action-buttons/) | A layout aware [v-btn](/components/buttons/) | [v3.5.7](/getting-started/release-notes/?version=v3.5.7) | | [v-number-input](/components/number-input/) | A component for numerical data | [v3.5.10](/getting-started/release-notes/?version=v3.5.10) | +| [v-snackbar-queue](/components/snackbar-queue/) | A queue for snackbars | [v3.6.0](/getting-started/release-notes/?version=v3.6.0) | | [v-sparkline](/components/sparklines/) | A basic data display component | [v3.5.5](/getting-started/release-notes/?version=v3.5.5) | | [v-speed-dial](/components/speed-dials/) | A component for display actions | [v3.5.8](/getting-started/release-notes/?version=v3.5.8) | | [v-time-picker](/components/time-pickers/) | A time-picker component | [v3.5.12](/getting-started/release-notes/?version=v3.5.12) | diff --git a/packages/docs/src/stores/app.ts b/packages/docs/src/stores/app.ts index 881f527b8b0..4b431132aae 100644 --- a/packages/docs/src/stores/app.ts +++ b/packages/docs/src/stores/app.ts @@ -7,12 +7,6 @@ export type Category = { color: string } -export type Notification = { - message: string - timeout?: number - color?: string -} - export type RootState = { apiSearch: string drawer: boolean | null @@ -21,7 +15,6 @@ export type RootState = { items: NavItem[] pages: string[] settings: boolean - notifications: Notification[] categories: Record } @@ -44,7 +37,6 @@ export const useAppStore = defineStore({ items: Array.from(data), pages: getPages(data as NavItem[]), settings: false, - notifications: [], categories: { api: { icon: 'mdi-flask-outline', @@ -92,11 +84,6 @@ export const useAppStore = defineStore({ }, }, } as RootState), - actions: { - snackbar (message: string, options: Partial = {}) { - this.notifications.push({ message, ...options }) - }, - }, }) function getPage (item: NavItem, parent = ''): string[] { diff --git a/packages/vuetify/src/components/VAutocomplete/VAutocomplete.tsx b/packages/vuetify/src/components/VAutocomplete/VAutocomplete.tsx index e676a30a39e..81d862e4185 100644 --- a/packages/vuetify/src/components/VAutocomplete/VAutocomplete.tsx +++ b/packages/vuetify/src/components/VAutocomplete/VAutocomplete.tsx @@ -250,21 +250,18 @@ export const VAutocomplete = genericComponent 0 + model.value.length > 0 && + !search.value ) return select(model.value[0], false) - if (selectionIndex.value < 0) { - if (e.key === 'Backspace' && !search.value) { - selectionIndex.value = length - 1 - } + if (~selectionIndex.value) { + const originalSelectionIndex = selectionIndex.value + select(model.value[selectionIndex.value], false) - return + selectionIndex.value = originalSelectionIndex >= length - 1 ? (length - 2) : originalSelectionIndex + } else if (e.key === 'Backspace' && !search.value) { + selectionIndex.value = length - 1 } - - const originalSelectionIndex = selectionIndex.value - select(model.value[selectionIndex.value], false) - - selectionIndex.value = originalSelectionIndex >= length - 1 ? (length - 2) : originalSelectionIndex } if (!props.multiple) return @@ -324,7 +321,7 @@ export const VAutocomplete = genericComponent()({ return ( 0 + model.value.length > 0 && + !search.value ) return select(model.value[0], false) - if (selectionIndex.value < 0) { - if (e.key === 'Backspace' && !search.value) { - selectionIndex.value = length - 1 - } - return - } + if (~selectionIndex.value) { + const originalSelectionIndex = selectionIndex.value + select(model.value[selectionIndex.value], false) - const originalSelectionIndex = selectionIndex.value - select(model.value[selectionIndex.value], false) - - selectionIndex.value = originalSelectionIndex >= length - 1 ? (length - 2) : originalSelectionIndex + selectionIndex.value = originalSelectionIndex >= length - 1 ? (length - 2) : originalSelectionIndex + } else if (e.key === 'Backspace' && !search.value) { + selectionIndex.value = length - 1 + } } if (!props.multiple) return @@ -405,7 +403,7 @@ export const VCombobox = genericComponent { diff --git a/packages/vuetify/src/components/VDatePicker/VDatePicker.tsx b/packages/vuetify/src/components/VDatePicker/VDatePicker.tsx index 9bd10159b80..51444e87762 100644 --- a/packages/vuetify/src/components/VDatePicker/VDatePicker.tsx +++ b/packages/vuetify/src/components/VDatePicker/VDatePicker.tsx @@ -232,6 +232,18 @@ export const VDatePicker = genericComponent { const before = adapter.date(wrapInArray(val)[0]) const after = adapter.date(wrapInArray(oldVal)[0]) + const newMonth = adapter.getMonth(before) + const newYear = adapter.getYear(before) + + if (newMonth !== month.value) { + month.value = newMonth + onUpdateMonth(month.value) + } + + if (newYear !== year.value) { + year.value = newYear + onUpdateYear(year.value) + } isReversing.value = adapter.isBefore(before, after) }) diff --git a/packages/vuetify/src/components/VDialog/VDialog.tsx b/packages/vuetify/src/components/VDialog/VDialog.tsx index a347c6d0bd9..4dcdea83b59 100644 --- a/packages/vuetify/src/components/VDialog/VDialog.tsx +++ b/packages/vuetify/src/components/VDialog/VDialog.tsx @@ -42,12 +42,11 @@ export const VDialog = genericComponent()({ props: makeVDialogProps(), emits: { - 'click:outside': (e: MouseEvent) => true, 'update:modelValue': (value: boolean) => true, afterLeave: () => true, }, - setup (props, { slots }) { + setup (props, { emit, slots }) { const isActive = useProxiedModel(props, 'modelValue') const { scopeId } = useScopeId() @@ -95,6 +94,10 @@ export const VDialog = genericComponent()({ } } + function onAfterLeave () { + emit('afterLeave') + } + watch(isActive, async val => { if (!val) { await nextTick() @@ -131,6 +134,7 @@ export const VDialog = genericComponent()({ contentProps={ contentProps } role="dialog" onAfterEnter={ onAfterEnter } + onAfterLeave={ onAfterLeave } { ...scopeId } > {{ diff --git a/packages/vuetify/src/components/VDialog/__test__/VDialog.spec.cy.tsx b/packages/vuetify/src/components/VDialog/__test__/VDialog.spec.cy.tsx new file mode 100644 index 00000000000..ec5d2296dec --- /dev/null +++ b/packages/vuetify/src/components/VDialog/__test__/VDialog.spec.cy.tsx @@ -0,0 +1,39 @@ +/// + +// Components +import { VDialog } from '..' + +// Utilities +import { ref } from 'vue' + +// Tests +describe('VDialog', () => { + it('should render correctly', () => { + const model = ref(false) + cy.mount(() => ( + +
Content
+
+ )).get('[data-test="dialog"]').should('not.exist') + .then(() => { model.value = true }) + .get('[data-test="dialog"]').should('be.visible') + .get('[data-test="content"]').should('be.visible') + .get('body').click() + .then(() => { + expect(model.value).to.be.false + }) + .get('[data-test="dialog"]').should('not.exist') + .get('[data-test="content"]').should('not.exist') + }) + + it('should emit afterLeave', () => { + const model = ref(true) + const onAfterLeave = cy.spy().as('afterLeave') + cy.mount(() => ( + +
Content
+
+ )).get('body').click() + .get('@afterLeave').should('have.been.calledOnce') + }) +}) diff --git a/packages/vuetify/src/components/VFileInput/VFileInput.tsx b/packages/vuetify/src/components/VFileInput/VFileInput.tsx index 87f88ef4e0f..7f2eb4786a0 100644 --- a/packages/vuetify/src/components/VFileInput/VFileInput.tsx +++ b/packages/vuetify/src/components/VFileInput/VFileInput.tsx @@ -68,7 +68,7 @@ export const makeVFileInputProps = propsFactory({ modelValue: { type: [Array, Object] as PropType, - default: () => ([]), + default: (props: any) => props.multiple ? [] : null, validator: (val: any) => { return wrapInArray(val).every(v => v != null && typeof v === 'object') }, diff --git a/packages/vuetify/src/components/VInput/VInput.tsx b/packages/vuetify/src/components/VInput/VInput.tsx index 078292b0554..4234aeb0aad 100644 --- a/packages/vuetify/src/components/VInput/VInput.tsx +++ b/packages/vuetify/src/components/VInput/VInput.tsx @@ -11,6 +11,7 @@ import { makeDensityProps, useDensity } from '@/composables/density' import { makeDimensionProps, useDimension } from '@/composables/dimensions' import { IconValue } from '@/composables/icons' import { useRtl } from '@/composables/locale' +import { makeThemeProps, provideTheme } from '@/composables/theme' import { makeValidationProps, useValidation } from '@/composables/validation' // Utilities @@ -68,6 +69,7 @@ export const makeVInputProps = propsFactory({ 'minWidth', 'width', ]), + ...makeThemeProps(), ...makeValidationProps(), }, 'VInput') @@ -99,6 +101,7 @@ export const VInput = genericComponent( setup (props, { attrs, slots, emit }) { const { densityClasses } = useDensity(props) const { dimensionStyles } = useDimension(props) + const { themeClasses } = provideTheme(props) const { rtlClasses } = useRtl() const { InputIcon } = useInputIcon(props) @@ -163,6 +166,7 @@ export const VInput = genericComponent( 'v-input--hide-spin-buttons': props.hideSpinButtons, }, densityClasses.value, + themeClasses.value, rtlClasses.value, validationClasses.value, props.class, diff --git a/packages/vuetify/src/components/VList/VList.tsx b/packages/vuetify/src/components/VList/VList.tsx index aa36192caa6..868ee43d2cd 100644 --- a/packages/vuetify/src/components/VList/VList.tsx +++ b/packages/vuetify/src/components/VList/VList.tsx @@ -205,7 +205,9 @@ export const VList = genericComponent()({ isActive.value = props.permanent || !mobile.value } - const { isDragging, dragProgress, dragStyles } = useTouch({ + const { isDragging, dragProgress } = useTouch({ + el: rootEl, isActive, isTemporary, width, @@ -242,7 +243,6 @@ export const VNavigationDrawer = genericComponent()({ style={[ backgroundColorStyles.value, layoutItemStyles.value, - dragStyles.value, ssrBootStyles.value, stickyStyles.value, props.style, diff --git a/packages/vuetify/src/components/VNavigationDrawer/touch.ts b/packages/vuetify/src/components/VNavigationDrawer/touch.ts index d1c5dd009fc..f7f2a4e66c7 100644 --- a/packages/vuetify/src/components/VNavigationDrawer/touch.ts +++ b/packages/vuetify/src/components/VNavigationDrawer/touch.ts @@ -1,13 +1,22 @@ // Composables +import { useToggleScope } from '@/composables/toggleScope' import { useVelocity } from '@/composables/touch' // Utilities -import { computed, onBeforeUnmount, onMounted, shallowRef } from 'vue' +import { computed, onBeforeUnmount, onMounted, onScopeDispose, shallowRef, watchEffect } from 'vue' // Types import type { Ref } from 'vue' -export function useTouch ({ isActive, isTemporary, width, touchless, position }: { +export function useTouch ({ + el, + isActive, + isTemporary, + width, + touchless, + position, +}: { + el: Ref isActive: Ref isTemporary: Ref width: Ref @@ -83,12 +92,12 @@ export function useTouch ({ isActive, isTemporary, width, touchless, position }: inElement || (isActive.value && isTemporary.value) ) { - maybeDragging = true start = [touchX, touchY] offset.value = getOffset(isHorizontal.value ? touchX : touchY, isActive.value) dragProgress.value = getProgress(isHorizontal.value ? touchX : touchY) + maybeDragging = offset.value > -20 && offset.value < 80 endTouch(e) addMovement(e) } @@ -174,6 +183,21 @@ export function useTouch ({ isActive, isTemporary, width, touchless, position }: } : undefined }) + useToggleScope(isDragging, () => { + const transform = el.value?.style.transform ?? null + const transition = el.value?.style.transition ?? null + + watchEffect(() => { + el.value?.style.setProperty('transform', dragStyles.value?.transform || 'none') + el.value?.style.setProperty('transition', dragStyles.value?.transition || null) + }) + + onScopeDispose(() => { + el.value?.style.setProperty('transform', transform) + el.value?.style.setProperty('transition', transition) + }) + }) + return { isDragging, dragProgress, diff --git a/packages/vuetify/src/components/VProgressLinear/VProgressLinear.tsx b/packages/vuetify/src/components/VProgressLinear/VProgressLinear.tsx index 6651dd1e19c..6503247a0b7 100644 --- a/packages/vuetify/src/components/VProgressLinear/VProgressLinear.tsx +++ b/packages/vuetify/src/components/VProgressLinear/VProgressLinear.tsx @@ -6,6 +6,7 @@ import { useBackgroundColor, useTextColor } from '@/composables/color' import { makeComponentProps } from '@/composables/component' import { useIntersectionObserver } from '@/composables/intersectionObserver' import { useRtl } from '@/composables/locale' +import { makeLocationProps, useLocation } from '@/composables/location' import { useProxiedModel } from '@/composables/proxiedModel' import { makeRoundedProps, useRounded } from '@/composables/rounded' import { makeTagProps } from '@/composables/tag' @@ -55,6 +56,7 @@ export const makeVProgressLinearProps = propsFactory({ roundedBar: Boolean, ...makeComponentProps(), + ...makeLocationProps({ location: 'top' } as const), ...makeRoundedProps(), ...makeTagProps(), ...makeThemeProps(), @@ -73,6 +75,7 @@ export const VProgressLinear = genericComponent()({ const progress = useProxiedModel(props, 'modelValue') const { isRtl, rtlClasses } = useRtl() const { themeClasses } = provideTheme(props) + const { locationStyles } = useLocation(props) const { textColorClasses, textColorStyles } = useTextColor(props, 'color') const { backgroundColorClasses, @@ -125,8 +128,11 @@ export const VProgressLinear = genericComponent()({ ]} style={[ { + bottom: props.location === 'bottom' ? 0 : undefined, + top: props.location === 'top' ? 0 : undefined, height: props.active ? convertToUnit(height.value) : 0, '--v-progress-linear-height': convertToUnit(height.value), + ...(props.absolute ? locationStyles.value : {}), }, props.style, ]} diff --git a/packages/vuetify/src/components/VSelect/VSelect.tsx b/packages/vuetify/src/components/VSelect/VSelect.tsx index 5994891febf..449ad3fed2f 100644 --- a/packages/vuetify/src/components/VSelect/VSelect.tsx +++ b/packages/vuetify/src/components/VSelect/VSelect.tsx @@ -250,6 +250,10 @@ export const VSelect = genericComponent item.title.toLowerCase().startsWith(keyboardLookupPrefix)) if (item !== undefined) { model.value = [item] + const index = displayItems.value.indexOf(item) + IN_BROWSER && window.requestAnimationFrame(() => { + index >= 0 && vVirtualScrollRef.value?.scrollToIndex(index) + }) } } @@ -302,7 +306,7 @@ export const VSelect = genericComponent { + watch(menu, () => { if (!props.hideSelected && menu.value && model.value.length) { const index = displayItems.value.findIndex( item => model.value.some(s => props.valueComparator(s.value, item.value)) diff --git a/packages/vuetify/src/components/VSnackbar/VSnackbar.sass b/packages/vuetify/src/components/VSnackbar/VSnackbar.sass index 1c16e8a82c6..0adfb2b48d7 100644 --- a/packages/vuetify/src/components/VSnackbar/VSnackbar.sass +++ b/packages/vuetify/src/components/VSnackbar/VSnackbar.sass @@ -8,6 +8,7 @@ z-index: $snackbar-z-index margin: $snackbar-wrapper-margin margin-inline-end: calc(#{$snackbar-wrapper-margin} + var(--v-scrollbar-offset)) + padding: var(--v-layout-top) var(--v-layout-right) var(--v-layout-bottom) var(--v-layout-left) &:not(.v-snackbar--centered):not(.v-snackbar--top) align-items: flex-end @@ -68,6 +69,24 @@ align-self: flex-end margin-bottom: $snackbar-vertical-action-margin-bottom + &--center + align-items: center + justify-content: center + + &--top + align-items: flex-start + + &--bottom + align-items: flex-end + + &--left, + &--start + justify-content: flex-start + + &--right, + &--end + justify-content: flex-end + .v-snackbar-transition &-enter-active, &-leave-active diff --git a/packages/vuetify/src/components/VSnackbar/VSnackbar.tsx b/packages/vuetify/src/components/VSnackbar/VSnackbar.tsx index f1d67cc25b6..3fc9f344d2a 100644 --- a/packages/vuetify/src/components/VSnackbar/VSnackbar.tsx +++ b/packages/vuetify/src/components/VSnackbar/VSnackbar.tsx @@ -8,8 +8,9 @@ import { makeVOverlayProps } from '@/components/VOverlay/VOverlay' import { VProgressLinear } from '@/components/VProgressLinear' // Composables +import { useLayout } from '@/composables' import { forwardRefs } from '@/composables/forwardRefs' -import { makeLocationProps, useLocation } from '@/composables/location' +import { makeLocationProps } from '@/composables/location' import { makePositionProps, usePosition } from '@/composables/position' import { useProxiedModel } from '@/composables/proxiedModel' import { makeRoundedProps, useRounded } from '@/composables/rounded' @@ -18,7 +19,7 @@ import { makeThemeProps, provideTheme } from '@/composables/theme' import { genOverlays, makeVariantProps, useVariant } from '@/composables/variant' // Utilities -import { mergeProps, nextTick, onMounted, onScopeDispose, ref, shallowRef, watch } from 'vue' +import { computed, mergeProps, nextTick, onMounted, onScopeDispose, ref, shallowRef, watch } from 'vue' import { genericComponent, omit, propsFactory, refElement, useRender } from '@/util' // Types @@ -98,7 +99,7 @@ export const VSnackbar = genericComponent()({ setup (props, { slots }) { const isActive = useProxiedModel(props, 'modelValue') - const { locationStyles } = useLocation(props) + const { mainStyles } = useLayout() const { positionClasses } = usePosition(props) const { scopeId } = useScopeId() const { themeClasses } = provideTheme(props) @@ -109,6 +110,7 @@ export const VSnackbar = genericComponent()({ const overlay = ref() const timerRef = ref() const isHovering = shallowRef(false) + const startY = shallowRef(0) watch(isActive, startTimeout) watch(() => props.timeout, startTimeout) @@ -149,6 +151,24 @@ export const VSnackbar = genericComponent()({ startTimeout() } + function onTouchstart (event: TouchEvent) { + startY.value = event.touches[0].clientY + } + + function onTouchend (event: TouchEvent) { + if (Math.abs(startY.value - event.changedTouches[0].clientY) > 50) { + isActive.value = false + } + } + + const locationClasses = computed(() => { + return props.location.split(' ').reduce((acc, loc) => { + acc[`v-snackbar--${loc}`] = true + + return acc + }, {} as Record) + }) + useRender(() => { const overlayProps = VOverlay.filterProps(props) const hasContent = !!(slots.default || slots.text || props.text) @@ -164,10 +184,14 @@ export const VSnackbar = genericComponent()({ 'v-snackbar--timer': !!props.timer, 'v-snackbar--vertical': props.vertical, }, + locationClasses.value, positionClasses.value, props.class, ]} - style={ props.style } + style={[ + mainStyles.value, + props.style, + ]} { ...overlayProps } v-model={ isActive.value } contentProps={ mergeProps({ @@ -179,7 +203,6 @@ export const VSnackbar = genericComponent()({ variantClasses.value, ], style: [ - locationStyles.value, colorStyles.value, ], onPointerenter, @@ -190,6 +213,8 @@ export const VSnackbar = genericComponent()({ scrim={ false } scrollStrategy="none" _disableGlobalStack + onTouchstart={ onTouchstart } + onTouchend={ onTouchend } { ...scopeId } v-slots={{ activator: slots.activator }} > diff --git a/packages/vuetify/src/components/VSwitch/VSwitch.sass b/packages/vuetify/src/components/VSwitch/VSwitch.sass index 3d3a8d5d874..45b3b22055b 100644 --- a/packages/vuetify/src/components/VSwitch/VSwitch.sass +++ b/packages/vuetify/src/components/VSwitch/VSwitch.sass @@ -101,7 +101,10 @@ .v-selection-control__input border-radius: 50% transition: $switch-control-input-transition - transform: translateX(-$switch-thumb-transform) + +tools.ltr() + transform: translateX(-$switch-thumb-transform) + +tools.rtl() + transform: translateX($switch-thumb-transform) position: absolute .v-icon @@ -109,7 +112,10 @@ .v-selection-control--dirty .v-selection-control__input - transform: translateX($switch-thumb-transform) + +tools.ltr() + transform: translateX($switch-thumb-transform) + +tools.rtl() + transform: translateX(-$switch-thumb-transform) &.v-switch--indeterminate .v-selection-control__input diff --git a/packages/vuetify/src/composables/__tests__/group.spec.ts b/packages/vuetify/src/composables/__tests__/group.spec.ts index 625e89dccd4..076e29b6169 100644 --- a/packages/vuetify/src/composables/__tests__/group.spec.ts +++ b/packages/vuetify/src/composables/__tests__/group.spec.ts @@ -3,7 +3,7 @@ import { makeGroupProps, useGroup, useGroupItem } from '../group' // Utilities import { describe, expect, it } from '@jest/globals' import { mount } from '@vue/test-utils' -import { defineComponent, h, nextTick, reactive } from 'vue' +import { defineComponent, h, nextTick, reactive, useSlots } from 'vue' describe('group', () => { describe('with complex values', () => { @@ -270,7 +270,8 @@ describe('group', () => { setup (props) { // @ts-expect-error missing emit useGroup(props, Symbol.for('test')) - return () => h('div', [ + const slot = useSlots() + return () => h('div', slot.default?.() ?? [ h(GroupItemComponent, { disabled: !!props.disabledItems?.[0] }), h(GroupItemComponent, { disabled: !!props.disabledItems?.[1] }), ]) @@ -370,5 +371,39 @@ describe('group', () => { expect(wrapper.emitted('update:modelValue')).toStrictEqual([[[0]]]) }) + + it('should update the items that use index as the value when delete', async () => { + const values = reactive(['one', 'two', 'three']) + const wrapper = mount(GroupComponent, { + props: { + multiple: false, + mandatory: false, + }, + slots: { + default () { + return values.map(value => h(GroupItemComponent, { key: value })) + }, + }, + }) + values.splice(1, 1) + values.push('four') + await nextTick() + let items = wrapper.findAllComponents(GroupItemComponent) + + await items[1].trigger('click') + await items[2].trigger('click') + + expect(wrapper.emitted()['update:modelValue']).toEqual([[1], [2]]) + + values.splice(1, 0, 'eight') + values.push('nine') + await nextTick() + items = wrapper.findAllComponents(GroupItemComponent) + + await items[3].trigger('click') + await items[4].trigger('click') + + expect(wrapper.emitted()['update:modelValue']).toEqual([[1], [2], [3], [4]]) + }) }) }) diff --git a/packages/vuetify/src/composables/group.ts b/packages/vuetify/src/composables/group.ts index 318f127f298..42d7e6b14a5 100644 --- a/packages/vuetify/src/composables/group.ts +++ b/packages/vuetify/src/composables/group.ts @@ -2,7 +2,7 @@ import { useProxiedModel } from './proxiedModel' // Utilities -import { computed, inject, onBeforeUnmount, onMounted, provide, reactive, toRef, unref, watch } from 'vue' +import { computed, inject, onBeforeUnmount, onMounted, onUpdated, provide, reactive, toRef, unref, watch } from 'vue' import { consoleWarn, deepEqual, findChildrenWithProvide, getCurrentInstance, getUid, propsFactory, wrapInArray } from '@/util' // Types @@ -13,6 +13,7 @@ export interface GroupItem { id: number value: Ref disabled: Ref + useIndexAsValue?: boolean } export interface GroupProps { @@ -181,6 +182,7 @@ export function useGroup ( if (unref(unwrapped.value) == null) { unwrapped.value = index + unwrapped.useIndexAsValue = true } if (index > -1) { @@ -219,6 +221,15 @@ export function useGroup ( isUnmounted = true }) + onUpdated(() => { + // #19655 update the items that use the index as the value. + for (let i = 0; i < items.length; i++) { + if (items[i].useIndexAsValue) { + items[i].value = i + } + } + }) + function select (id: number, value?: boolean) { const item = items.find(item => item.id === id) if (value && item?.disabled) return diff --git a/packages/vuetify/src/labs/VFab/VFab.sass b/packages/vuetify/src/labs/VFab/VFab.sass index 6ab35247c01..e6bf9eea331 100644 --- a/packages/vuetify/src/labs/VFab/VFab.sass +++ b/packages/vuetify/src/labs/VFab/VFab.sass @@ -53,6 +53,7 @@ .v-fab__container align-self: center display: inline-flex + position: absolute vertical-align: middle .v-fab--app & diff --git a/packages/vuetify/src/labs/VFab/VFab.tsx b/packages/vuetify/src/labs/VFab/VFab.tsx index dd2f84ae57a..15a7dd8b87e 100644 --- a/packages/vuetify/src/labs/VFab/VFab.tsx +++ b/packages/vuetify/src/labs/VFab/VFab.tsx @@ -25,6 +25,7 @@ export const makeVFabProps = propsFactory({ app: Boolean, appear: Boolean, extended: Boolean, + layout: Boolean, location: { type: String as PropType, default: 'bottom end', @@ -78,8 +79,8 @@ export const VFab = genericComponent()({ id: props.name, order: computed(() => parseInt(props.order, 10)), position, - layoutSize: height, - elementSize: computed(() => height.value + 32), + layoutSize: computed(() => props.layout ? height.value + 24 : 0), + elementSize: computed(() => height.value + 24), active: computed(() => props.app && model.value), absolute: toRef(props, 'absolute'), }) diff --git a/packages/vuetify/src/labs/VNumberInput/VNumberInput.tsx b/packages/vuetify/src/labs/VNumberInput/VNumberInput.tsx index 7ce3ea4e428..ea9b8cced49 100644 --- a/packages/vuetify/src/labs/VNumberInput/VNumberInput.tsx +++ b/packages/vuetify/src/labs/VNumberInput/VNumberInput.tsx @@ -13,8 +13,8 @@ import { makeFocusProps, useFocus } from '@/composables/focus' import { useProxiedModel } from '@/composables/proxiedModel' // Utilities -import { computed, ref } from 'vue' -import { filterInputAttrs, genericComponent, only, propsFactory, useRender } from '@/util' +import { computed, ref, watchEffect } from 'vue' +import { clamp, filterInputAttrs, genericComponent, getDecimals, only, propsFactory, useRender } from '@/util' // Types import type { PropType } from 'vue' @@ -39,9 +39,18 @@ const makeVNumberInputProps = propsFactory({ }, inset: Boolean, hideInput: Boolean, - min: Number, - max: Number, - step: Number, + min: { + type: Number, + default: -Infinity, + }, + max: { + type: Number, + default: Infinity, + }, + step: { + type: Number, + default: 1, + }, ...only(makeVInputProps(), [ 'density', @@ -79,8 +88,8 @@ export const VNumberInput = genericComponent()({ ...makeVNumberInputProps(), modelValue: { - type: [Number, String], - default: 0, + type: Number, + default: undefined, }, }, @@ -93,6 +102,24 @@ export const VNumberInput = genericComponent()({ const { isFocused, focus, blur } = useFocus(props) const inputRef = ref() + const stepDecimals = computed(() => getDecimals(props.step)) + const modelDecimals = computed(() => model.value != null ? getDecimals(model.value) : 0) + + const canIncrease = computed(() => { + if (model.value == null) return true + return model.value + props.step <= props.max + }) + const canDecrease = computed(() => { + if (model.value == null) return true + return model.value - props.step >= props.min + }) + + watchEffect(() => { + if (model.value != null && (model.value < props.min || model.value > props.max)) { + model.value = clamp(model.value, props.min, props.max) + } + }) + function onFocus () { if (!isFocused.value) focus() } @@ -101,14 +128,22 @@ export const VNumberInput = genericComponent()({ return props.hideInput ? 'stacked' : props.controlVariant }) + const incrementSlotProps = computed(() => ({ click: onClickUp })) + + const decrementSlotProps = computed(() => ({ click: onClickDown })) + function toggleUpDown (increment = true) { + if (model.value == null) { + model.value = 0 + return + } + + const decimals = Math.max(modelDecimals.value, stepDecimals.value) if (increment) { - inputRef.value?.stepUp() + if (canIncrease.value) model.value = +(((model.value + props.step).toFixed(decimals))) } else { - inputRef.value?.stepDown() + if (canDecrease.value) model.value = +(((model.value - props.step).toFixed(decimals))) } - - if (inputRef.value) model.value = parseInt(inputRef.value.value, 10) } function onClickUp () { @@ -119,9 +154,33 @@ export const VNumberInput = genericComponent()({ toggleUpDown(false) } - const incrementSlotProps = computed(() => ({ click: onClickUp })) + function onKeydown (e: KeyboardEvent) { + if ( + ['Enter', 'ArrowLeft', 'ArrowRight', 'Backspace'].includes(e.key) || + e.ctrlKey + ) return - const decrementSlotProps = computed(() => ({ click: onClickDown })) + if (['ArrowDown'].includes(e.key)) { + e.preventDefault() + toggleUpDown(false) + return + } + if (['ArrowUp'].includes(e.key)) { + e.preventDefault() + toggleUpDown() + return + } + + // Only numbers, +, - & . are allowed + if (!/^[0-9\-+.]+$/.test(e.key)) { + e.preventDefault() + } + } + + function onInput (e: Event) { + const el = e.target as HTMLInputElement + model.value = el.value ? +(el.value) : undefined + } useRender(() => { const fieldProps = filterFieldProps(props) @@ -135,9 +194,11 @@ export const VNumberInput = genericComponent()({ { !slots.decrement ? ( ()({ key="decrement-defaults" defaults={{ VBtn: { + disabled: !canDecrease.value, flat: true, height: defaultHeight, size: 'small', @@ -166,9 +228,11 @@ export const VNumberInput = genericComponent()({ { !slots.increment ? ( ()({ key="increment-defaults" defaults={{ VBtn: { + disabled: !canIncrease.value, flat: true, height: defaultHeight, size: 'small', @@ -231,12 +296,11 @@ export const VNumberInput = genericComponent()({ }) => ( + +// Components +import { VNumberInput } from '../VNumberInput' + +// Utilities +import { ref } from 'vue' + +describe('VNumberInput', () => { + describe('native number input quirks', () => { + it('should not bypass min', () => { + const numberInputValue = ref(1) + cy.mount(() => + + ) + .get('.v-number-input input').should('have.value', '5') + .should(() => expect(numberInputValue.value).to.equal(5)) + }) + + it('should not bypass max', () => { + const numberInputValue = ref(20) + cy.mount(() => + + ) + .get('.v-number-input input').should('have.value', '15') + .should(() => expect(numberInputValue.value).to.equal(15)) + }) + + it('should support decimal step', () => { + const numberInputValue = ref(0) + cy.mount(() => + ( + + ) + ) + .get('button[name="increment-btn"]') + .click() + .get('.v-number-input input').should('have.value', '0.03') + .then(() => expect(numberInputValue.value).to.equal(0.03)) + .get('button[name="increment-btn"]') + .click() + .get('.v-number-input input').should('have.value', '0.06') + .then(() => expect(numberInputValue.value).to.equal(0.06)) + .get('button[name="decrement-btn"]') + .click() + .get('.v-number-input input').should('have.value', '0.03') + .then(() => expect(numberInputValue.value).to.equal(0.03)) + .get('button[name="decrement-btn"]') + .click() + .get('.v-number-input input').should('have.value', '0') + .then(() => expect(numberInputValue.value).to.equal(0)) + }) + }) +}) diff --git a/packages/vuetify/src/labs/VSnackbarQueue/VSnackbarQueue.tsx b/packages/vuetify/src/labs/VSnackbarQueue/VSnackbarQueue.tsx new file mode 100644 index 00000000000..f6c3c39d4be --- /dev/null +++ b/packages/vuetify/src/labs/VSnackbarQueue/VSnackbarQueue.tsx @@ -0,0 +1,165 @@ +// Components +import { VBtn } from '@/components/VBtn' +import { VDefaultsProvider } from '@/components/VDefaultsProvider' +import { makeVSnackbarProps, VSnackbar } from '@/components/VSnackbar/VSnackbar' + +// Composables +import { useLocale } from '@/composables/locale' + +// Utilities +import { computed, nextTick, shallowRef, watch } from 'vue' +import { genericComponent, omit, propsFactory, useRender } from '@/util' + +// Types +import type { PropType } from 'vue' +import type { GenericProps } from '@/util' + +export type VSnackbarQueueSlots = { + default: { item: T } + text: { item: T } + actions: { + item: T + props: { + onClick: () => void + } + } +} + +export type SnackbarMessage = Omit< + VSnackbar['$props'], + | '$children' + | 'modelValue' + | 'onUpdate:modelValue' + | 'activator' + | 'activatorProps' + | 'closeDelay' + | 'openDelay' + | 'openOnClick' + | 'openOnFocus' + | 'openOnHover' +> + +export const makeVSnackbarQueueProps = propsFactory({ + // TODO: Port this to Snackbar on dev + closable: [Boolean, String], + closeText: { + type: String, + default: '$vuetify.dismiss', + }, + modelValue: { + type: Array as PropType, + default: () => [], + }, + + ...omit(makeVSnackbarProps(), ['modelValue']), +}, 'VSnackbarQueue') + +export const VSnackbarQueue = genericComponent ( + props: { + modelValue?: T + 'onUpdate:modelValue'?: (val: T) => void + }, + slots: VSnackbarQueueSlots, +) => GenericProps>()({ + name: 'VSnackbarQueue', + + props: makeVSnackbarQueueProps(), + + emits: { + 'update:modelValue': (val: (string | SnackbarMessage)[]) => true, + }, + + setup (props, { emit, slots }) { + const { t } = useLocale() + + const isActive = shallowRef(false) + const isVisible = shallowRef(false) + const current = shallowRef() + + watch(() => props.modelValue.length, (val, oldVal) => { + if (!isVisible.value && val > oldVal) { + showNext() + } + }) + watch(isActive, val => { + if (val) isVisible.value = true + }) + + function onAfterLeave () { + if (props.modelValue.length) { + showNext() + } else { + current.value = undefined + isVisible.value = false + } + } + function showNext () { + const [next, ...rest] = props.modelValue + emit('update:modelValue', rest) + current.value = typeof next === 'string' ? { text: next } : next + nextTick(() => { + isActive.value = true + }) + } + function onClickClose () { + isActive.value = false + } + + const btnProps = computed(() => ({ + color: typeof props.closable === 'string' ? props.closable : undefined, + text: t(props.closeText), + })) + + useRender(() => { + const hasActions = !!(props.closable || slots.actions) + const { modelValue: _, ...snackbarProps } = VSnackbar.filterProps(props as any) + + return ( + <> + { isVisible.value && !!current.value && ( + slots.default + ? ( + + { slots.default({ item: current.value }) } + + ) : ( + + {{ + text: slots.text ? () => slots.text?.({ item: current.value! }) : undefined, + actions: hasActions ? () => ( + <> + { !slots.actions ? ( + + ) : ( + + { slots.actions({ + item: current.value!, + props: { onClick: onClickClose }, + })} + + )} + + ) : undefined, + }} + + ) + )} + + ) + }) + }, +}) + +export type VSnackbarQueue = InstanceType diff --git a/packages/vuetify/src/labs/VSnackbarQueue/index.ts b/packages/vuetify/src/labs/VSnackbarQueue/index.ts new file mode 100644 index 00000000000..8fe3df988b5 --- /dev/null +++ b/packages/vuetify/src/labs/VSnackbarQueue/index.ts @@ -0,0 +1 @@ +export { VSnackbarQueue } from './VSnackbarQueue' diff --git a/packages/vuetify/src/labs/VTimePicker/VTimePickerClock.tsx b/packages/vuetify/src/labs/VTimePicker/VTimePickerClock.tsx index 57f29b7a8d8..5b05d53e6c3 100644 --- a/packages/vuetify/src/labs/VTimePicker/VTimePickerClock.tsx +++ b/packages/vuetify/src/labs/VTimePicker/VTimePickerClock.tsx @@ -21,9 +21,7 @@ export const makeVTimePickerClockProps = propsFactory({ ampm: Boolean, color: String, disabled: Boolean, - displayedValue: { - default: null, - }, + displayedValue: null, double: Boolean, format: { type: Function, diff --git a/packages/vuetify/src/labs/components.ts b/packages/vuetify/src/labs/components.ts index 98850084f8e..037336bc275 100644 --- a/packages/vuetify/src/labs/components.ts +++ b/packages/vuetify/src/labs/components.ts @@ -4,6 +4,7 @@ export * from './VEmptyState' export * from './VFab' export * from './VNumberInput' export * from './VPicker' +export * from './VSnackbarQueue' export * from './VSparkline' export * from './VSpeedDial' export * from './VTimePicker' diff --git a/packages/vuetify/src/locale/af.ts b/packages/vuetify/src/locale/af.ts index 16b8461da6e..34fc7bd4c76 100644 --- a/packages/vuetify/src/locale/af.ts +++ b/packages/vuetify/src/locale/af.ts @@ -2,6 +2,7 @@ export default { badge: 'badge', open: 'Open', close: 'Close', + dismiss: 'Dismiss', confirmEdit: { ok: 'OK', cancel: 'Cancel', diff --git a/packages/vuetify/src/locale/ar.ts b/packages/vuetify/src/locale/ar.ts index b265d61335a..b5ec2b2ea6f 100644 --- a/packages/vuetify/src/locale/ar.ts +++ b/packages/vuetify/src/locale/ar.ts @@ -2,6 +2,7 @@ export default { badge: 'شارة', open: 'Open', close: 'إغلاق', + dismiss: 'Dismiss', confirmEdit: { ok: 'OK', cancel: 'Cancel', diff --git a/packages/vuetify/src/locale/az.ts b/packages/vuetify/src/locale/az.ts index 1bfeb3ccd86..b452d1ccd95 100644 --- a/packages/vuetify/src/locale/az.ts +++ b/packages/vuetify/src/locale/az.ts @@ -2,6 +2,7 @@ export default { badge: 'nişan', open: 'Open', close: 'Bağla', + dismiss: 'Dismiss', confirmEdit: { ok: 'OK', cancel: 'Cancel', diff --git a/packages/vuetify/src/locale/bg.ts b/packages/vuetify/src/locale/bg.ts index 97556f97b8a..f795df46016 100644 --- a/packages/vuetify/src/locale/bg.ts +++ b/packages/vuetify/src/locale/bg.ts @@ -2,6 +2,7 @@ export default { badge: 'Значка', open: 'Отвори', close: 'Затвори', + dismiss: 'Dismiss', confirmEdit: { ok: 'OK', cancel: 'Отмяна', diff --git a/packages/vuetify/src/locale/ca.ts b/packages/vuetify/src/locale/ca.ts index 95e0c4cd1f5..09b06b0b9e9 100644 --- a/packages/vuetify/src/locale/ca.ts +++ b/packages/vuetify/src/locale/ca.ts @@ -2,6 +2,7 @@ export default { badge: 'Insígnia', open: 'Open', close: 'Tancar', + dismiss: 'Dismiss', confirmEdit: { ok: 'OK', cancel: 'Cancel', diff --git a/packages/vuetify/src/locale/ckb.ts b/packages/vuetify/src/locale/ckb.ts index 7a85013a3a0..64ffde6ea1a 100644 --- a/packages/vuetify/src/locale/ckb.ts +++ b/packages/vuetify/src/locale/ckb.ts @@ -2,6 +2,7 @@ export default { badge: 'باج', open: 'Open', close: 'داخستن', + dismiss: 'Dismiss', confirmEdit: { ok: 'OK', cancel: 'Cancel', diff --git a/packages/vuetify/src/locale/cs.ts b/packages/vuetify/src/locale/cs.ts index 4ebe6d78d77..85a8b6b613a 100644 --- a/packages/vuetify/src/locale/cs.ts +++ b/packages/vuetify/src/locale/cs.ts @@ -2,6 +2,7 @@ export default { badge: 'Odznak', open: 'Otevřiť', close: 'Zavřít', + dismiss: 'Dismiss', confirmEdit: { ok: 'OK', cancel: 'Zrušit', diff --git a/packages/vuetify/src/locale/da.ts b/packages/vuetify/src/locale/da.ts index c80f32a89d4..8a80af26500 100644 --- a/packages/vuetify/src/locale/da.ts +++ b/packages/vuetify/src/locale/da.ts @@ -2,6 +2,7 @@ export default { badge: 'Emblem', open: 'Open', close: 'Luk', + dismiss: 'Dismiss', confirmEdit: { ok: 'OK', cancel: 'Cancel', diff --git a/packages/vuetify/src/locale/de.ts b/packages/vuetify/src/locale/de.ts index aa8d614e8aa..7d1dacd2701 100644 --- a/packages/vuetify/src/locale/de.ts +++ b/packages/vuetify/src/locale/de.ts @@ -2,6 +2,7 @@ export default { badge: 'Abzeichen', open: 'Öffnen', close: 'Schließen', + dismiss: 'Dismiss', confirmEdit: { ok: 'OK', cancel: 'Abbrechen', diff --git a/packages/vuetify/src/locale/el.ts b/packages/vuetify/src/locale/el.ts index 9a2c757b2fa..be1701683f9 100755 --- a/packages/vuetify/src/locale/el.ts +++ b/packages/vuetify/src/locale/el.ts @@ -2,6 +2,7 @@ export default { badge: 'Σήμα', open: 'Open', close: 'Close', + dismiss: 'Dismiss', confirmEdit: { ok: 'OK', cancel: 'Cancel', diff --git a/packages/vuetify/src/locale/en.ts b/packages/vuetify/src/locale/en.ts index c03b6d6395b..cf94f5e9786 100644 --- a/packages/vuetify/src/locale/en.ts +++ b/packages/vuetify/src/locale/en.ts @@ -2,6 +2,7 @@ export default { badge: 'Badge', open: 'Open', close: 'Close', + dismiss: 'Dismiss', confirmEdit: { ok: 'OK', cancel: 'Cancel', diff --git a/packages/vuetify/src/locale/es.ts b/packages/vuetify/src/locale/es.ts index 81dd808d8db..849ddececaa 100644 --- a/packages/vuetify/src/locale/es.ts +++ b/packages/vuetify/src/locale/es.ts @@ -2,6 +2,7 @@ export default { badge: 'Placa', open: 'Open', close: 'Cerrar', + dismiss: 'Dismiss', confirmEdit: { ok: 'OK', cancel: 'Cancel', diff --git a/packages/vuetify/src/locale/et.ts b/packages/vuetify/src/locale/et.ts index f6ac5a40204..b420079173d 100644 --- a/packages/vuetify/src/locale/et.ts +++ b/packages/vuetify/src/locale/et.ts @@ -2,6 +2,7 @@ export default { badge: 'Märk', open: 'Open', close: 'Sulge', + dismiss: 'Dismiss', confirmEdit: { ok: 'OK', cancel: 'Cancel', diff --git a/packages/vuetify/src/locale/fa.ts b/packages/vuetify/src/locale/fa.ts index 87841876b98..e1efd668b60 100644 --- a/packages/vuetify/src/locale/fa.ts +++ b/packages/vuetify/src/locale/fa.ts @@ -2,6 +2,7 @@ export default { badge: 'نشان', open: 'Open', close: 'بستن', + dismiss: 'Dismiss', confirmEdit: { ok: 'OK', cancel: 'لغو', diff --git a/packages/vuetify/src/locale/fi.ts b/packages/vuetify/src/locale/fi.ts index 53b916003e9..d03c0703928 100644 --- a/packages/vuetify/src/locale/fi.ts +++ b/packages/vuetify/src/locale/fi.ts @@ -2,6 +2,7 @@ export default { badge: 'Infopiste', open: 'Open', close: 'Sulje', + dismiss: 'Dismiss', confirmEdit: { ok: 'OK', cancel: 'Cancel', diff --git a/packages/vuetify/src/locale/fr.ts b/packages/vuetify/src/locale/fr.ts index dfd33b512c6..717e2902829 100644 --- a/packages/vuetify/src/locale/fr.ts +++ b/packages/vuetify/src/locale/fr.ts @@ -2,6 +2,7 @@ export default { badge: 'Badge', open: 'Ouvrir', close: 'Fermer', + dismiss: 'Dismiss', confirmEdit: { ok: 'OK', cancel: 'Annuler', diff --git a/packages/vuetify/src/locale/he.ts b/packages/vuetify/src/locale/he.ts index 253b5c1708c..ebc04e428e6 100644 --- a/packages/vuetify/src/locale/he.ts +++ b/packages/vuetify/src/locale/he.ts @@ -2,6 +2,7 @@ export default { badge: 'תג', open: 'Open', close: 'סגור', + dismiss: 'Dismiss', confirmEdit: { ok: 'OK', cancel: 'Cancel', diff --git a/packages/vuetify/src/locale/hr.ts b/packages/vuetify/src/locale/hr.ts index 7e82c3be8d2..d24be21324b 100644 --- a/packages/vuetify/src/locale/hr.ts +++ b/packages/vuetify/src/locale/hr.ts @@ -2,6 +2,7 @@ export default { badge: 'Bedž', open: 'Open', close: 'Zatvori', + dismiss: 'Dismiss', confirmEdit: { ok: 'OK', cancel: 'Cancel', diff --git a/packages/vuetify/src/locale/hu.ts b/packages/vuetify/src/locale/hu.ts index ae4efb4bded..707e2faa4e2 100644 --- a/packages/vuetify/src/locale/hu.ts +++ b/packages/vuetify/src/locale/hu.ts @@ -2,6 +2,7 @@ export default { badge: 'Jelvény', open: 'Open', close: 'Bezárás', + dismiss: 'Dismiss', confirmEdit: { ok: 'OK', cancel: 'Cancel', diff --git a/packages/vuetify/src/locale/id.ts b/packages/vuetify/src/locale/id.ts index 734fa04c40f..ad689c5f478 100644 --- a/packages/vuetify/src/locale/id.ts +++ b/packages/vuetify/src/locale/id.ts @@ -2,6 +2,7 @@ export default { badge: 'Lencana', open: 'Open', close: 'Tutup', + dismiss: 'Dismiss', confirmEdit: { ok: 'OK', cancel: 'Cancel', diff --git a/packages/vuetify/src/locale/it.ts b/packages/vuetify/src/locale/it.ts index 0614b098ce1..cc2d11e9f53 100644 --- a/packages/vuetify/src/locale/it.ts +++ b/packages/vuetify/src/locale/it.ts @@ -2,6 +2,7 @@ export default { badge: 'Distintivo', open: 'Apri', close: 'Chiudi', + dismiss: 'Dismiss', confirmEdit: { ok: 'OK', cancel: 'Annulla', diff --git a/packages/vuetify/src/locale/ja.ts b/packages/vuetify/src/locale/ja.ts index 00211bf9280..1156492f552 100644 --- a/packages/vuetify/src/locale/ja.ts +++ b/packages/vuetify/src/locale/ja.ts @@ -2,6 +2,7 @@ export default { badge: 'バッジ', open: 'Open', close: '閉じる', + dismiss: 'Dismiss', confirmEdit: { ok: 'OK', cancel: 'Cancel', diff --git a/packages/vuetify/src/locale/km.ts b/packages/vuetify/src/locale/km.ts index f3f4b238701..10c1cd735ac 100644 --- a/packages/vuetify/src/locale/km.ts +++ b/packages/vuetify/src/locale/km.ts @@ -2,6 +2,7 @@ export default { badge: 'ផ្លាក', open: 'បើក', close: 'បិទ', + dismiss: 'Dismiss', confirmEdit: { ok: 'យល់ព្រម', cancel: 'បោះបង់', diff --git a/packages/vuetify/src/locale/ko.ts b/packages/vuetify/src/locale/ko.ts index f0c212dd701..f038b7c3679 100644 --- a/packages/vuetify/src/locale/ko.ts +++ b/packages/vuetify/src/locale/ko.ts @@ -2,6 +2,7 @@ export default { badge: '배지', open: 'Open', close: '닫기', + dismiss: 'Dismiss', confirmEdit: { ok: 'OK', cancel: 'Cancel', diff --git a/packages/vuetify/src/locale/lt.ts b/packages/vuetify/src/locale/lt.ts index 82174e1330c..307340e31a3 100644 --- a/packages/vuetify/src/locale/lt.ts +++ b/packages/vuetify/src/locale/lt.ts @@ -2,6 +2,7 @@ export default { badge: 'Ženklelis', open: 'Atidaryti', close: 'Uždaryti', + dismiss: 'Dismiss', confirmEdit: { ok: 'OK', cancel: 'Atšaukti', diff --git a/packages/vuetify/src/locale/lv.ts b/packages/vuetify/src/locale/lv.ts index 911cca0f464..eb493589646 100644 --- a/packages/vuetify/src/locale/lv.ts +++ b/packages/vuetify/src/locale/lv.ts @@ -2,6 +2,7 @@ export default { badge: 'Žetons', open: 'Open', close: 'Aizvērt', + dismiss: 'Dismiss', confirmEdit: { ok: 'OK', cancel: 'Cancel', diff --git a/packages/vuetify/src/locale/nl.ts b/packages/vuetify/src/locale/nl.ts index de2fe1fb9e9..b8c6032fe32 100644 --- a/packages/vuetify/src/locale/nl.ts +++ b/packages/vuetify/src/locale/nl.ts @@ -2,6 +2,7 @@ export default { badge: 'Insigne', open: 'Openen', close: 'Sluiten', + dismiss: 'Dismiss', confirmEdit: { ok: 'OK', cancel: 'Annuleren', diff --git a/packages/vuetify/src/locale/no.ts b/packages/vuetify/src/locale/no.ts index 3fb78ac86b0..c9fc72ddfc6 100644 --- a/packages/vuetify/src/locale/no.ts +++ b/packages/vuetify/src/locale/no.ts @@ -2,6 +2,7 @@ export default { badge: 'Skilt', open: 'Åpne', close: 'Lukk', + dismiss: 'Dismiss', confirmEdit: { ok: 'OK', cancel: 'Avbryt', diff --git a/packages/vuetify/src/locale/pl.ts b/packages/vuetify/src/locale/pl.ts index bdb1000c1ff..0e45ad375df 100644 --- a/packages/vuetify/src/locale/pl.ts +++ b/packages/vuetify/src/locale/pl.ts @@ -2,6 +2,7 @@ export default { badge: 'Odznaka', open: 'Otwórz', close: 'Zamknij', + dismiss: 'Dismiss', confirmEdit: { ok: 'OK', cancel: 'Anuluj', diff --git a/packages/vuetify/src/locale/pt.ts b/packages/vuetify/src/locale/pt.ts index 52ec4131ddd..d6094cb7edd 100644 --- a/packages/vuetify/src/locale/pt.ts +++ b/packages/vuetify/src/locale/pt.ts @@ -2,6 +2,7 @@ export default { badge: 'Distintivo', open: 'Abrir', close: 'Fechar', + dismiss: 'Dismiss', confirmEdit: { ok: 'OK', cancel: 'Cancel', diff --git a/packages/vuetify/src/locale/ro.ts b/packages/vuetify/src/locale/ro.ts index e0016f9088f..615b2861637 100644 --- a/packages/vuetify/src/locale/ro.ts +++ b/packages/vuetify/src/locale/ro.ts @@ -2,6 +2,7 @@ export default { badge: 'Insignă', open: 'Open', close: 'Închideți', + dismiss: 'Dismiss', confirmEdit: { ok: 'OK', cancel: 'Anulează', diff --git a/packages/vuetify/src/locale/ru.ts b/packages/vuetify/src/locale/ru.ts index 7af99bc3779..3c6eb2cde0e 100644 --- a/packages/vuetify/src/locale/ru.ts +++ b/packages/vuetify/src/locale/ru.ts @@ -2,6 +2,7 @@ export default { badge: 'Знак', open: 'Открыть', close: 'Закрыть', + dismiss: 'Dismiss', confirmEdit: { ok: 'ОК', cancel: 'Отмена', diff --git a/packages/vuetify/src/locale/sk.ts b/packages/vuetify/src/locale/sk.ts index eef18e9739b..9b1ffe2481e 100644 --- a/packages/vuetify/src/locale/sk.ts +++ b/packages/vuetify/src/locale/sk.ts @@ -2,6 +2,7 @@ export default { badge: 'Odznak', open: 'Otvoriť', close: 'Zavrieť', + dismiss: 'Dismiss', confirmEdit: { ok: 'OK', cancel: 'Zrušiť', diff --git a/packages/vuetify/src/locale/sl.ts b/packages/vuetify/src/locale/sl.ts index f31c327c6b5..c36e668c611 100644 --- a/packages/vuetify/src/locale/sl.ts +++ b/packages/vuetify/src/locale/sl.ts @@ -2,6 +2,7 @@ export default { badge: 'Značka', open: 'Odpri', close: 'Zapri', + dismiss: 'Dismiss', confirmEdit: { ok: 'V redu', cancel: 'Prekliči', diff --git a/packages/vuetify/src/locale/sr-Cyrl.ts b/packages/vuetify/src/locale/sr-Cyrl.ts index f923df03f01..6e9c1f9cc16 100644 --- a/packages/vuetify/src/locale/sr-Cyrl.ts +++ b/packages/vuetify/src/locale/sr-Cyrl.ts @@ -2,6 +2,7 @@ export default { badge: 'Значка', open: 'Open', close: 'Затвори', + dismiss: 'Dismiss', confirmEdit: { ok: 'OK', cancel: 'Cancel', diff --git a/packages/vuetify/src/locale/sr-Latn.ts b/packages/vuetify/src/locale/sr-Latn.ts index 98dc97ab4ce..485d81bd2d0 100644 --- a/packages/vuetify/src/locale/sr-Latn.ts +++ b/packages/vuetify/src/locale/sr-Latn.ts @@ -2,6 +2,7 @@ export default { badge: 'Značka', open: 'Open', close: 'Zatvori', + dismiss: 'Dismiss', confirmEdit: { ok: 'OK', cancel: 'Cancel', diff --git a/packages/vuetify/src/locale/sv.ts b/packages/vuetify/src/locale/sv.ts index 5818b705339..2a0b576b7aa 100644 --- a/packages/vuetify/src/locale/sv.ts +++ b/packages/vuetify/src/locale/sv.ts @@ -2,6 +2,7 @@ export default { badge: 'Bricka', open: 'Open', close: 'Stäng', + dismiss: 'Dismiss', confirmEdit: { ok: 'OK', cancel: 'Avbryt', diff --git a/packages/vuetify/src/locale/th.ts b/packages/vuetify/src/locale/th.ts index cd22cdcdd58..672643cd6c0 100644 --- a/packages/vuetify/src/locale/th.ts +++ b/packages/vuetify/src/locale/th.ts @@ -2,6 +2,7 @@ export default { badge: 'สัญลักษณ์', open: 'Open', close: 'ปิด', + dismiss: 'Dismiss', confirmEdit: { ok: 'OK', cancel: 'Cancel', diff --git a/packages/vuetify/src/locale/tr.ts b/packages/vuetify/src/locale/tr.ts index 7f0242c6b81..2463d23ba1e 100644 --- a/packages/vuetify/src/locale/tr.ts +++ b/packages/vuetify/src/locale/tr.ts @@ -2,6 +2,7 @@ export default { badge: 'rozet', open: 'Open', close: 'Kapat', + dismiss: 'Dismiss', confirmEdit: { ok: 'OK', cancel: 'Cancel', diff --git a/packages/vuetify/src/locale/uk.ts b/packages/vuetify/src/locale/uk.ts index ab6af48c040..4d1d28540ac 100644 --- a/packages/vuetify/src/locale/uk.ts +++ b/packages/vuetify/src/locale/uk.ts @@ -2,6 +2,7 @@ export default { badge: 'Знак', open: 'Open', close: 'Закрити', + dismiss: 'Dismiss', confirmEdit: { ok: 'OK', cancel: 'Cancel', diff --git a/packages/vuetify/src/locale/vi.ts b/packages/vuetify/src/locale/vi.ts index 4d0a1b7b27e..67915a2dc1e 100644 --- a/packages/vuetify/src/locale/vi.ts +++ b/packages/vuetify/src/locale/vi.ts @@ -2,6 +2,7 @@ export default { badge: 'Huy hiệu', open: 'Open', close: 'Đóng', + dismiss: 'Dismiss', confirmEdit: { ok: 'OK', cancel: 'Cancel', diff --git a/packages/vuetify/src/locale/zh-Hans.ts b/packages/vuetify/src/locale/zh-Hans.ts index 6dddccf4a68..dec1c61f811 100644 --- a/packages/vuetify/src/locale/zh-Hans.ts +++ b/packages/vuetify/src/locale/zh-Hans.ts @@ -2,6 +2,7 @@ export default { badge: '徽章', open: 'Open', close: '关闭', + dismiss: 'Dismiss', confirmEdit: { ok: 'OK', cancel: 'Cancel', diff --git a/packages/vuetify/src/locale/zh-Hant.ts b/packages/vuetify/src/locale/zh-Hant.ts index 013282d2f85..dbae45b6526 100644 --- a/packages/vuetify/src/locale/zh-Hant.ts +++ b/packages/vuetify/src/locale/zh-Hant.ts @@ -2,6 +2,7 @@ export default { badge: '徽章', open: 'Open', close: '關閉', + dismiss: 'Dismiss', confirmEdit: { ok: 'OK', cancel: 'Cancel', diff --git a/packages/vuetify/src/styles/settings/_variables.scss b/packages/vuetify/src/styles/settings/_variables.scss index ebddd5d20a8..615e71b0e77 100644 --- a/packages/vuetify/src/styles/settings/_variables.scss +++ b/packages/vuetify/src/styles/settings/_variables.scss @@ -208,7 +208,7 @@ $typography: map-deep-merge( 'h1': ( 'size': 6rem, 'weight': 300, - 'line-height': 6rem, + 'line-height': 1, 'letter-spacing': -.015625em, 'font-family': $heading-font-family, 'text-transform': none @@ -216,7 +216,7 @@ $typography: map-deep-merge( 'h2': ( 'size': 3.75rem, 'weight': 300, - 'line-height': 3.75rem, + 'line-height': 1, 'letter-spacing': -.0083333333em, 'font-family': $heading-font-family, 'text-transform': none @@ -224,7 +224,7 @@ $typography: map-deep-merge( 'h3': ( 'size': 3rem, 'weight': 400, - 'line-height': 3.125rem, + 'line-height': 1.05, 'letter-spacing': normal, 'font-family': $heading-font-family, 'text-transform': none @@ -232,7 +232,7 @@ $typography: map-deep-merge( 'h4': ( 'size': 2.125rem, 'weight': 400, - 'line-height': 2.5rem, + 'line-height': 1.175, 'letter-spacing': .0073529412em, 'font-family': $heading-font-family, 'text-transform': none @@ -240,7 +240,7 @@ $typography: map-deep-merge( 'h5': ( 'size': 1.5rem, 'weight': 400, - 'line-height': 2rem, + 'line-height': 1.333, 'letter-spacing': normal, 'font-family': $heading-font-family, 'text-transform': none @@ -248,7 +248,7 @@ $typography: map-deep-merge( 'h6': ( 'size': 1.25rem, 'weight': 500, - 'line-height': 2rem, + 'line-height': 1.6, 'letter-spacing': .0125em, 'font-family': $heading-font-family, 'text-transform': none @@ -256,7 +256,7 @@ $typography: map-deep-merge( 'subtitle-1': ( 'size': 1rem, 'weight': normal, - 'line-height': 1.75rem, + 'line-height': 1.75, 'letter-spacing': .009375em, 'font-family': $body-font-family, 'text-transform': none @@ -264,7 +264,7 @@ $typography: map-deep-merge( 'subtitle-2': ( 'size': .875rem, 'weight': 500, - 'line-height': 1.375rem, + 'line-height': 1.6, 'letter-spacing': .0071428571em, 'font-family': $body-font-family, 'text-transform': none @@ -272,7 +272,7 @@ $typography: map-deep-merge( 'body-1': ( 'size': 1rem, 'weight': 400, - 'line-height': 1.5rem, + 'line-height': 1.5, 'letter-spacing': .03125em, 'font-family': $body-font-family, 'text-transform': none @@ -280,7 +280,7 @@ $typography: map-deep-merge( 'body-2': ( 'size': .875rem, 'weight': 400, - 'line-height': 1.25rem, + 'line-height': 1.425, 'letter-spacing': .0178571429em, 'font-family': $body-font-family, 'text-transform': none @@ -288,7 +288,7 @@ $typography: map-deep-merge( 'button': ( 'size': .875rem, 'weight': 500, - 'line-height': 2.25rem, + 'line-height': 2.6, 'letter-spacing': .0892857143em, 'font-family': $body-font-family, 'text-transform': uppercase @@ -296,7 +296,7 @@ $typography: map-deep-merge( 'caption': ( 'size': .75rem, 'weight': 400, - 'line-height': 1.25rem, + 'line-height': 1.667, 'letter-spacing': .0333333333em, 'font-family': $body-font-family, 'text-transform': none @@ -304,7 +304,7 @@ $typography: map-deep-merge( 'overline': ( 'size': .75rem, 'weight': 500, - 'line-height': 2rem, + 'line-height': 2.667, 'letter-spacing': .1666666667em, 'font-family': $body-font-family, 'text-transform': uppercase @@ -317,7 +317,14 @@ $flat-typography: () !default; @each $type, $values in $typography { $flat-typography: map-deep-merge( $flat-typography, - (#{$type}: map.values($values)) + (#{$type}: ( + map.get($values, 'size'), + map.get($values, 'weight'), + map.get($values, 'line-height'), + map.get($values, 'letter-spacing'), + map.get($values, 'font-family'), + map.get($values, 'text-transform'), + )) ); } diff --git a/yarn.lock b/yarn.lock index 8ab0eec4b9e..bbae562cc96 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4108,10 +4108,10 @@ dependencies: upath "^2.0.1" -"@vuetify/one@^1.7.0": - version "1.7.0" - resolved "https://registry.yarnpkg.com/@vuetify/one/-/one-1.7.0.tgz#0170d6f0178c1525a02b4b1d732afc87a706c01d" - integrity sha512-VQhbEPG45Y6+sHuMy15kINOmDjCsMD9zhjqF1kZAY93IadQNUQk18pQ8hW4EprXsTebLXEuzdcqGSt8Hew3qpg== +"@vuetify/one@^1.7.2": + version "1.7.2" + resolved "https://registry.yarnpkg.com/@vuetify/one/-/one-1.7.2.tgz#fb66fe7319ca100d3ce61362e8a8d677bd58664e" + integrity sha512-UcvihL55oDtWiJpzfIjuyGmjpTqyr4RBH1y69bin/kZDVzuQCm+Sni8D7eUFpTsQqyOzNzxAkSkRZB+wP4Z5og== "@vueuse/head@^1.3.1": version "1.3.1"