diff --git a/src/components/register.ts b/src/components/register.ts index f90805f7ac..3c8d38ecfb 100644 --- a/src/components/register.ts +++ b/src/components/register.ts @@ -10,6 +10,7 @@ import VDialog from './v-dialog'; import VDivider from './v-divider'; import VForm from './v-form'; import VHover from './v-hover/'; +import VModal from './v-modal/'; import VIcon from './v-icon/'; import VInput from './v-input/'; import VItemGroup, { VItem } from './v-item-group'; @@ -48,6 +49,7 @@ Vue.component('v-dialog', VDialog); Vue.component('v-divider', VDivider); Vue.component('v-form', VForm); Vue.component('v-hover', VHover); +Vue.component('v-modal', VModal); Vue.component('v-icon', VIcon); Vue.component('v-input', VInput); Vue.component('v-item-group', VItemGroup); diff --git a/src/components/v-card/v-card.vue b/src/components/v-card/v-card.vue index c74c7ebfb6..8903dd6881 100644 --- a/src/components/v-card/v-card.vue +++ b/src/components/v-card/v-card.vue @@ -28,13 +28,15 @@ export default defineComponent({ .v-card { --v-card-min-width: none; --v-card-max-width: 400px; + --v-card-height: auto; --v-card-min-height: none; - --v-card-max-height: none; + --v-card-max-height: min-content; --v-card-padding: 16px; --v-card-background-color: var(--highlight); min-width: var(--v-card-min-width); max-width: var(--v-card-max-width); + height: var(--v-card-height); min-height: var(--v-card-min-height); max-height: var(--v-card-max-height); background-color: var(--v-card-background-color); diff --git a/src/components/v-dialog/v-dialog.vue b/src/components/v-dialog/v-dialog.vue index 5b962c116f..17d843cc01 100644 --- a/src/components/v-dialog/v-dialog.vue +++ b/src/components/v-dialog/v-dialog.vue @@ -3,12 +3,12 @@ -
- -
+ +
+
-
+
@@ -56,6 +56,8 @@ export default defineComponent({ diff --git a/src/components/v-list/readme.md b/src/components/v-list/readme.md index 8e332cc382..979c10ee62 100644 --- a/src/components/v-list/readme.md +++ b/src/components/v-list/readme.md @@ -66,7 +66,8 @@ A wrapper for list items that formats children nicely. Can be used on its own or | `dense` | Removes some padding to make the individual list item shorter | `false` | | `lines` | Sets if the list item will support `1`, `2`, or `3` lines of content | `null` | | `to` | Render as vue router-link with to link | `null` | -| `disabled` | Disable the list item | `false` | +| `disabled` | Disable the list item | `false` | +| `active` | Enable the list item's active state | `false` | ## Events diff --git a/src/components/v-list/v-list-item.vue b/src/components/v-list/v-list-item.vue index 1003450193..e3a5555999 100644 --- a/src/components/v-list/v-list-item.vue +++ b/src/components/v-list/v-list-item.vue @@ -5,6 +5,7 @@ class="v-list-item" :to="to" :class="{ + active, dense, link: isClickable, 'three-line': lines === 3, @@ -40,6 +41,10 @@ export default defineComponent({ type: Boolean, default: false, }, + active: { + type: Boolean, + default: false, + }, }, setup(props, { listeners }) { const component = computed(() => (props.to ? 'router-link' : 'li')); diff --git a/src/components/v-modal/index.ts b/src/components/v-modal/index.ts new file mode 100644 index 0000000000..c3a4ca2b16 --- /dev/null +++ b/src/components/v-modal/index.ts @@ -0,0 +1,4 @@ +import VModal from './v-modal.vue'; + +export { VModal }; +export default VModal; diff --git a/src/components/v-modal/readme.md b/src/components/v-modal/readme.md new file mode 100644 index 0000000000..9818b9ca27 --- /dev/null +++ b/src/components/v-modal/readme.md @@ -0,0 +1,73 @@ +# Modal + +A modal is basically an elaborate pre-configured dialog. It supports an optional left sidebar that allows for easier tab usage. + +## Usage + +```html + + Hello, world! + +``` + +```html + + + + Hello, world! + +``` + +```html + + + + + + + Hello, world! + I'm page 2! + I'm page 3! + + + + +``` + +## Props + +| Prop | Description | Default | +|--------------|-----------------------------------------------------------------|---------| +| `title`* | Title for the modal | | +| `subtitle` | Optional subtitle for the modal | | +| `active` | If the modal is active. Used in `v-model` | `false` | +| `persistent` | Prevent the user from exiting the modal by clicking the overlay | `false` | + +## Events + +| Event | Description | Value | +|----------|--------------------------|-----------| +| `toggle` | Sync the `v-model` value | `boolean` | + +## Slots +| Slot | Description | Data | +|-------------|--------------------------------------------------------|-------------------------| +| _default_ | Modal content | | +| `activator` | Element to enable the modal | `{ on: () => void }` | +| `sidebar` | Sidebar content for the modal. Often used for `v-tabs` | | +| `footer` | Footer content. Often used for action buttons | `{ close: () => void }` | + +## CSS Variables +n/a diff --git a/src/components/v-modal/v-modal.story.ts b/src/components/v-modal/v-modal.story.ts new file mode 100644 index 0000000000..465e80edac --- /dev/null +++ b/src/components/v-modal/v-modal.story.ts @@ -0,0 +1,84 @@ +import withPadding from '../../../.storybook/decorators/with-padding'; +import readme from './readme.md'; +import { defineComponent, ref } from '@vue/composition-api'; + +export default { + title: 'Components / Modal', + parameters: { + notes: readme, + }, + decorators: [withPadding], +}; + +export const basic = () => + defineComponent({ + setup() { + const active = ref(false); + return { active }; + }, + template: ` +
+ + + +

Hello world!

+ + +
+ +
+ `, + }); + +export const withNav = () => + defineComponent({ + setup() { + const active = ref(false); + const current = ref(['hello']); + return { active, current }; + }, + template: ` +
+ + + + + + + +

Hello world!

+
+ + +

I'm a modal with tabs

+
+
+ + + +
+ +
+ `, + }); diff --git a/src/components/v-modal/v-modal.test.ts b/src/components/v-modal/v-modal.test.ts new file mode 100644 index 0000000000..944a497925 --- /dev/null +++ b/src/components/v-modal/v-modal.test.ts @@ -0,0 +1,24 @@ +import { shallowMount, createLocalVue } from '@vue/test-utils'; +import VueCompositionAPI from '@vue/composition-api'; + +import VModal from './v-modal.vue'; +import VDialog from '@/components/v-dialog/'; +import VIcon from '@/components/v-icon/'; + +const localVue = createLocalVue(); +localVue.use(VueCompositionAPI); +localVue.component('v-dialog', VDialog); +localVue.component('v-icon', VIcon); + +describe('Components / Modal', () => { + it('Renders', () => { + const component = shallowMount(VModal, { + localVue, + propsData: { + title: 'My Modal', + }, + }); + + expect(component.isVueInstance()).toBe(true); + }); +}); diff --git a/src/components/v-modal/v-modal.vue b/src/components/v-modal/v-modal.vue new file mode 100644 index 0000000000..02b6e43ec5 --- /dev/null +++ b/src/components/v-modal/v-modal.vue @@ -0,0 +1,187 @@ + + + + + diff --git a/src/components/v-tabs/v-tab/v-tab.vue b/src/components/v-tabs/v-tab/v-tab.vue index 24b4beebcf..e64f94692d 100644 --- a/src/components/v-tabs/v-tab/v-tab.vue +++ b/src/components/v-tabs/v-tab/v-tab.vue @@ -1,11 +1,20 @@ diff --git a/src/compositions/groupable/groupable.ts b/src/compositions/groupable/groupable.ts index bb2f0bf43d..c191488496 100644 --- a/src/compositions/groupable/groupable.ts +++ b/src/compositions/groupable/groupable.ts @@ -11,9 +11,9 @@ type GroupableInstance = { * Used to make child item part of the group context. Needs to be used in a component that is a child * of a component that has the `useGroupableParent` composition enabled */ -export function useGroupable(value?: string | number) { +export function useGroupable(value?: string | number, group = 'item-group') { // Injects the registration / toggle functions from the parent scope - const parentFunctions = inject('item-group', null); + const parentFunctions = inject(group, null); if (isEmpty(parentFunctions)) { return { @@ -65,7 +65,8 @@ type GroupableParentOptions = { */ export function useGroupableParent( state: GroupableParentState = {}, - options: GroupableParentOptions = {} + options: GroupableParentOptions = {}, + group = 'item-group' ) { // References to the active state and value of the individual child items const items = ref([]); @@ -95,7 +96,7 @@ export function useGroupableParent( // Provide the needed functions to all children groupable components. Note: nested item groups // will override the item-group namespace, making nested item groups possible. - provide('item-group', { register, unregister, toggle }); + provide(group, { register, unregister, toggle }); // Whenever the value of the selection changes, we have to update all the children's internal // states. If not, you can have an activated item that's not actually active. diff --git a/src/compositions/groupable/readme.md b/src/compositions/groupable/readme.md index 120d928369..126215a892 100644 --- a/src/compositions/groupable/readme.md +++ b/src/compositions/groupable/readme.md @@ -8,7 +8,7 @@ the functionality of the `v-item-group` base component, and other groupable comp Use the `useGroupableParent` function in a parent component that will contain one or more components in it's slots (deeply nested or not) that use the `useGroupable` compositions. -### `useGroupableParent(state: GroupableParentState, options: GroupableParentOptions): void` +### `useGroupableParent(state: GroupableParentState, options: GroupableParentOptions, group: string): void` The `useGroupableParent` composition accepts two paremeters: state and options. State includes a `selection` key that can be used to pass an array of selected items, so you can @@ -42,7 +42,10 @@ export default defineComponent({ }); ``` -### `useGroupable(value: string | number): { active: Ref; toggle: () => void; }` +The optional group parameter allows you to control to what group this parent is registered. This +can be useful when you have complexly nested groups. + +### `useGroupable(value: string | number | undefined, group: string): { active: Ref; toggle: () => void; }` Registers this component as a child of the first parent component that uses the `useGroupableParent` component. @@ -61,3 +64,6 @@ export default defineComponent({ } }); ``` + +The optional group parameter allows you to control to what group this child is registered. This +can be useful when you have complexly nested groups.