Skip to content

Commit 0132df6

Browse files
Updating templates
1 parent d699cf2 commit 0132df6

File tree

3 files changed

+328
-1
lines changed

3 files changed

+328
-1
lines changed

src/playground/configs/database.json

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -248,7 +248,9 @@
248248
"active": null,
249249
"title": null,
250250
"body": null,
251-
"reviewed": null
251+
"reviewed": null,
252+
"date": "",
253+
"range": 50
252254
},
253255
{
254256
"userId": 8,

src/playground/configs/templates/PlaygroundPage.vue

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -167,6 +167,22 @@
167167
/>
168168
</template>
169169

170+
<template #[`item.range`]="{ item }">
171+
<VInlineCustomField
172+
v-model="item.raw.range"
173+
:loading="item.raw.loading"
174+
@update="updatedValue(item.raw, 'range')"
175+
>
176+
<template #default="">
177+
<v-slider
178+
v-model="item.raw.range"
179+
show-ticks
180+
step="10"
181+
></v-slider>
182+
</template>
183+
</VInlineCustomField>
184+
</template>
185+
170186
<template #[`item.reviewed`]="{ item }">
171187
<VInlineCheckbox
172188
v-model="item.raw.reviewed"
@@ -297,6 +313,13 @@ const headers = [
297313
key: 'body',
298314
title: 'Body',
299315
},
316+
// ? "Range" is used for the VInlineCustomField example
317+
// ? Comment out the body field above to use this one (so it fits) //
318+
// {
319+
// align: 'start',
320+
// key: 'range',
321+
// title: 'Range',
322+
// },
300323
{
301324
align: 'start',
302325
key: 'reviewed',

src/plugin/VInlineCustomField.vue

Lines changed: 302 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,302 @@
1+
<template>
2+
<div
3+
:class="inlineFieldsContainerClass"
4+
:style="inlineFieldsContainerStyle"
5+
>
6+
<div
7+
v-if="!showField && !settings.fieldOnly"
8+
:class="displayContainerClass"
9+
>
10+
<div :class="displayInputControlClasses">
11+
<DisplayedValue
12+
v-bind="bindingDisplay"
13+
@toggleField="toggleField"
14+
/>
15+
</div>
16+
</div>
17+
18+
<div
19+
v-else
20+
class="d-flex align-center py-2"
21+
:class="fieldContainerClass"
22+
>
23+
24+
<slot
25+
name="default"
26+
v-bind="slotBindings"
27+
/>
28+
29+
<SaveFieldButtons
30+
v-model="modelValue"
31+
:cancel-button-color="settings.cancelButtonColor"
32+
:cancel-button-size="settings.cancelButtonSize"
33+
:cancel-button-title="settings.cancelButtonTitle"
34+
:cancel-button-variant="settings.cancelButtonVariant"
35+
:cancel-icon="settings.cancelIcon"
36+
:cancel-icon-color="settings.cancelIconColor"
37+
:error="error"
38+
:field-only="settings.fieldOnly"
39+
:hide-save-icon="settings.hideSaveIcon"
40+
:loading="loadingProp"
41+
:loading-icon="settings.loadingIcon"
42+
:loading-icon-color="settings.loadingIconColor"
43+
:required="settings.required"
44+
:save-button-color="settings.saveButtonColor"
45+
:save-button-size="settings.saveButtonSize"
46+
:save-button-title="settings.saveButtonTitle"
47+
:save-button-variant="settings.saveButtonVariant"
48+
:save-icon="settings.saveIcon"
49+
:save-icon-color="settings.saveIconColor"
50+
@close="closeField"
51+
@save="saveValue"
52+
/>
53+
</div>
54+
</div>
55+
</template>
56+
57+
<script setup lang="ts">
58+
import {
59+
CloseSiblingsBus,
60+
FieldValue,
61+
TimeOpened,
62+
VInlineTextFieldProps,
63+
} from '@/types';
64+
import { IconOptions } from 'vuetify';
65+
import { textFieldProps } from './utils/props';
66+
import { DisplayedValue, SaveFieldButtons } from './components/index';
67+
import {
68+
useCheckForErrors,
69+
useToggleField,
70+
useTruncateText,
71+
} from './composables/methods';
72+
import {
73+
useDisplayContainerClass,
74+
useDisplayInputControlClasses,
75+
useFieldContainerClass,
76+
useInlineFieldsContainerClass,
77+
} from './composables/classes';
78+
import { useInlineFieldsContainerStyle } from './composables/styles';
79+
import inlineEmits from './utils/emits';
80+
81+
82+
const modelValue = defineModel<FieldValue>();
83+
84+
const attrs = useAttrs();
85+
const emit = defineEmits([...inlineEmits]);
86+
87+
const iconOptions = inject<IconOptions>(Symbol.for('vuetify:icons'));
88+
89+
const props = withDefaults(defineProps<VInlineTextFieldProps>(), { ...textFieldProps });
90+
let settings = reactive({ ...attrs, ...props });
91+
const loadingProp = computed(() => props.loading);
92+
93+
const empty = ref<boolean>(false);
94+
const error = ref<boolean>(false);
95+
const showField = ref<boolean>(false);
96+
const timeOpened = ref<TimeOpened>(null);
97+
let originalValue = modelValue.value;
98+
99+
100+
// ------------------------------------------------ Loading //
101+
watch(() => loadingProp.value, (newVal, oldVal) => {
102+
if (!newVal && oldVal && showField.value) {
103+
toggleField();
104+
}
105+
});
106+
107+
108+
// ------------------------------------------------ The displayed value //
109+
const displayValue = computed(() => {
110+
if (modelValue.value) {
111+
empty.value = false;
112+
113+
if (settings.truncateLength) {
114+
return useTruncateText({
115+
length: settings.truncateLength,
116+
suffix: settings.truncateSuffix,
117+
text: modelValue.value as string,
118+
});
119+
}
120+
121+
return modelValue.value;
122+
}
123+
124+
empty.value = true;
125+
return settings.emptyText;
126+
});
127+
128+
129+
// ------------------------------------------------ Binding Events & Props //
130+
const slotBindings = computed(() => ({
131+
...settings,
132+
...{
133+
loading: loadingProp.value,
134+
originalValue: originalValue,
135+
}
136+
}));
137+
138+
const bindingDisplay = computed(() => {
139+
return {
140+
color: settings.color,
141+
displayAppendIcon: props.displayAppendIcon,
142+
displayAppendIconColor: props.displayAppendIconColor,
143+
displayAppendIconSize: props.displayAppendIconSize,
144+
displayAppendInnerIcon: props.displayAppendInnerIcon,
145+
displayAppendInnerIconColor: props.displayAppendInnerIconColor,
146+
displayAppendInnerIconSize: props.displayAppendInnerIconSize,
147+
displayPrependIcon: props.displayPrependIcon,
148+
displayPrependIconColor: props.displayPrependIconColor,
149+
displayPrependIconSize: props.displayPrependIconSize,
150+
displayPrependInnerIcon: props.displayPrependInnerIcon,
151+
displayPrependInnerIconColor: props.displayPrependInnerIconColor,
152+
displayPrependInnerIconSize: props.displayPrependInnerIconSize,
153+
displayValue: displayValue.value,
154+
empty,
155+
error,
156+
field: 'v-text-field',
157+
underlineColor: settings.underlineColor,
158+
underlineStyle: settings.underlineStyle,
159+
underlineWidth: settings.underlineWidth,
160+
underlined: settings.underlined,
161+
valueColor: settings.valueColor,
162+
};
163+
});
164+
165+
166+
// ------------------------------------------------ Class & Styles //
167+
const inlineFieldsContainerClass = computed(() => useInlineFieldsContainerClass({
168+
density: settings.density,
169+
disabled: settings.disabled,
170+
field: 'v-text-field',
171+
iconSet: iconOptions?.defaultSet,
172+
loading: loadingProp.value,
173+
loadingWait: settings.loadingWait,
174+
tableField: settings.tableField,
175+
variant: settings.variant,
176+
}));
177+
178+
const displayContainerClass = computed(() => useDisplayContainerClass({
179+
density: settings.density,
180+
field: 'v-text-field',
181+
}));
182+
183+
const displayInputControlClasses = useDisplayInputControlClasses({
184+
density: settings.density,
185+
variant: settings.variant,
186+
});
187+
188+
const fieldContainerClass = computed(() => useFieldContainerClass({
189+
active: showField.value,
190+
name: 'text-field',
191+
}));
192+
193+
const inlineFieldsContainerStyle = computed(() => useInlineFieldsContainerStyle());
194+
195+
196+
// ------------------------------------------------ Key event to cancel/close field //
197+
function closeField() {
198+
error.value = false;
199+
modelValue.value = originalValue;
200+
toggleField();
201+
}
202+
203+
204+
// ------------------------------------------------ Toggle the field //
205+
function toggleField() {
206+
if (settings.disabled || (settings.loadingWait && loadingProp.value)) {
207+
return;
208+
}
209+
210+
const response = useToggleField({
211+
attrs,
212+
closeSiblings: settings.closeSiblings,
213+
fieldOnly: settings.fieldOnly,
214+
props,
215+
showField,
216+
timeOpened: timeOpened.value,
217+
});
218+
219+
settings = { ...settings, ...response.settings };
220+
showField.value = response.showField;
221+
timeOpened.value = response.timeOpened;
222+
223+
if (closeSiblingsBus !== null && settings.closeSiblings && showField.value && !settings.fieldOnly) {
224+
closeSiblingsBus.emit(response.timeOpened);
225+
}
226+
}
227+
228+
229+
// ------------------------------------------------ Check for errors //
230+
const internalErrors = ref();
231+
232+
watch(() => showField.value, () => {
233+
if (showField.value) {
234+
checkInternalErrors();
235+
}
236+
});
237+
238+
watch(() => modelValue.value, () => {
239+
if (showField.value) {
240+
checkInternalErrors();
241+
}
242+
});
243+
244+
function checkInternalErrors() {
245+
const response = useCheckForErrors({
246+
required: settings.required,
247+
rules: settings.rules,
248+
value: modelValue,
249+
});
250+
251+
error.value = response.errors;
252+
253+
internalErrors.value = response.results;
254+
return response.results;
255+
}
256+
257+
258+
// ------------------------------------------------ Save the value / Emit update //
259+
function saveValue() {
260+
if (error.value) {
261+
error.value = true;
262+
return;
263+
}
264+
265+
originalValue = modelValue.value;
266+
emit('update', modelValue.value);
267+
268+
if (!settings.loadingWait) {
269+
toggleField();
270+
}
271+
}
272+
273+
274+
// ------------------------------------------------ Close siblings bus event //
275+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
276+
let closeSiblingsBus: unknown | any;
277+
let unsubscribeBus: () => void;
278+
279+
if (settings.closeSiblings) {
280+
import('@vueuse/core').then(({ useEventBus }) => {
281+
closeSiblingsBus = useEventBus(CloseSiblingsBus);
282+
unsubscribeBus = closeSiblingsBus.on(closeSiblingsListener);
283+
});
284+
}
285+
286+
function closeSiblingsListener(identifier: TimeOpened) {
287+
emit('update:closeSiblingFields', timeOpened);
288+
289+
if (showField.value && timeOpened.value !== identifier) {
290+
closeField();
291+
}
292+
}
293+
294+
onUnmounted(() => {
295+
if (typeof unsubscribeBus !== 'undefined') {
296+
closeSiblingsBus.off(closeSiblingsListener);
297+
}
298+
});
299+
</script>
300+
301+
<style lang="scss" scoped>
302+
</style>

0 commit comments

Comments
 (0)