@veritree/ui 0.2.0 → 0.3.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/index.js CHANGED
@@ -30,6 +30,22 @@ import VTTabList from './src/Tabs/VTTabList.vue';
30
30
  import VTTabPanel from './src/Tabs/VTTabPanel.vue';
31
31
  import VTTabPanels from './src/Tabs/VTTabPanels.vue';
32
32
 
33
+ import VTDialog from './src/Dialog/VTDialog.vue';
34
+ import VTDialogClose from './src/Dialog/VTDialogClose.vue';
35
+ import VTDialogContent from './src/Dialog/VTDialogContent.vue';
36
+ import VTDialogFooter from './src/Dialog/VTDialogFooter.vue';
37
+ import VTDialogHeader from './src/Dialog/VTDialogHeader.vue';
38
+ import VTDialogMain from './src/Dialog/VTDialogMain.vue';
39
+ import VTDialogOverlay from './src/Dialog/VTDialogOverlay.vue';
40
+
41
+ import VTDrawer from './src/Drawer/VTDrawer.vue';
42
+ import VTDrawerClose from './src/Drawer/VTDrawerClose.vue';
43
+ import VTDrawerContent from './src/Drawer/VTDrawerContent.vue';
44
+ import VTDrawerFooter from './src/Drawer/VTDrawerFooter.vue';
45
+ import VTDrawerHeader from './src/Drawer/VTDrawerHeader.vue';
46
+ import VTDrawerMain from './src/Drawer/VTDrawerMain.vue';
47
+ import VTDrawerOverlay from './src/Drawer/VTDrawerOverlay.vue';
48
+
33
49
  export {
34
50
  VTAlert,
35
51
  // VTSpinner,
@@ -54,5 +70,19 @@ export {
54
70
  VTTabList,
55
71
  VTTabPanel,
56
72
  VTTabPanels,
73
+ VTDrawer,
74
+ VTDrawerClose,
75
+ VTDrawerContent,
76
+ VTDrawerFooter,
77
+ VTDrawerHeader,
78
+ VTDrawerMain,
79
+ VTDrawerOverlay,
80
+ VTDialog,
81
+ VTDialogClose,
82
+ VTDialogContent,
83
+ VTDialogFooter,
84
+ VTDialogHeader,
85
+ VTDialogMain,
86
+ VTDialogOverlay,
57
87
  }
58
88
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@veritree/ui",
3
- "version": "0.2.0",
3
+ "version": "0.3.0",
4
4
  "description": "veritree ui library",
5
5
  "type": "module",
6
6
  "main": "index.js",
@@ -0,0 +1,118 @@
1
+ <template>
2
+ <div
3
+ v-if="visible"
4
+ :id="id"
5
+ :class="{
6
+ Dialog: headless,
7
+ 'fixed inset-0 z-50 grid grid-cols-1 grid-rows-1 p-4 md:p-8': !headless,
8
+ }"
9
+ aria-modal="true"
10
+ @click="hide"
11
+ >
12
+ <slot></slot>
13
+ </div>
14
+ </template>
15
+
16
+ <script>
17
+ import { genId } from '~/utils/ids';
18
+
19
+ export default {
20
+ name: 'VTDialog',
21
+
22
+ provide() {
23
+ return {
24
+ api: () => {
25
+ const id = this.id;
26
+ const isDark = this.dark;
27
+ const isHeadless = this.headless;
28
+
29
+ const registerContent = (content) => {
30
+ if (!content) return;
31
+ this.content = content;
32
+ };
33
+
34
+ const registerOverlay = (overlay) => {
35
+ if (!overlay) return;
36
+ this.overlay = overlay;
37
+ };
38
+
39
+ const hide = () => this.hide();
40
+
41
+ const emit = () => this.emit();
42
+
43
+ return {
44
+ id,
45
+ isDark,
46
+ isHeadless,
47
+ hide,
48
+ emit,
49
+ registerContent,
50
+ registerOverlay,
51
+ };
52
+ },
53
+ };
54
+ },
55
+
56
+ model: {
57
+ prop: 'visible',
58
+ },
59
+
60
+ props: {
61
+ visible: {
62
+ type: Boolean,
63
+ default: false,
64
+ },
65
+ headless: {
66
+ type: Boolean,
67
+ default: false,
68
+ },
69
+ dark: {
70
+ type: Boolean,
71
+ default: false,
72
+ },
73
+ },
74
+
75
+ data() {
76
+ return {
77
+ id: `dialog-${genId()}`,
78
+ content: null,
79
+ overlay: null,
80
+ };
81
+ },
82
+
83
+ computed: {
84
+ hasContent() {
85
+ return this.content !== null;
86
+ },
87
+
88
+ hasOverlay() {
89
+ return this.overlay !== null;
90
+ },
91
+ },
92
+
93
+ watch: {
94
+ visible(isVisible) {
95
+ if (!isVisible) this.hide();
96
+ },
97
+ },
98
+
99
+ mounted() {
100
+ if (this.hasContent) this.content.show();
101
+ if (this.hasOverlay) this.overlay.show();
102
+ },
103
+
104
+ methods: {
105
+ hide() {
106
+ if (this.hasOverlay) this.overlay.hide();
107
+ if (this.hasContent) this.content.hide();
108
+ },
109
+
110
+ emit() {
111
+ this.$nextTick(() => {
112
+ this.$emit('input', false);
113
+ this.$emit('hidden');
114
+ });
115
+ },
116
+ },
117
+ };
118
+ </script>
@@ -0,0 +1,46 @@
1
+ <template>
2
+ <VTButton
3
+ variant="icon"
4
+ :class="{
5
+ 'Dialog-close': headless,
6
+ 'absolute right-4 top-4': !headless,
7
+ }"
8
+ :theme="theme"
9
+ @click.prevent="hide"
10
+ ><slot><IconClose class="h-5 w-5" /></slot
11
+ ></VTButton>
12
+ </template>
13
+
14
+ <script>
15
+ import { IconClose } from '@veritree/icons';
16
+ import VTButton from '~/components/Button/VTButton.vue';
17
+
18
+ export default {
19
+ name: 'VTDialogClose',
20
+
21
+ components: { IconClose, VTButton },
22
+
23
+ inject: ['api'],
24
+
25
+ computed: {
26
+ dark() {
27
+ return this.api().isDark;
28
+ },
29
+
30
+ headless() {
31
+ return this.api().isHeadless;
32
+ },
33
+
34
+ // temporary till button theme is implemented
35
+ theme() {
36
+ return this.dark ? 'dark' : null;
37
+ },
38
+ },
39
+
40
+ methods: {
41
+ hide() {
42
+ this.api().hide();
43
+ },
44
+ },
45
+ };
46
+ </script>
@@ -0,0 +1,72 @@
1
+ <template>
2
+ <transition
3
+ enter-active-class="duration-300 ease-out"
4
+ enter-class="translate-y-[50px] opacity-0"
5
+ enter-to-class="translate-y-0 opacity-100"
6
+ leave-active-class="duration-300 ease-out"
7
+ leave-class="translate-y-0 opacity-100"
8
+ leave-to-class="translate-y-[50px] opacity-0"
9
+ @after-leave="hideDialog"
10
+ >
11
+ <div
12
+ v-show="visible"
13
+ :class="{
14
+ 'Dialog-content': headless,
15
+ 'relative m-auto max-h-full max-w-full overflow-auto rounded p-6 focus:outline-none sm:p-10':
16
+ !headless,
17
+ 'bg-white': !dark,
18
+ 'bg-fd-600': dark,
19
+ }"
20
+ tabindex="-1"
21
+ @keyup.esc="hide"
22
+ @click.stop
23
+ >
24
+ <slot></slot>
25
+ </div>
26
+ </transition>
27
+ </template>
28
+
29
+ <script>
30
+ export default {
31
+ name: 'VTDialogContent',
32
+
33
+ inject: ['api'],
34
+
35
+ data() {
36
+ return {
37
+ visible: false,
38
+ };
39
+ },
40
+
41
+ computed: {
42
+ dark() {
43
+ return this.api().isDark;
44
+ },
45
+
46
+ headless() {
47
+ return this.api().isHeadless;
48
+ },
49
+ },
50
+
51
+ mounted() {
52
+ this.api().registerContent(this);
53
+ this.show();
54
+
55
+ this.$nextTick(() => this.$el.focus());
56
+ },
57
+
58
+ methods: {
59
+ show() {
60
+ this.visible = true;
61
+ },
62
+
63
+ hide() {
64
+ this.visible = false;
65
+ },
66
+
67
+ hideDialog() {
68
+ this.api().emit();
69
+ },
70
+ },
71
+ };
72
+ </script>
@@ -0,0 +1,30 @@
1
+ <template>
2
+ <component :is="as" :class="{ 'Dialog-footer': headless }">
3
+ <slot></slot>
4
+ </component>
5
+ </template>
6
+
7
+ <script>
8
+ export default {
9
+ name: 'VTDialogFooter',
10
+
11
+ inject: ['api'],
12
+
13
+ props: {
14
+ as: {
15
+ type: String,
16
+ default: 'footer',
17
+ },
18
+ },
19
+
20
+ computed: {
21
+ dark() {
22
+ return this.api().isDark;
23
+ },
24
+
25
+ headless() {
26
+ return this.api().isHeadless;
27
+ },
28
+ },
29
+ };
30
+ </script>
@@ -0,0 +1,56 @@
1
+ <template>
2
+ <component
3
+ :is="as"
4
+ :id="id"
5
+ :class="{
6
+ 'Dialog-header': headless,
7
+ 'mb-8 text-2xl font-semibold': !headless,
8
+ }"
9
+ >
10
+ <slot></slot>
11
+ </component>
12
+ </template>
13
+
14
+ <script>
15
+ export default {
16
+ name: 'VTDialogHeader',
17
+
18
+ inject: ['api'],
19
+
20
+ props: {
21
+ as: {
22
+ type: String,
23
+ default: 'header',
24
+ },
25
+ },
26
+
27
+ computed: {
28
+ dark() {
29
+ return this.api().isDark;
30
+ },
31
+
32
+ headless() {
33
+ return this.api().isHeadless;
34
+ },
35
+
36
+ id() {
37
+ return `${this.api().id}-header`;
38
+ },
39
+ },
40
+
41
+ mounted() {
42
+ this.setDialogLabelledby();
43
+ },
44
+
45
+ methods: {
46
+ // In here because if there is no header, the dialog will not be labelled by
47
+ setDialogLabelledby() {
48
+ const dialog = document.getElementById(this.api().id);
49
+
50
+ if (dialog) {
51
+ dialog.setAttribute('aria-labelledby', this.id);
52
+ }
53
+ },
54
+ },
55
+ };
56
+ </script>
@@ -0,0 +1,49 @@
1
+ <template>
2
+ <component :is="as" :id="id" :class="{ 'Dialog-body': headless }">
3
+ <slot></slot>
4
+ </component>
5
+ </template>
6
+
7
+ <script>
8
+ export default {
9
+ name: 'VTDialogMain',
10
+
11
+ inject: ['api'],
12
+
13
+ props: {
14
+ as: {
15
+ type: String,
16
+ default: 'main',
17
+ },
18
+ },
19
+
20
+ computed: {
21
+ dark() {
22
+ return this.api().isDark;
23
+ },
24
+
25
+ headless() {
26
+ return this.api().isHeadless;
27
+ },
28
+
29
+ id() {
30
+ return `${this.api().id}-desc`;
31
+ },
32
+ },
33
+
34
+ mounted() {
35
+ this.setDialogDescribedby();
36
+ },
37
+
38
+ methods: {
39
+ // In here because if there is no body, the dialog will not be described by
40
+ setDialogDescribedby() {
41
+ const dialog = document.getElementById(this.api().id);
42
+
43
+ if (dialog) {
44
+ dialog.setAttribute('aria-describedby', this.id);
45
+ }
46
+ },
47
+ },
48
+ };
49
+ </script>
@@ -0,0 +1,52 @@
1
+ <template>
2
+ <FadeInOut>
3
+ <div
4
+ v-if="visible"
5
+ :class="{
6
+ 'Dialog-overlay': headless,
7
+ 'fixed inset-0 bg-fd-450/80': !headless,
8
+ }"
9
+ ></div>
10
+ </FadeInOut>
11
+ </template>
12
+
13
+ <script>
14
+ import FadeInOut from '~/components/Transitions/FadeInOut.vue';
15
+
16
+ export default {
17
+ name: 'VTDialogOverlay',
18
+
19
+ components: {
20
+ FadeInOut,
21
+ },
22
+
23
+ inject: ['api'],
24
+
25
+ data() {
26
+ return {
27
+ visible: false,
28
+ };
29
+ },
30
+
31
+ computed: {
32
+ dark() {
33
+ return this.api().isDark;
34
+ },
35
+
36
+ headless() {
37
+ return this.api().isHeadless;
38
+ },
39
+ },
40
+
41
+ mounted() {
42
+ this.visible = true;
43
+ this.api().registerOverlay(this);
44
+ },
45
+
46
+ methods: {
47
+ hide() {
48
+ this.visible = false;
49
+ },
50
+ },
51
+ };
52
+ </script>
@@ -0,0 +1,113 @@
1
+ <template>
2
+ <div
3
+ v-if="visible"
4
+ :id="id"
5
+ :class="{ Drawer: headless, 'fixed inset-0 z-50 h-screen': !headless }"
6
+ aria-modal="true"
7
+ @click="hide"
8
+ >
9
+ <slot></slot>
10
+ </div>
11
+ </template>
12
+
13
+ <script>
14
+ import { genId } from '~/utils/ids';
15
+
16
+ export default {
17
+ name: 'VTDrawer',
18
+
19
+ provide() {
20
+ return {
21
+ api: () => {
22
+ const id = this.id;
23
+ const isDark = this.dark;
24
+ const isHeadless = this.headless;
25
+ const isRight = this.right;
26
+
27
+ const registerContent = (content) => {
28
+ if (!content) return;
29
+ this.content = content;
30
+ };
31
+
32
+ const registerOverlay = (overlay) => {
33
+ if (!overlay) return;
34
+ this.overlay = overlay;
35
+ };
36
+
37
+ const hide = () => this.hide();
38
+
39
+ const emit = () => this.emit();
40
+
41
+ return {
42
+ id,
43
+ isDark,
44
+ isHeadless,
45
+ isRight,
46
+ hide,
47
+ emit,
48
+ registerContent,
49
+ registerOverlay,
50
+ };
51
+ },
52
+ };
53
+ },
54
+
55
+ model: {
56
+ prop: 'visible',
57
+ },
58
+
59
+ props: {
60
+ visible: {
61
+ type: Boolean,
62
+ default: false,
63
+ },
64
+ headless: {
65
+ type: Boolean,
66
+ default: false,
67
+ },
68
+ dark: {
69
+ type: Boolean,
70
+ default: false,
71
+ },
72
+ right: {
73
+ type: Boolean,
74
+ default: false,
75
+ },
76
+ },
77
+
78
+ data() {
79
+ return {
80
+ id: `drawer-${genId()}`,
81
+ content: null,
82
+ overlay: null,
83
+ };
84
+ },
85
+
86
+ watch: {
87
+ visible(isVisible) {
88
+ if (!isVisible) this.hide();
89
+ },
90
+ },
91
+
92
+ mounted() {
93
+ if (!this.content) return;
94
+ this.content.show();
95
+ },
96
+
97
+ methods: {
98
+ hide() {
99
+ if (!this.content) return;
100
+ this.content.hide();
101
+ if (!this.overlay) return;
102
+ this.overlay.hide();
103
+ },
104
+
105
+ emit() {
106
+ this.$nextTick(() => {
107
+ this.$emit('input', false);
108
+ this.$emit('hidden');
109
+ });
110
+ },
111
+ },
112
+ };
113
+ </script>
@@ -0,0 +1,50 @@
1
+ <template>
2
+ <VTButton
3
+ variant="icon"
4
+ :class="{
5
+ 'Drawer-close': headless,
6
+ 'ml-auto mb-4 text-inherit sm:mb-8': !headless,
7
+ }"
8
+ :theme="theme"
9
+ @click.prevent="hide"
10
+ ><slot><IconLeft :class="{ 'rotate-180': right }" /></slot
11
+ ></VTButton>
12
+ </template>
13
+
14
+ <script>
15
+ import { IconLeft } from '@veritree/icons';
16
+ import VTButton from '~/components/Button/VTButton.vue';
17
+
18
+ export default {
19
+ name: 'VTDrawerClose',
20
+
21
+ components: { IconLeft, VTButton },
22
+
23
+ inject: ['api'],
24
+
25
+ computed: {
26
+ dark() {
27
+ return this.api().isDark;
28
+ },
29
+
30
+ headless() {
31
+ return this.api().isHeadless;
32
+ },
33
+
34
+ right() {
35
+ return this.api().isRight;
36
+ },
37
+
38
+ // temporary till button theme is implemented
39
+ theme() {
40
+ return this.dark ? 'dark' : null;
41
+ },
42
+ },
43
+
44
+ methods: {
45
+ hide() {
46
+ this.api().hide();
47
+ },
48
+ },
49
+ };
50
+ </script>
@@ -0,0 +1,97 @@
1
+ <template>
2
+ <transition
3
+ :enter-active-class="activeClass"
4
+ :enter-class="enterClass"
5
+ :enter-to-class="enterToClass"
6
+ :leave-active-class="activeClass"
7
+ :leave-class="leaveClass"
8
+ :leave-to-class="leaveToClass"
9
+ @after-leave="hideDrawer"
10
+ >
11
+ <div
12
+ v-show="visible"
13
+ :class="{
14
+ 'Drawer-content': headless,
15
+ 'absolute z-20 flex h-screen max-h-full max-w-full flex-col overflow-auto p-5 outline-0 sm:px-10 sm:py-6':
16
+ !headless,
17
+ 'bg-white': !dark,
18
+ 'bg-fd-600': dark,
19
+ 'right-0': right,
20
+ }"
21
+ tabindex="-1"
22
+ @keyup.esc="hide"
23
+ @click.stop
24
+ >
25
+ <slot></slot>
26
+ </div>
27
+ </transition>
28
+ </template>
29
+
30
+ <script>
31
+ export default {
32
+ name: 'VTDrawerContent',
33
+
34
+ inject: ['api'],
35
+
36
+ data() {
37
+ return {
38
+ visible: false,
39
+ };
40
+ },
41
+
42
+ computed: {
43
+ dark() {
44
+ return this.api().isDark;
45
+ },
46
+
47
+ headless() {
48
+ return this.api().isHeadless;
49
+ },
50
+
51
+ right() {
52
+ return this.api().isRight;
53
+ },
54
+
55
+ activeClass() {
56
+ return 'transform transition duration-300 ease-in-out';
57
+ },
58
+
59
+ enterClass() {
60
+ return `opacity-0 ${this.right ? '' : '-'}translate-x-full`;
61
+ },
62
+
63
+ enterToClass() {
64
+ return `opacity-100 ${this.right ? '-' : ''}translate-x-0`;
65
+ },
66
+
67
+ leaveClass() {
68
+ return `opacity-100 ${this.right ? '-' : ''}translate-x-0`;
69
+ },
70
+
71
+ leaveToClass() {
72
+ return `opacity-0 ${this.right ? '' : '-'}translate-x-full`;
73
+ },
74
+ },
75
+
76
+ mounted() {
77
+ this.api().registerContent(this);
78
+ this.show();
79
+
80
+ this.$nextTick(() => this.$el.focus());
81
+ },
82
+
83
+ methods: {
84
+ show() {
85
+ this.visible = true;
86
+ },
87
+
88
+ hide() {
89
+ this.visible = false;
90
+ },
91
+
92
+ hideDrawer() {
93
+ this.api().emit();
94
+ },
95
+ },
96
+ };
97
+ </script>
@@ -0,0 +1,30 @@
1
+ <template>
2
+ <component :is="as" :class="{ 'Dialog-footer': headless }">
3
+ <slot></slot>
4
+ </component>
5
+ </template>
6
+
7
+ <script>
8
+ export default {
9
+ name: 'VTDrawerFooter',
10
+
11
+ inject: ['api'],
12
+
13
+ props: {
14
+ as: {
15
+ type: String,
16
+ default: 'footer',
17
+ },
18
+ },
19
+
20
+ computed: {
21
+ dark() {
22
+ return this.api().isDark;
23
+ },
24
+
25
+ headless() {
26
+ return this.api().isHeadless;
27
+ },
28
+ },
29
+ };
30
+ </script>
@@ -0,0 +1,56 @@
1
+ <template>
2
+ <component
3
+ :is="as"
4
+ :id="id"
5
+ :class="{
6
+ 'Drawer-header': headless,
7
+ 'mb-8 text-2xl font-semibold': !headless,
8
+ }"
9
+ >
10
+ <slot></slot>
11
+ </component>
12
+ </template>
13
+
14
+ <script>
15
+ export default {
16
+ name: 'VTDrawerHeader',
17
+
18
+ inject: ['api'],
19
+
20
+ props: {
21
+ as: {
22
+ type: String,
23
+ default: 'header',
24
+ },
25
+ },
26
+
27
+ computed: {
28
+ dark() {
29
+ return this.api().isDark;
30
+ },
31
+
32
+ headless() {
33
+ return this.api().isHeadless;
34
+ },
35
+
36
+ id() {
37
+ return `${this.api().id}-header`;
38
+ },
39
+ },
40
+
41
+ mounted() {
42
+ this.setDialogLabelledby();
43
+ },
44
+
45
+ methods: {
46
+ // In here because if there is no header, the dialog will not be labelled by
47
+ setDialogLabelledby() {
48
+ const dialog = document.getElementById(this.api().id);
49
+
50
+ if (dialog) {
51
+ dialog.setAttribute('aria-labelledby', this.id);
52
+ }
53
+ },
54
+ },
55
+ };
56
+ </script>
@@ -0,0 +1,53 @@
1
+ <template>
2
+ <component
3
+ :is="as"
4
+ :id="id"
5
+ :class="{ 'Drawer-body': headless, 'flex-1': !headless }"
6
+ >
7
+ <slot></slot>
8
+ </component>
9
+ </template>
10
+
11
+ <script>
12
+ export default {
13
+ name: 'VTDrawerMain',
14
+
15
+ inject: ['api'],
16
+
17
+ props: {
18
+ as: {
19
+ type: String,
20
+ default: 'main',
21
+ },
22
+ },
23
+
24
+ computed: {
25
+ dark() {
26
+ return this.api().isDark;
27
+ },
28
+
29
+ headless() {
30
+ return this.api().isHeadless;
31
+ },
32
+
33
+ id() {
34
+ return `${this.api().id}-desc`;
35
+ },
36
+ },
37
+
38
+ mounted() {
39
+ this.setDialogDescribedby();
40
+ },
41
+
42
+ methods: {
43
+ // In here because if there is no body, the dialog will not be described by
44
+ setDialogDescribedby() {
45
+ const dialog = document.getElementById(this.api().id);
46
+
47
+ if (dialog) {
48
+ dialog.setAttribute('aria-describedby', this.id);
49
+ }
50
+ },
51
+ },
52
+ };
53
+ </script>
@@ -0,0 +1,50 @@
1
+ <template>
2
+ <FadeInOut>
3
+ <div
4
+ v-if="visible"
5
+ :class="{
6
+ 'Drawer-overlay': headless,
7
+ 'fixed inset-0 z-10 bg-fd-450/80': !headless,
8
+ }"
9
+ ></div>
10
+ </FadeInOut>
11
+ </template>
12
+
13
+ <script>
14
+ import FadeInOut from '~/components/Transitions/FadeInOut.vue';
15
+
16
+ export default {
17
+ components: {
18
+ FadeInOut,
19
+ },
20
+
21
+ inject: ['api'],
22
+
23
+ data() {
24
+ return {
25
+ visible: false,
26
+ };
27
+ },
28
+
29
+ computed: {
30
+ dark() {
31
+ return this.api().isDark;
32
+ },
33
+
34
+ headless() {
35
+ return this.api().isHeadless;
36
+ },
37
+ },
38
+
39
+ mounted() {
40
+ this.visible = true;
41
+ this.api().registerOverlay(this);
42
+ },
43
+
44
+ methods: {
45
+ hide() {
46
+ this.visible = false;
47
+ },
48
+ },
49
+ };
50
+ </script>