sprintify-ui 0.4.6 → 0.4.8

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.
@@ -0,0 +1,223 @@
1
+ import BaseBelongsToFetch from './BaseBelongsToFetch.vue';
2
+ import ShowValue from '@/../.storybook/components/ShowValue.vue';
3
+ import { options } from '@/../.storybook/utils';
4
+ import { createFieldStory } from '../../.storybook/utils';
5
+
6
+ const sizes = ['xs', 'sm', 'base'];
7
+
8
+ export default {
9
+ title: 'Form/BaseBelongsToFetch',
10
+ component: BaseBelongsToFetch,
11
+ argTypes: {
12
+ size: {
13
+ control: {
14
+ type: 'select',
15
+ options: sizes,
16
+ },
17
+ },
18
+ },
19
+ args: {
20
+ url: 'https://effettandem.com/api/content/articles',
21
+ field: 'title',
22
+ },
23
+ decorators: [() => ({ template: '<div class="mb-36"><story/></div>' })],
24
+ };
25
+
26
+ const Template = (args) => ({
27
+ components: { BaseBelongsToFetch, ShowValue },
28
+ setup() {
29
+ const value = ref(null);
30
+ return { args, value };
31
+ },
32
+ template: `
33
+ <BaseBelongsToFetch v-model="value" v-bind="args"></BaseBelongsToFetch>
34
+ <ShowValue :value="value" />
35
+ `,
36
+ });
37
+
38
+ export const Demo = Template.bind({});
39
+ Demo.args = {};
40
+
41
+ export const AlwaysShowDropdown = Template.bind({});
42
+ AlwaysShowDropdown.args = {
43
+ inline: true,
44
+ dropdownShow: 'always',
45
+ };
46
+
47
+ export const NoFocus = Template.bind({});
48
+ NoFocus.args = {
49
+ visibleFocus: false,
50
+ };
51
+
52
+ export const Inline = Template.bind({});
53
+ Inline.args = {
54
+ inline: true,
55
+ };
56
+
57
+ export const ShowEmptyOption = Template.bind({});
58
+ ShowEmptyOption.args = {
59
+ showEmptyOption: true,
60
+ emptyOptionLabel: 'No Jedi',
61
+ };
62
+
63
+ export const Sizes = (args) => ({
64
+ components: { BaseBelongsToFetch },
65
+ setup() {
66
+ const value = ref(null);
67
+ return { args, sizes, value };
68
+ },
69
+ template: `
70
+ <div v-for="size in sizes" class="mb-1">
71
+ <p class="text-xs text-slate-600 leading-tight">{{ size }}</p>
72
+ <BaseBelongsToFetch v-model="value" v-bind="args" :size="size"></BaseBelongsToFetch>
73
+ </div>
74
+ `,
75
+ });
76
+
77
+ export const Disabled = Template.bind({});
78
+ Disabled.args = {
79
+ currentModel: options[0],
80
+ primaryKey: 'value',
81
+ field: 'label',
82
+ disabled: true,
83
+ };
84
+
85
+ export const SlotOption = (args) => {
86
+ return {
87
+ components: { BaseBelongsToFetch },
88
+ setup() {
89
+ const value = ref(null);
90
+ return { args, value };
91
+ },
92
+ template: `
93
+ <BaseBelongsToFetch
94
+ v-model="value"
95
+ v-bind="args"
96
+ >
97
+ <template #option="{ option, active, selected }">
98
+ <div
99
+ class="rounded px-2 py-1"
100
+ :class="{
101
+ 'hover:bg-slate-100': !active && !selected,
102
+ 'bg-slate-200 hover:bg-slate-300': active && !selected,
103
+ 'bg-blue-500 text-white hover:bg-blue-600': !active && selected,
104
+ 'bg-blue-600 text-white hover:bg-blue-700': active && selected,
105
+ }"
106
+ >
107
+ <p class="text-sm font-medium">{{ option.title }}</p>
108
+ <p class="opacity-60 text-xs">{{ option.owner?.name }}</p>
109
+ </div>
110
+ </template>
111
+ </BaseBelongsToFetch>
112
+ `,
113
+ };
114
+ };
115
+
116
+ export const SlotFooter = (args) => {
117
+ return {
118
+ components: { BaseBelongsToFetch },
119
+ setup() {
120
+ const value = ref(null);
121
+ function onClick() {
122
+ setTimeout(() => {
123
+ alert(1);
124
+ }, 150);
125
+ }
126
+ return { args, value, onClick };
127
+ },
128
+ template: `
129
+ <BaseBelongsToFetch
130
+ v-model="value"
131
+ v-bind="args"
132
+ >
133
+ <template #footer>
134
+ <div class="text-center p-2 border-t">
135
+ <button @click=onClick class="btn btn-sm w-full btn-slate-200-outline">This is the footer 💯</button>
136
+ </div>
137
+ </template>
138
+ </BaseBelongsToFetch>
139
+ `,
140
+ };
141
+ };
142
+
143
+ export const SlotEmpty = (args) => {
144
+ return {
145
+ components: { BaseBelongsToFetch },
146
+ setup() {
147
+ const value = ref(null);
148
+ return { args, value };
149
+ },
150
+ template: `
151
+ <BaseBelongsToFetch
152
+ v-model="value"
153
+ v-bind="args"
154
+ >
155
+ <template #empty="props">
156
+ <div>
157
+ <div v-if="props.firstSearch" class="text-center py-10 p-6">🤓🤓🤓</div>
158
+ <div v-else class="text-center p-6">Start your search... 🔎</div>
159
+ </div>
160
+ </template>
161
+ </BaseBelongsToFetch>
162
+ `,
163
+ };
164
+ };
165
+
166
+ export const WithSelect = (args) => {
167
+ return {
168
+ components: { BaseBelongsToFetch, ShowValue },
169
+ setup() {
170
+ const value = ref(options[0]);
171
+ const selected = ref(null);
172
+
173
+ const select = {
174
+ options: [
175
+ { label: 'All', value: 'all' },
176
+ { label: 'Video', value: 'video' },
177
+ { label: 'Article', value: 'article' },
178
+ ],
179
+ labelKey: 'label',
180
+ valueKey: 'value',
181
+ onChange: (option) => {
182
+ selected.value = option.value;
183
+ },
184
+ };
185
+
186
+ const url = computed(() => {
187
+ if (selected.value == 'all' || !selected.value) {
188
+ return 'https://effettandem.com/api/content/articles';
189
+ }
190
+ return (
191
+ 'https://effettandem.com/api/content/articles' +
192
+ '?type=' +
193
+ selected.value
194
+ );
195
+ });
196
+
197
+ return { args, value, url, selected, select };
198
+ },
199
+ template: `
200
+ <BaseBelongsToFetch
201
+ v-model="value"
202
+ v-bind="args"
203
+ :url="url"
204
+ :select="select"
205
+ >
206
+ </BaseBelongsToFetch>
207
+
208
+ <div class="mb-2"></div>
209
+
210
+ <p class="text-xs mb-0">Selection</p>
211
+ <ShowValue class="mt-0 mb-2" :value="selected" />
212
+
213
+ <p class="text-xs mb-0">Model Value</p>
214
+ <ShowValue class="mt-0" :value="value" />
215
+ `,
216
+ };
217
+ };
218
+
219
+ export const Field = createFieldStory({
220
+ component: BaseBelongsToFetch,
221
+ componentName: 'BaseBelongsToFetch',
222
+ label: 'Article',
223
+ });
@@ -0,0 +1,193 @@
1
+ <template>
2
+ <BaseAutocompleteFetch
3
+ ref="autocompleteFetch"
4
+ :model-value="model"
5
+ :url="url"
6
+ :disabled="disabled"
7
+ :name="name"
8
+ :placeholder="placeholder"
9
+ :required="required"
10
+ :value-key="primaryKey"
11
+ :label-key="field"
12
+ :has-error="hasError"
13
+ :inline="inline"
14
+ :size="size"
15
+ :dropdown-show="dropdownShow"
16
+ :show-model-value="showModelValue"
17
+ :show-empty-option="showEmptyOption"
18
+ :empty-option-label="emptyOptionLabel"
19
+ :visible-focus="visibleFocus"
20
+ :select="select"
21
+ @update:model-value="onUpdate"
22
+ >
23
+ <template #option="optionProps">
24
+ <slot
25
+ name="option"
26
+ v-bind="optionProps"
27
+ />
28
+ </template>
29
+ <template #empty="emptyProps">
30
+ <slot
31
+ name="empty"
32
+ v-bind="emptyProps"
33
+ />
34
+ </template>
35
+ <template #footer="footerProps">
36
+ <slot
37
+ name="footer"
38
+ v-bind="footerProps"
39
+ />
40
+ </template>
41
+ </BaseAutocompleteFetch>
42
+ </template>
43
+
44
+ <script lang="ts" setup>
45
+ import { PropType } from 'vue';
46
+ import { AxiosResponse } from 'axios';
47
+ import { config } from '@/index';
48
+ import BaseAutocompleteFetch from './BaseAutocompleteFetch.vue';
49
+ import { Option, SelectConfiguration } from '@/types';
50
+
51
+ const props = defineProps({
52
+ modelValue: {
53
+ default: undefined,
54
+ type: [String, Number, null] as PropType<
55
+ string | number | null | undefined
56
+ >,
57
+ },
58
+ url: {
59
+ required: true,
60
+ type: String,
61
+ },
62
+ showRouteUrl: {
63
+ default: undefined,
64
+ type: Function as PropType<((id: string | number) => string) | undefined>,
65
+ },
66
+ primaryKey: {
67
+ default: 'id',
68
+ type: String,
69
+ },
70
+ field: {
71
+ required: true,
72
+ type: String,
73
+ },
74
+ required: {
75
+ default: false,
76
+ type: Boolean,
77
+ },
78
+ disabled: {
79
+ default: false,
80
+ type: Boolean,
81
+ },
82
+ name: {
83
+ default: undefined,
84
+ type: String,
85
+ },
86
+ placeholder: {
87
+ default: undefined,
88
+ type: String,
89
+ },
90
+ currentModel: {
91
+ default: null,
92
+ type: [Object, null] as PropType<Option | null>,
93
+ },
94
+ hasError: {
95
+ default: false,
96
+ type: Boolean,
97
+ },
98
+ inline: {
99
+ default: false,
100
+ type: Boolean,
101
+ },
102
+ size: {
103
+ default: 'base',
104
+ type: String as PropType<'xs' | 'sm' | 'base'>,
105
+ },
106
+ dropdownShow: {
107
+ default: 'focus',
108
+ type: String as PropType<'focus' | 'always'>,
109
+ },
110
+ showModelValue: {
111
+ default: true,
112
+ type: Boolean,
113
+ },
114
+ visibleFocus: {
115
+ default: true,
116
+ type: Boolean,
117
+ },
118
+ showEmptyOption: {
119
+ default: false,
120
+ type: Boolean,
121
+ },
122
+ emptyOptionLabel: {
123
+ default: undefined,
124
+ type: String,
125
+ },
126
+ select: {
127
+ default: undefined,
128
+ type: Object as PropType<SelectConfiguration | undefined>,
129
+ },
130
+ });
131
+
132
+ const http = config.http;
133
+
134
+ const emit = defineEmits(['update:modelValue']);
135
+
136
+ const autocompleteFetch = ref<InstanceType<
137
+ typeof BaseAutocompleteFetch
138
+ > | null>(null);
139
+
140
+ const model = ref(props.currentModel);
141
+
142
+ watch(
143
+ () => props.currentModel,
144
+ (newValue, oldValue) => {
145
+ model.value = newValue;
146
+ },
147
+ { deep: true }
148
+ );
149
+
150
+ watch(
151
+ () => props.modelValue,
152
+ (newValue, oldValue) => {
153
+ if (!props.modelValue) {
154
+ model.value = null;
155
+ return;
156
+ }
157
+
158
+ if (newValue == oldValue) {
159
+ return;
160
+ }
161
+
162
+ if (props.showRouteUrl == null) {
163
+ return;
164
+ }
165
+
166
+ http
167
+ .get(props.showRouteUrl(props.modelValue))
168
+ .then((response: AxiosResponse) => {
169
+ model.value = response.data.data;
170
+ })
171
+ .catch((e: Error) => e);
172
+ },
173
+ { immediate: true }
174
+ );
175
+
176
+ function onUpdate(newModel: Option | null) {
177
+ if (!newModel) {
178
+ model.value = null;
179
+ emit('update:modelValue', null);
180
+ } else {
181
+ model.value = newModel;
182
+ emit('update:modelValue', newModel[props.primaryKey]);
183
+ }
184
+ }
185
+
186
+ defineExpose({
187
+ focus: () => autocompleteFetch.value?.focus(),
188
+ blur: () => autocompleteFetch.value?.blur(),
189
+ open: () => autocompleteFetch.value?.open(),
190
+ close: () => autocompleteFetch.value?.close(),
191
+ setKeywords: (input: string) => autocompleteFetch.value?.setKeywords(input),
192
+ });
193
+ </script>
@@ -10,6 +10,7 @@ import BaseAvatar from './BaseAvatar.vue';
10
10
  import BaseAvatarGroup from './BaseAvatarGroup.vue';
11
11
  import BaseBadge from './BaseBadge.vue';
12
12
  import BaseBelongsTo from './BaseBelongsTo.vue';
13
+ import BaseBelongsToFetch from './BaseBelongsToFetch.vue';
13
14
  import BaseBoolean from './BaseBoolean.vue';
14
15
  import BaseBreadcrumbs from './BaseBreadcrumbs.vue';
15
16
  import BaseButton from './BaseButton.vue';
@@ -109,6 +110,7 @@ export {
109
110
  BaseAvatarGroup,
110
111
  BaseBadge,
111
112
  BaseBelongsTo,
113
+ BaseBelongsToFetch,
112
114
  BaseBoolean,
113
115
  BaseBreadcrumbs,
114
116
  BaseButton,