sprintify-ui 0.0.11 → 0.0.12
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/dist/sprintify-ui.es.js +4906 -3570
- package/dist/style.css +1 -1
- package/dist/types/src/components/BaseCharacterCounter.vue.d.ts +143 -0
- package/dist/types/src/components/BaseInput.vue.d.ts +39 -5
- package/dist/types/src/components/BaseLoadingCover.vue.d.ts +72 -0
- package/dist/types/src/components/BaseModalCenter.vue.d.ts +8 -8
- package/dist/types/src/components/BaseModalSide.vue.d.ts +8 -8
- package/dist/types/src/components/BasePagination.vue.d.ts +105 -13
- package/dist/types/src/components/BasePaginationSimple.vue.d.ts +2 -2
- package/dist/types/src/components/BaseSelect.vue.d.ts +130 -26
- package/dist/types/src/components/BaseSwitch.vue.d.ts +15 -8
- package/dist/types/src/components/BaseTabItem.vue.d.ts +26 -4
- package/dist/types/src/components/BaseTextareaAutoresize.vue.d.ts +175 -21
- package/dist/types/src/components/index.d.ts +24 -1
- package/dist/types/src/index.d.ts +4 -0
- package/package.json +1 -1
- package/src/components/BaseCharacterCounter.stories.js +30 -0
- package/src/components/BaseCharacterCounter.vue +60 -0
- package/src/components/BaseDataIterator.stories.js +2 -2
- package/src/components/BaseDataIterator.vue +32 -38
- package/src/components/BaseDataTable.stories.js +2 -2
- package/src/components/BaseFileUploader.vue +4 -0
- package/src/components/BaseInput.stories.js +46 -0
- package/src/components/BaseInput.vue +10 -2
- package/src/components/BaseInputLabel.stories.js +31 -0
- package/src/components/BaseInputLabel.vue +1 -1
- package/src/components/BaseLoadingCover.stories.js +55 -0
- package/src/components/BaseLoadingCover.vue +19 -1
- package/src/components/BaseMenu.stories.js +125 -0
- package/src/components/BaseModalCenter.stories.js +61 -0
- package/src/components/BaseModalCenter.vue +2 -2
- package/src/components/BaseModalSide.stories.js +55 -0
- package/src/components/BaseModalSide.vue +2 -2
- package/src/components/BaseNavbar.stories.js +150 -0
- package/src/components/BaseNavbar.vue +3 -0
- package/src/components/BaseNavbarItem.vue +1 -0
- package/src/components/BaseNavbarItemContent.vue +3 -0
- package/src/components/BasePagination.stories.js +32 -0
- package/src/components/BasePagination.vue +126 -40
- package/src/components/BasePaginationSimple.vue +3 -3
- package/src/components/BasePanel.stories.js +56 -0
- package/src/components/BasePassword.stories.js +36 -0
- package/src/components/BasePassword.vue +11 -5
- package/src/components/BaseProcessRing.stories.js +27 -0
- package/src/components/BaseReadMore.stories.js +30 -0
- package/src/components/BaseReadMore.vue +1 -1
- package/src/components/BaseSelect.stories.js +67 -0
- package/src/components/BaseSelect.vue +144 -44
- package/src/components/BaseSideNavigation.stories.js +55 -0
- package/src/components/BaseSideNavigation.vue +7 -2
- package/src/components/BaseSideNavigationItem.vue +11 -3
- package/src/components/BaseSkeleton.stories.js +36 -0
- package/src/components/BaseSwitch.stories.js +101 -0
- package/src/components/BaseSwitch.vue +90 -12
- package/src/components/BaseSystemAlert.stories.js +63 -0
- package/src/components/BaseTabItem.vue +19 -6
- package/src/components/BaseTabs.stories.js +54 -0
- package/src/components/BaseTabs.vue +3 -3
- package/src/components/BaseTextarea.stories.js +35 -0
- package/src/components/BaseTextarea.vue +1 -1
- package/src/components/BaseTextareaAutoresize.stories.js +49 -0
- package/src/components/BaseTextareaAutoresize.vue +83 -87
- package/src/components/index.ts +46 -0
- package/src/lang/en.json +1 -0
- package/src/lang/fr.json +1 -0
- package/dist/types/src/components/BaseWordCount.vue.d.ts +0 -31
- package/src/components/BaseWordCount.vue +0 -36
|
@@ -41,19 +41,19 @@ export default defineComponent({
|
|
|
41
41
|
type: Number,
|
|
42
42
|
},
|
|
43
43
|
},
|
|
44
|
-
emits: ['model-value
|
|
44
|
+
emits: ['update:model-value'],
|
|
45
45
|
methods: {
|
|
46
46
|
next() {
|
|
47
47
|
if (this.modelValue >= this.lastPage) {
|
|
48
48
|
return;
|
|
49
49
|
}
|
|
50
|
-
this.$emit('model-value
|
|
50
|
+
this.$emit('update:model-value', this.modelValue + 1);
|
|
51
51
|
},
|
|
52
52
|
previous() {
|
|
53
53
|
if (this.modelValue == 1) {
|
|
54
54
|
return;
|
|
55
55
|
}
|
|
56
|
-
this.$emit('model-value
|
|
56
|
+
this.$emit('update:model-value', this.modelValue - 1);
|
|
57
57
|
},
|
|
58
58
|
},
|
|
59
59
|
});
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import BaseContainer from './BaseContainer.vue';
|
|
2
|
+
import BasePanel from './BasePanel.vue';
|
|
3
|
+
import BaseCard from './BaseCard.vue';
|
|
4
|
+
import BaseCardRow from './BaseCardRow.vue';
|
|
5
|
+
import BaseInputLabel from './BaseInputLabel.vue';
|
|
6
|
+
import BaseInput from './BaseInput.vue';
|
|
7
|
+
|
|
8
|
+
export default {
|
|
9
|
+
title: 'Layout/BasePanel',
|
|
10
|
+
component: BasePanel,
|
|
11
|
+
args: {
|
|
12
|
+
title: 'General information',
|
|
13
|
+
description:
|
|
14
|
+
'Enter general user information, name and age. User must be more than 10 years old to participate.',
|
|
15
|
+
},
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
const Template = (args) => ({
|
|
19
|
+
components: {
|
|
20
|
+
BaseContainer,
|
|
21
|
+
BasePanel,
|
|
22
|
+
BaseCard,
|
|
23
|
+
BaseCardRow,
|
|
24
|
+
BaseInputLabel,
|
|
25
|
+
BaseInput,
|
|
26
|
+
},
|
|
27
|
+
setup() {
|
|
28
|
+
return { args };
|
|
29
|
+
},
|
|
30
|
+
template: `
|
|
31
|
+
<div class="bg-slate-100 py-10">
|
|
32
|
+
<BaseContainer>
|
|
33
|
+
<BasePanel v-bind="args">
|
|
34
|
+
<BaseCard>
|
|
35
|
+
<BaseCardRow>
|
|
36
|
+
<div class="mb-4">
|
|
37
|
+
<BaseInputLabel label="Name" />
|
|
38
|
+
<BaseInput name="name" class="w-full" placeholder="Enter you name" />
|
|
39
|
+
</div>
|
|
40
|
+
|
|
41
|
+
<div class="mb-4">
|
|
42
|
+
<BaseInputLabel label="Age" />
|
|
43
|
+
<BaseInput name="age" class="w-full" placeholder="Enter you age" />
|
|
44
|
+
</div>
|
|
45
|
+
|
|
46
|
+
<button class="btn btn-primary mt-2">Submit</button>
|
|
47
|
+
</BaseCardRow>
|
|
48
|
+
</BaseCard>
|
|
49
|
+
</BasePanel>
|
|
50
|
+
</BaseContainer>
|
|
51
|
+
</div>
|
|
52
|
+
`,
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
export const Demo = Template.bind({});
|
|
56
|
+
Demo.args = {};
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import BasePassword from './BasePassword.vue';
|
|
2
|
+
|
|
3
|
+
export default {
|
|
4
|
+
title: 'Form/BasePassword',
|
|
5
|
+
component: BasePassword,
|
|
6
|
+
args: {
|
|
7
|
+
required: true,
|
|
8
|
+
name: 'password',
|
|
9
|
+
},
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
const Template = (args) => ({
|
|
13
|
+
components: {
|
|
14
|
+
BasePassword,
|
|
15
|
+
},
|
|
16
|
+
setup() {
|
|
17
|
+
const value = ref('');
|
|
18
|
+
return { args, value };
|
|
19
|
+
},
|
|
20
|
+
template: `
|
|
21
|
+
<form @submit.prevent="">
|
|
22
|
+
<BasePassword class="border-slate-300" v-model="value" v-bind="args"></BasePassword>
|
|
23
|
+
</form>
|
|
24
|
+
`,
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
export const Demo = Template.bind({});
|
|
28
|
+
Demo.args = {
|
|
29
|
+
placeholder: 'Enter your password',
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
export const Disabled = Template.bind({});
|
|
33
|
+
Disabled.args = {
|
|
34
|
+
disabled: true,
|
|
35
|
+
placeholder: 'Enter your password',
|
|
36
|
+
};
|
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
<template>
|
|
2
|
-
<div
|
|
2
|
+
<div
|
|
3
|
+
class="flex rounded border border-slate-300 bg-white"
|
|
4
|
+
:class="[disabled ? 'cursor-not-allowed text-slate-300' : '']"
|
|
5
|
+
>
|
|
3
6
|
<input
|
|
4
7
|
ref="input"
|
|
5
8
|
:value="modelValue"
|
|
@@ -8,7 +11,7 @@
|
|
|
8
11
|
:disabled="disabled"
|
|
9
12
|
:placeholder="placeholder"
|
|
10
13
|
:required="required"
|
|
11
|
-
class="grow rounded-l border-none focus:ring-2 focus:ring-primary-500"
|
|
14
|
+
class="grow rounded-l rounded-r-none border-none focus:ring-2 focus:ring-primary-500 disabled:cursor-not-allowed"
|
|
12
15
|
:class="inputClass"
|
|
13
16
|
@input="onInput"
|
|
14
17
|
/>
|
|
@@ -16,15 +19,16 @@
|
|
|
16
19
|
<button
|
|
17
20
|
tabindex="-1"
|
|
18
21
|
type="button"
|
|
19
|
-
class="pr-3 text-slate-500"
|
|
22
|
+
class="pr-3 text-slate-500 disabled:cursor-not-allowed disabled:text-slate-300"
|
|
23
|
+
:disabled="disabled"
|
|
20
24
|
@click="showPassword = !showPassword"
|
|
21
25
|
>
|
|
22
26
|
<BaseIcon
|
|
23
27
|
v-if="!showPassword"
|
|
24
|
-
icon="heroicons:eye-
|
|
28
|
+
icon="heroicons:eye-slash-20-solid"
|
|
25
29
|
class="h-5 w-5"
|
|
26
30
|
/>
|
|
27
|
-
<BaseIcon v-else icon="heroicons:eye" class="h-5 w-5" />
|
|
31
|
+
<BaseIcon v-else icon="heroicons:eye-20-solid" class="h-5 w-5" />
|
|
28
32
|
</button>
|
|
29
33
|
</div>
|
|
30
34
|
</div>
|
|
@@ -33,8 +37,10 @@
|
|
|
33
37
|
<script lang="ts">
|
|
34
38
|
import { trim } from 'lodash';
|
|
35
39
|
import { defineComponent, PropType } from 'vue';
|
|
40
|
+
import { Icon as BaseIcon } from '@iconify/vue';
|
|
36
41
|
|
|
37
42
|
export default defineComponent({
|
|
43
|
+
components: { BaseIcon },
|
|
38
44
|
props: {
|
|
39
45
|
modelValue: {
|
|
40
46
|
default: '',
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import BaseProcessRing from './BaseProcessRing.vue';
|
|
2
|
+
|
|
3
|
+
export default {
|
|
4
|
+
title: 'Components/BaseProcessRing',
|
|
5
|
+
component: BaseProcessRing,
|
|
6
|
+
args: {
|
|
7
|
+
class: 'text-primary-500',
|
|
8
|
+
radius: 100,
|
|
9
|
+
stroke: 5,
|
|
10
|
+
progress: 0.6,
|
|
11
|
+
},
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
const Template = (args) => ({
|
|
15
|
+
components: {
|
|
16
|
+
BaseProcessRing,
|
|
17
|
+
},
|
|
18
|
+
setup() {
|
|
19
|
+
return { args };
|
|
20
|
+
},
|
|
21
|
+
template: `
|
|
22
|
+
<BaseProcessRing v-bind="args"></BaseProcessRing>
|
|
23
|
+
`,
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
export const Demo = Template.bind({});
|
|
27
|
+
Demo.args = {};
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import BaseReadMore from './BaseReadMore.vue';
|
|
2
|
+
import BaseContainer from './BaseContainer.vue';
|
|
3
|
+
|
|
4
|
+
export default {
|
|
5
|
+
title: 'Components/BaseReadMore',
|
|
6
|
+
component: BaseReadMore,
|
|
7
|
+
args: {},
|
|
8
|
+
};
|
|
9
|
+
|
|
10
|
+
const Template = (args) => ({
|
|
11
|
+
components: {
|
|
12
|
+
BaseReadMore,
|
|
13
|
+
BaseContainer,
|
|
14
|
+
},
|
|
15
|
+
setup() {
|
|
16
|
+
return { args };
|
|
17
|
+
},
|
|
18
|
+
template: `
|
|
19
|
+
<BaseContainer size="3xl">
|
|
20
|
+
<BaseReadMore v-bind="args">
|
|
21
|
+
<p class="text-slate-600">
|
|
22
|
+
Aute incididunt laborum in sint commodo reprehenderit et aliquip aliqua proident exercitation nostrud sunt. Consectetur laborum elit do non ullamco anim fugiat laboris non aliqua consequat culpa. Veniam aliquip irure et qui dolore eiusmod exercitation elit exercitation ad excepteur reprehenderit enim. Id laboris do eu amet qui Lorem excepteur. Eiusmod eiusmod est qui minim consectetur aliqua occaecat sit tempor nulla. Velit et eiusmod id ut adipisicing occaecat deserunt.Excepteur occaecat nulla deserunt Lorem eu mollit non esse. Velit do proident adipisicing labore ipsum nostrud ipsum pariatur magna. Consequat do reprehenderit eiusmod cupidatat cupidatat cupidatat reprehenderit. Aute fugiat ex fugiat in ut enim dolore est qui. Lorem irure eiusmod eiusmod cillum aute nostrud sint aute enim aute. Aliquip aliqua quis deserunt commodo fugiat incididunt sint qui proident.
|
|
23
|
+
</p>
|
|
24
|
+
</BaseReadMore>
|
|
25
|
+
</BaseContainer>
|
|
26
|
+
`,
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
export const Demo = Template.bind({});
|
|
30
|
+
Demo.args = {};
|
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
class="mt-1 inline appearance-none border-b border-dashed border-slate-400 bg-transparent px-0.5 py-0 text-slate-900 hover:text-slate-600"
|
|
14
14
|
@click="showMore = true"
|
|
15
15
|
>
|
|
16
|
-
<span class="text-base font-
|
|
16
|
+
<span class="text-base font-medium">{{ $t('sui.read_more') }}</span>
|
|
17
17
|
</button>
|
|
18
18
|
</div>
|
|
19
19
|
</template>
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import BaseSelect from './BaseSelect.vue';
|
|
2
|
+
import BaseInputLabel from './BaseInputLabel.vue';
|
|
3
|
+
|
|
4
|
+
export default {
|
|
5
|
+
title: 'Form/BaseSelect',
|
|
6
|
+
component: BaseSelect,
|
|
7
|
+
args: {
|
|
8
|
+
name: 'name',
|
|
9
|
+
class: 'w-full',
|
|
10
|
+
},
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
const Template = (args) => ({
|
|
14
|
+
components: {
|
|
15
|
+
BaseSelect,
|
|
16
|
+
BaseInputLabel,
|
|
17
|
+
},
|
|
18
|
+
setup() {
|
|
19
|
+
function onSubmit() {
|
|
20
|
+
alert('submit');
|
|
21
|
+
}
|
|
22
|
+
// To test, you should try the following scenarios
|
|
23
|
+
// - valid (ex: 'javascript')
|
|
24
|
+
// - invalid (ex: 'test')
|
|
25
|
+
// - empty string ''
|
|
26
|
+
// - null
|
|
27
|
+
// - undefined
|
|
28
|
+
|
|
29
|
+
// Test on Safari AND Chrome
|
|
30
|
+
|
|
31
|
+
// For optional, make sure the default option is selected
|
|
32
|
+
// For required, try to submit, it should be prevented
|
|
33
|
+
const value = ref('test');
|
|
34
|
+
return { args, value, onSubmit };
|
|
35
|
+
},
|
|
36
|
+
template: `
|
|
37
|
+
<form @submit.prevent="onSubmit" class="border-none">
|
|
38
|
+
<BaseInputLabel :required="args.required" label="Choose your favorite programing language" />
|
|
39
|
+
<BaseSelect v-model="value" v-bind="args">
|
|
40
|
+
<option value="javascript">JavaScript</option>
|
|
41
|
+
<option value="typescript">Typescript</option>
|
|
42
|
+
<option value="golang">Golang</option>
|
|
43
|
+
</BaseSelect>
|
|
44
|
+
<button type="submit" class="btn btn-primary mt-5">Submit</button>
|
|
45
|
+
</form>
|
|
46
|
+
<pre class="mt-4 bg-slate-800 font-light text-xs p-3 rounded text-white">{{ {value} }}</pre>
|
|
47
|
+
`,
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
export const Optional = Template.bind({});
|
|
51
|
+
Optional.args = {
|
|
52
|
+
required: false,
|
|
53
|
+
placeholder: 'I dont like programing languages',
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
export const Required = Template.bind({});
|
|
57
|
+
Required.args = {
|
|
58
|
+
required: true,
|
|
59
|
+
placeholder: 'Choose a language',
|
|
60
|
+
};
|
|
61
|
+
|
|
62
|
+
export const Disabled = Template.bind({});
|
|
63
|
+
Disabled.args = {
|
|
64
|
+
required: true,
|
|
65
|
+
placeholder: 'Choose a language',
|
|
66
|
+
disabled: true,
|
|
67
|
+
};
|
|
@@ -1,59 +1,159 @@
|
|
|
1
1
|
<template>
|
|
2
2
|
<select
|
|
3
|
-
|
|
3
|
+
ref="select"
|
|
4
|
+
:value="modelValueInternal"
|
|
4
5
|
:name="name"
|
|
5
6
|
:disabled="disabled"
|
|
6
7
|
:required="required"
|
|
7
|
-
class="rounded"
|
|
8
|
+
class="rounded border border-slate-300 disabled:cursor-not-allowed disabled:text-slate-300 disabled:opacity-100"
|
|
9
|
+
:class="[!modelValue && required ? 'text-slate-400' : '']"
|
|
8
10
|
@change="onChange($event)"
|
|
9
11
|
>
|
|
10
|
-
<
|
|
11
|
-
|
|
12
|
-
|
|
12
|
+
<template v-if="required">
|
|
13
|
+
<option disabled hidden :value="EMPTY_VALUE_INTERNAL">
|
|
14
|
+
{{ placeholder ? placeholder : $t('sui.select_an_option') }}
|
|
15
|
+
</option>
|
|
16
|
+
</template>
|
|
17
|
+
<template v-else>
|
|
18
|
+
<option :value="EMPTY_VALUE_INTERNAL">
|
|
19
|
+
{{ placeholder ? placeholder : $t('sui.select_an_option') }}
|
|
20
|
+
</option>
|
|
21
|
+
</template>
|
|
13
22
|
<slot />
|
|
14
23
|
</select>
|
|
15
24
|
</template>
|
|
16
25
|
|
|
17
|
-
<script lang="ts">
|
|
18
|
-
import {
|
|
19
|
-
import { get } from 'lodash';
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
disabled: {
|
|
36
|
-
default: false,
|
|
37
|
-
type: Boolean,
|
|
38
|
-
},
|
|
39
|
-
required: {
|
|
40
|
-
default: false,
|
|
41
|
-
type: Boolean,
|
|
42
|
-
},
|
|
26
|
+
<script lang="ts" setup>
|
|
27
|
+
import { PropType, Ref } from 'vue';
|
|
28
|
+
import { get, isEqual } from 'lodash';
|
|
29
|
+
import { useMounted } from '@vueuse/core';
|
|
30
|
+
|
|
31
|
+
type Option = string | number | null;
|
|
32
|
+
|
|
33
|
+
const EMPTY_VALUE_INTERNAL = '';
|
|
34
|
+
const EMPTY_VALUE_EXTERNAL = null;
|
|
35
|
+
|
|
36
|
+
const props = defineProps({
|
|
37
|
+
modelValue: {
|
|
38
|
+
default: undefined,
|
|
39
|
+
type: [String, Number, null, undefined] as PropType<Option>,
|
|
40
|
+
},
|
|
41
|
+
name: {
|
|
42
|
+
default: undefined,
|
|
43
|
+
type: String,
|
|
43
44
|
},
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
onChange(event: Event): any {
|
|
48
|
-
if (event === null) {
|
|
49
|
-
this.$emit('update:modelValue', null);
|
|
50
|
-
}
|
|
51
|
-
const value = get(event, 'target.value', null);
|
|
52
|
-
if (value === null) {
|
|
53
|
-
this.$emit('update:modelValue', null);
|
|
54
|
-
}
|
|
55
|
-
this.$emit('update:modelValue', value);
|
|
56
|
-
},
|
|
45
|
+
placeholder: {
|
|
46
|
+
default: '',
|
|
47
|
+
type: String,
|
|
57
48
|
},
|
|
49
|
+
disabled: {
|
|
50
|
+
default: false,
|
|
51
|
+
type: Boolean,
|
|
52
|
+
},
|
|
53
|
+
required: {
|
|
54
|
+
default: false,
|
|
55
|
+
type: Boolean,
|
|
56
|
+
},
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
const mounted = useMounted();
|
|
60
|
+
const select = ref(null) as Ref<HTMLSelectElement | null>;
|
|
61
|
+
|
|
62
|
+
const emit = defineEmits(['update:modelValue']);
|
|
63
|
+
|
|
64
|
+
function isEmptyExternal(value: string | number | null | undefined) {
|
|
65
|
+
if (value === undefined || value === EMPTY_VALUE_EXTERNAL) {
|
|
66
|
+
return true;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
return false;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
function isEmptyInternal(value: string | number | null | undefined) {
|
|
73
|
+
if (value === undefined || value === EMPTY_VALUE_INTERNAL) {
|
|
74
|
+
return true;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
return false;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* Transform to external empty NULL
|
|
82
|
+
* to internal empty ''.
|
|
83
|
+
*/
|
|
84
|
+
const modelValueInternal = computed(() => {
|
|
85
|
+
if (isEmptyExternal(props.modelValue)) {
|
|
86
|
+
return EMPTY_VALUE_INTERNAL;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
if (!validModelValue()) {
|
|
90
|
+
return EMPTY_VALUE_INTERNAL;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
return props.modelValue;
|
|
58
94
|
});
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* Checks if the current modelValue is valid
|
|
98
|
+
*/
|
|
99
|
+
function validModelValue(): boolean {
|
|
100
|
+
if (props.modelValue === EMPTY_VALUE_EXTERNAL) {
|
|
101
|
+
return true;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
const options = [...(select.value?.options ?? [])];
|
|
105
|
+
return options.findIndex((o) => isEqual(o.value, props.modelValue)) != -1;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* This watcher is used to update the parent value
|
|
110
|
+
* if the given modelValue should be empty.
|
|
111
|
+
*/
|
|
112
|
+
watch(
|
|
113
|
+
() => props.modelValue,
|
|
114
|
+
() => validateAndFixModelVale()
|
|
115
|
+
);
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* Re-trigger the same validation on mounted
|
|
119
|
+
*/
|
|
120
|
+
onMounted(() => {
|
|
121
|
+
validateAndFixModelVale();
|
|
122
|
+
});
|
|
123
|
+
|
|
124
|
+
/**
|
|
125
|
+
* Validate the current Model Value. Transform to external empty if :
|
|
126
|
+
* 1. is internal empty
|
|
127
|
+
* 2. is in the current options
|
|
128
|
+
*/
|
|
129
|
+
function validateAndFixModelVale() {
|
|
130
|
+
if (!mounted.value) {
|
|
131
|
+
return;
|
|
132
|
+
}
|
|
133
|
+
if (isEmptyInternal(props.modelValue)) {
|
|
134
|
+
emit('update:modelValue', EMPTY_VALUE_EXTERNAL);
|
|
135
|
+
} else if (!validModelValue()) {
|
|
136
|
+
emit('update:modelValue', EMPTY_VALUE_EXTERNAL);
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
/**
|
|
141
|
+
*
|
|
142
|
+
* Emit change while mutating internal empty value ''
|
|
143
|
+
* to external empty value NULL.
|
|
144
|
+
*/
|
|
145
|
+
function onChange(event: Event) {
|
|
146
|
+
if (event === null) {
|
|
147
|
+
emit('update:modelValue', EMPTY_VALUE_EXTERNAL);
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
const value = get(event, 'target.value', null);
|
|
151
|
+
|
|
152
|
+
if (isEmptyExternal(value)) {
|
|
153
|
+
emit('update:modelValue', EMPTY_VALUE_EXTERNAL);
|
|
154
|
+
return;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
emit('update:modelValue', value);
|
|
158
|
+
}
|
|
59
159
|
</script>
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import BaseSideNavigation from './BaseSideNavigation.vue';
|
|
2
|
+
import BaseSideNavigationItem from './BaseSideNavigationItem.vue';
|
|
3
|
+
import BaseContainer from './BaseContainer.vue';
|
|
4
|
+
import BaseCard from './BaseCard.vue';
|
|
5
|
+
import BaseCardRow from './BaseCardRow.vue';
|
|
6
|
+
|
|
7
|
+
export default {
|
|
8
|
+
title: 'Layout/BaseSideNavigation',
|
|
9
|
+
component: BaseSideNavigation,
|
|
10
|
+
args: {},
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
const Template = (args) => ({
|
|
14
|
+
components: {
|
|
15
|
+
BaseSideNavigation,
|
|
16
|
+
BaseSideNavigationItem,
|
|
17
|
+
BaseContainer,
|
|
18
|
+
BaseCard,
|
|
19
|
+
BaseCardRow,
|
|
20
|
+
},
|
|
21
|
+
setup() {
|
|
22
|
+
return { args };
|
|
23
|
+
},
|
|
24
|
+
template: `
|
|
25
|
+
<div class="bg-slate-100 py-10">
|
|
26
|
+
<BaseContainer>
|
|
27
|
+
<div class="md:flex">
|
|
28
|
+
<div class="shrink-0 mb-6 md:mb-0" style="margin-right: 2rem; width: 200px;">
|
|
29
|
+
<BaseSideNavigation v-bind="args">
|
|
30
|
+
<BaseSideNavigationItem to="/">
|
|
31
|
+
Home
|
|
32
|
+
</BaseSideNavigationItem>
|
|
33
|
+
<BaseSideNavigationItem to="/setup">
|
|
34
|
+
Setup
|
|
35
|
+
</BaseSideNavigationItem>
|
|
36
|
+
<BaseSideNavigationItem to="/settings">
|
|
37
|
+
Settings
|
|
38
|
+
</BaseSideNavigationItem>
|
|
39
|
+
</BaseSideNavigation>
|
|
40
|
+
</div>
|
|
41
|
+
<div class="grow">
|
|
42
|
+
<BaseCard>
|
|
43
|
+
<BaseCardRow>
|
|
44
|
+
{{ $route.path }}
|
|
45
|
+
</BaseCardRow>
|
|
46
|
+
</BaseCard>
|
|
47
|
+
</div>
|
|
48
|
+
</div>
|
|
49
|
+
</BaseContainer>
|
|
50
|
+
</div>
|
|
51
|
+
`,
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
export const Demo = Template.bind({});
|
|
55
|
+
Demo.args = {};
|
|
@@ -1,6 +1,11 @@
|
|
|
1
1
|
<template>
|
|
2
|
-
<nav
|
|
3
|
-
<
|
|
2
|
+
<nav aria-label="Sidebar" class="relative">
|
|
3
|
+
<div class="absolute bottom-0 left-0 h-full w-px bg-slate-300" />
|
|
4
|
+
<div class="relative overflow-x-auto overflow-y-hidden">
|
|
5
|
+
<div class="space-y-2">
|
|
6
|
+
<slot />
|
|
7
|
+
</div>
|
|
8
|
+
</div>
|
|
4
9
|
</nav>
|
|
5
10
|
</template>
|
|
6
11
|
|
|
@@ -3,15 +3,23 @@
|
|
|
3
3
|
<a
|
|
4
4
|
:href="disabled ? undefined : href"
|
|
5
5
|
:disabled="disabled"
|
|
6
|
-
class="flex items-center
|
|
6
|
+
class="group relative flex items-center px-3 py-1"
|
|
7
7
|
:class="[
|
|
8
8
|
isActive
|
|
9
|
-
? '
|
|
10
|
-
: 'text-slate-600 hover:
|
|
9
|
+
? 'font-semibold text-blue-600'
|
|
10
|
+
: 'text-slate-600 hover:text-slate-900',
|
|
11
11
|
disabled ? 'cursor-not-allowed opacity-60' : '',
|
|
12
12
|
]"
|
|
13
13
|
@click.prevent="onClick(navigate)"
|
|
14
14
|
>
|
|
15
|
+
<div
|
|
16
|
+
class="absolute left-0 top-0 h-full"
|
|
17
|
+
:class="[
|
|
18
|
+
isActive
|
|
19
|
+
? 'w-[2px] bg-blue-600'
|
|
20
|
+
: 'group-hover:w-px group-hover:bg-slate-700',
|
|
21
|
+
]"
|
|
22
|
+
></div>
|
|
15
23
|
<slot />
|
|
16
24
|
</a>
|
|
17
25
|
</router-link>
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import BaseSkeleton from './BaseSkeleton.vue';
|
|
2
|
+
import BaseContainer from './BaseContainer.vue';
|
|
3
|
+
|
|
4
|
+
export default {
|
|
5
|
+
title: 'Components/BaseSkeleton',
|
|
6
|
+
component: BaseSkeleton,
|
|
7
|
+
args: {},
|
|
8
|
+
};
|
|
9
|
+
|
|
10
|
+
const Template = (args) => ({
|
|
11
|
+
components: {
|
|
12
|
+
BaseSkeleton,
|
|
13
|
+
BaseContainer,
|
|
14
|
+
},
|
|
15
|
+
setup() {
|
|
16
|
+
return { args };
|
|
17
|
+
},
|
|
18
|
+
template: `
|
|
19
|
+
<BaseContainer size="xl">
|
|
20
|
+
<div class="flex space-x-5">
|
|
21
|
+
<div class="shrink-0">
|
|
22
|
+
<BaseSkeleton class="h-16 rounded-full w-16" v-bind="args"></BaseSkeleton>
|
|
23
|
+
</div>
|
|
24
|
+
<div class="grow">
|
|
25
|
+
<BaseSkeleton class="w-52 h-8 mb-3" v-bind="args"></BaseSkeleton>
|
|
26
|
+
<BaseSkeleton class="w-full h-4 mb-1" v-bind="args"></BaseSkeleton>
|
|
27
|
+
<BaseSkeleton class="w-full h-4 mb-1" v-bind="args"></BaseSkeleton>
|
|
28
|
+
<BaseSkeleton class="w-full h-4 mb-1" v-bind="args"></BaseSkeleton>
|
|
29
|
+
</div>
|
|
30
|
+
</div>
|
|
31
|
+
</BaseContainer>
|
|
32
|
+
`,
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
export const Demo = Template.bind({});
|
|
36
|
+
Demo.args = {};
|