@rotki/ui-library 2.23.4 → 2.24.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/dist/components/date-time-picker/RuiDateTimePicker.js.map +1 -1
- package/dist/components/date-time-picker/RuiDateTimePicker.vue.d.ts +16 -0
- package/dist/components/date-time-picker/RuiDateTimePicker.vue_vue_type_script_setup_true_lang.js +20 -3
- package/dist/components/date-time-picker/RuiDateTimePicker.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/date-time-picker/partial-time.d.ts +35 -0
- package/dist/components/date-time-picker/partial-time.js +71 -0
- package/dist/components/date-time-picker/partial-time.js.map +1 -0
- package/dist/components/date-time-picker/use-date-bounds.d.ts +27 -0
- package/dist/components/date-time-picker/use-date-bounds.js +56 -0
- package/dist/components/date-time-picker/use-date-bounds.js.map +1 -0
- package/dist/components/date-time-picker/use-date-time-selection.d.ts +4 -0
- package/dist/components/date-time-picker/use-date-time-selection.js +39 -38
- package/dist/components/date-time-picker/use-date-time-selection.js.map +1 -1
- package/dist/components/forms/auto-complete/RuiAutoComplete.js.map +1 -1
- package/dist/components/forms/auto-complete/RuiAutoComplete.vue_vue_type_script_setup_true_lang.js +2 -1
- package/dist/components/forms/auto-complete/RuiAutoComplete.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/tables/data-table/RuiDataTableCell.js.map +1 -1
- package/dist/components/tables/data-table/RuiDataTableCell.vue_vue_type_script_setup_true_lang.js +4 -3
- package/dist/components/tables/data-table/RuiDataTableCell.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/tables/data-table/RuiDataTableRow.js.map +1 -1
- package/dist/components/tables/data-table/RuiDataTableRow.vue_vue_type_script_setup_true_lang.js +6 -6
- package/dist/components/tables/data-table/RuiDataTableRow.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/composables/forms/auto-complete/value.d.ts +2 -0
- package/dist/composables/forms/auto-complete/value.js +16 -12
- package/dist/composables/forms/auto-complete/value.js.map +1 -1
- package/dist/web-types.json +10 -1
- package/package.json +1 -1
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import { useRuiI8n } from "../../composables/use-rui-i18n.js";
|
|
2
|
-
import { RUI_I18N_KEYS } from "../../i18n/keys.js";
|
|
3
1
|
import "./dayjs-setup.js";
|
|
4
2
|
import { formatWallClock, guessTimezone, includeMilliseconds, includeSeconds } from "./utils.js";
|
|
5
|
-
import { buildDateTime, clampToBounds
|
|
3
|
+
import { buildDateTime, clampToBounds } from "./segment-utils.js";
|
|
4
|
+
import { completePartialEntry } from "./partial-time.js";
|
|
5
|
+
import { useDateBounds } from "./use-date-bounds.js";
|
|
6
6
|
import { computed, nextTick, onMounted, ref, watch } from "vue";
|
|
7
7
|
import { isDefined, watchIgnorable } from "@vueuse/core";
|
|
8
8
|
import { get as get$1, set as set$1 } from "@vueuse/shared";
|
|
@@ -10,8 +10,7 @@ import dayjs from "dayjs";
|
|
|
10
10
|
//#region src/components/date-time-picker/use-date-time-selection.ts
|
|
11
11
|
var MILLISECONDS = 1e3;
|
|
12
12
|
function useDateTimeSelection(options) {
|
|
13
|
-
const { accuracy, allowEmpty, dateFormat, maxDate, minDate, modelValue, type } = options;
|
|
14
|
-
const { t } = useRuiI8n();
|
|
13
|
+
const { accuracy, allowEmpty, dateFormat, maxDate, minDate, modelValue, partialTime, type } = options;
|
|
15
14
|
const selectedYear = ref();
|
|
16
15
|
const selectedMonth = ref();
|
|
17
16
|
const selectedDay = ref();
|
|
@@ -20,11 +19,14 @@ function useDateTimeSelection(options) {
|
|
|
20
19
|
const selectedSecond = ref();
|
|
21
20
|
const selectedMillisecond = ref();
|
|
22
21
|
const selectedTimezone = ref(guessTimezone());
|
|
23
|
-
const internalErrorMessages = ref([]);
|
|
24
22
|
const now = ref(dayjs.tz(void 0, guessTimezone()));
|
|
25
|
-
const
|
|
26
|
-
|
|
27
|
-
|
|
23
|
+
const { internalErrorMessages, isDateValid, maxAllowedDate, minAllowedDate } = useDateBounds({
|
|
24
|
+
dateFormat,
|
|
25
|
+
epochSeconds: type === "epoch",
|
|
26
|
+
maxDate,
|
|
27
|
+
minDate,
|
|
28
|
+
now
|
|
29
|
+
});
|
|
28
30
|
const segmentData = {
|
|
29
31
|
DD: selectedDay,
|
|
30
32
|
HH: selectedHour,
|
|
@@ -76,35 +78,6 @@ function useDateTimeSelection(options) {
|
|
|
76
78
|
year: get$1(selectedYear)
|
|
77
79
|
}, accuracy);
|
|
78
80
|
}
|
|
79
|
-
/**
|
|
80
|
-
* Writes a bound the way the field writes its value. `toLocaleDateString()`
|
|
81
|
-
* followed the browser locale and ignored the picker's own format, so a
|
|
82
|
-
* day-first field could report its limit month-first, and it dropped the time
|
|
83
|
-
* entirely, which left a mid-day bound unable to explain itself.
|
|
84
|
-
*/
|
|
85
|
-
function formatBound(bound) {
|
|
86
|
-
return dayjs(bound).format(get$1(dateFormat));
|
|
87
|
-
}
|
|
88
|
-
function isDateValid(date) {
|
|
89
|
-
const min = get$1(minAllowedDate);
|
|
90
|
-
const max = get$1(maxAllowedDate);
|
|
91
|
-
set$1(internalErrorMessages, []);
|
|
92
|
-
if (min && date.isBefore(min)) {
|
|
93
|
-
const formatted = formatBound(min);
|
|
94
|
-
const errorMessage = t(RUI_I18N_KEYS.dateTimePicker.dateBeforeMin, { date: formatted }, `Date cannot be before ${formatted}`);
|
|
95
|
-
set$1(internalErrorMessages, [...get$1(internalErrorMessages), errorMessage]);
|
|
96
|
-
return false;
|
|
97
|
-
}
|
|
98
|
-
if (max && date.isAfter(max)) {
|
|
99
|
-
const formatted = formatBound(max);
|
|
100
|
-
const nowError = t(RUI_I18N_KEYS.dateTimePicker.dateInFuture, "The selected date cannot be in the future");
|
|
101
|
-
const maxError = t(RUI_I18N_KEYS.dateTimePicker.dateAfterMax, { date: formatted }, `Date cannot be after ${formatted}`);
|
|
102
|
-
const errorMessage = maxDate === "now" ? nowError : maxError;
|
|
103
|
-
set$1(internalErrorMessages, [...get$1(internalErrorMessages), errorMessage]);
|
|
104
|
-
return false;
|
|
105
|
-
}
|
|
106
|
-
return true;
|
|
107
|
-
}
|
|
108
81
|
function emitUpdate(updatedModel) {
|
|
109
82
|
set$1(modelValue, {
|
|
110
83
|
"date": () => updatedModel.toDate(),
|
|
@@ -197,6 +170,33 @@ function useDateTimeSelection(options) {
|
|
|
197
170
|
set$1(now, dayjs());
|
|
198
171
|
updateModelValue();
|
|
199
172
|
});
|
|
173
|
+
/**
|
|
174
|
+
* Fills in the segments an incomplete entry never reached and commits it, so
|
|
175
|
+
* a bare date can become a value. Called when the user is done with the field
|
|
176
|
+
* rather than on every keystroke, since a segment still being typed is not
|
|
177
|
+
* yet one they left out. The fill is written back into the field, so the
|
|
178
|
+
* value it decided on is the one on screen.
|
|
179
|
+
*/
|
|
180
|
+
function commitPartialTime() {
|
|
181
|
+
if (partialTime === void 0) return;
|
|
182
|
+
const target = completePartialEntry({
|
|
183
|
+
day: get$1(selectedDay),
|
|
184
|
+
hour: get$1(selectedHour),
|
|
185
|
+
millisecond: get$1(selectedMillisecond),
|
|
186
|
+
minute: get$1(selectedMinute),
|
|
187
|
+
month: get$1(selectedMonth),
|
|
188
|
+
second: get$1(selectedSecond),
|
|
189
|
+
year: get$1(selectedYear)
|
|
190
|
+
}, {
|
|
191
|
+
accuracy,
|
|
192
|
+
maxDate: get$1(maxAllowedDate),
|
|
193
|
+
minDate: get$1(minAllowedDate),
|
|
194
|
+
mode: partialTime
|
|
195
|
+
});
|
|
196
|
+
if (!target) return;
|
|
197
|
+
ignoreUpdates(() => applySegments(target));
|
|
198
|
+
updateModelValue();
|
|
199
|
+
}
|
|
200
200
|
function updateInternalModel(value) {
|
|
201
201
|
ignoreUpdates(() => {
|
|
202
202
|
const updatedValue = type === "epoch" && typeof value === "number" ? value * MILLISECONDS : value;
|
|
@@ -222,6 +222,7 @@ function useDateTimeSelection(options) {
|
|
|
222
222
|
});
|
|
223
223
|
return {
|
|
224
224
|
clear,
|
|
225
|
+
commitPartialTime,
|
|
225
226
|
getDateTime,
|
|
226
227
|
internalErrorMessages,
|
|
227
228
|
isDateValid,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"use-date-time-selection.js","names":[],"sources":["../../../src/components/date-time-picker/use-date-time-selection.ts"],"sourcesContent":["import type { ComputedRef, Ref, WritableComputedRef } from 'vue';\nimport type { SegmentData } from '@/components/date-time-picker/types';\nimport type { TimeAccuracy } from '@/consts/time-accuracy';\nimport dayjs, { type Dayjs } from 'dayjs';\nimport { buildDateTime, clampToBounds, resolveBound } from '@/components/date-time-picker/segment-utils';\nimport { formatWallClock, guessTimezone, includeMilliseconds, includeSeconds } from '@/components/date-time-picker/utils';\nimport { useRuiI8n } from '@/composables/use-rui-i18n';\nimport { RUI_I18N_KEYS } from '@/i18n/keys';\nimport '@/components/date-time-picker/dayjs-setup';\n\ntype DateTimeModelType = 'date' | 'epoch-ms' | 'epoch';\n\ntype ModelValueType<T extends DateTimeModelType> =\n T extends 'date' ? Date | undefined :\n T extends 'epoch-ms' ? number | undefined :\n T extends 'epoch' ? number | undefined :\n Date | number | undefined;\n\ninterface DateTimeSelectionOptions<T extends DateTimeModelType> {\n modelValue: Ref<ModelValueType<T>>;\n type: T;\n accuracy: TimeAccuracy;\n minDate: Date | number | undefined;\n maxDate: Date | number | 'now' | undefined;\n allowEmpty: boolean;\n /**\n * The field's own format, so a bound named in an error message is written the\n * same way round as the value the user is looking at.\n */\n dateFormat: Ref<string>;\n}\n\ninterface DateTimeSelectionReturn {\n selectedYear: Ref<number | undefined>;\n selectedMonth: Ref<number | undefined>;\n selectedDay: Ref<number | undefined>;\n selectedHour: Ref<number | undefined>;\n selectedMinute: Ref<number | undefined>;\n selectedSecond: Ref<number | undefined>;\n selectedMillisecond: Ref<number | undefined>;\n selectedTimezone: Ref<string | undefined>;\n selectedDate: WritableComputedRef<Date | undefined>;\n selectedTime: WritableComputedRef<Date | undefined>;\n valueSet: ComputedRef<boolean>;\n internalErrorMessages: Ref<string[]>;\n now: Ref<Dayjs>;\n segmentData: SegmentData;\n minAllowedDate: ComputedRef<Date>;\n maxAllowedDate: ComputedRef<Date | undefined>;\n getDateTime: () => Dayjs;\n setNow: () => void;\n setToday: () => void;\n clear: () => void;\n isDateValid: (date: Dayjs) => boolean;\n}\n\nconst MILLISECONDS = 1000;\n\nexport function useDateTimeSelection<T extends DateTimeModelType>(\n options: DateTimeSelectionOptions<T>,\n): DateTimeSelectionReturn {\n const {\n accuracy,\n allowEmpty,\n dateFormat,\n maxDate,\n minDate,\n modelValue,\n type,\n } = options;\n\n const { t } = useRuiI8n();\n\n const selectedYear = ref<number | undefined>();\n const selectedMonth = ref<number | undefined>();\n const selectedDay = ref<number | undefined>();\n\n const selectedHour = ref<number | undefined>();\n const selectedMinute = ref<number | undefined>();\n const selectedSecond = ref<number | undefined>();\n const selectedMillisecond = ref<number | undefined>();\n const selectedTimezone = ref<string | undefined>(guessTimezone());\n\n const internalErrorMessages = ref<string[]>([]);\n const now = ref<Dayjs>(dayjs.tz(undefined, guessTimezone()));\n\n const epochSeconds = type === 'epoch';\n\n const minAllowedDate = computed<Date>(\n () => resolveBound(minDate, epochSeconds) ?? new Date(1970, 0, 1),\n );\n\n const maxAllowedDate = computed<Date | undefined>(\n () => (maxDate === 'now' ? get(now).toDate() : resolveBound(maxDate, epochSeconds)),\n );\n\n const segmentData: SegmentData = {\n DD: selectedDay,\n HH: selectedHour,\n MM: selectedMonth,\n SSS: selectedMillisecond,\n YYYY: selectedYear,\n mm: selectedMinute,\n ss: selectedSecond,\n };\n\n const selectedDate = computed<Date | undefined>({\n get() {\n if (!(isDefined(selectedYear) && isDefined(selectedMonth) && isDefined(selectedDay))) {\n return undefined;\n }\n const date = new Date();\n date.setFullYear(get(selectedYear));\n // Set day to 1 first to prevent month overflow when today's day > days in target month\n // e.g., if today is Dec 30 and we set month to Feb, day 30 would overflow to March\n date.setDate(1);\n date.setMonth(get(selectedMonth) - 1);\n date.setDate(get(selectedDay));\n return date;\n },\n set(value?: Date) {\n set(selectedYear, value?.getFullYear());\n set(selectedMonth, value ? value.getMonth() + 1 : undefined);\n set(selectedDay, value?.getDate());\n },\n });\n\n const selectedTime = computed<Date | undefined>({\n get() {\n if (!(isDefined(selectedHour) && isDefined(selectedMinute))) {\n return undefined;\n }\n const date = new Date();\n date.setHours(\n get(selectedHour),\n get(selectedMinute),\n get(selectedSecond) ?? 0,\n get(selectedMillisecond) ?? 0,\n );\n return date;\n },\n set(value?: Date) {\n set(selectedHour, value?.getHours());\n set(selectedMinute, value?.getMinutes());\n set(selectedSecond, value?.getSeconds());\n set(selectedMillisecond, value?.getMilliseconds());\n },\n });\n\n const valueSet = computed<boolean>(() => isDefined(selectedDate) && isDefined(selectedTime));\n\n function getDateTime(): Dayjs {\n return buildDateTime({\n day: get(selectedDay),\n hour: get(selectedHour),\n millisecond: get(selectedMillisecond),\n minute: get(selectedMinute),\n month: get(selectedMonth),\n second: get(selectedSecond),\n year: get(selectedYear),\n }, accuracy);\n }\n\n /**\n * Writes a bound the way the field writes its value. `toLocaleDateString()`\n * followed the browser locale and ignored the picker's own format, so a\n * day-first field could report its limit month-first, and it dropped the time\n * entirely, which left a mid-day bound unable to explain itself.\n */\n function formatBound(bound: Date): string {\n return dayjs(bound).format(get(dateFormat));\n }\n\n function isDateValid(date: Dayjs): boolean {\n const min = get(minAllowedDate);\n const max = get(maxAllowedDate);\n\n set(internalErrorMessages, []);\n\n if (min && date.isBefore(min)) {\n const formatted = formatBound(min);\n const errorMessage = t(RUI_I18N_KEYS.dateTimePicker.dateBeforeMin, {\n date: formatted,\n }, `Date cannot be before ${formatted}`);\n set(internalErrorMessages, [...get(internalErrorMessages), errorMessage]);\n return false;\n }\n\n if (max && date.isAfter(max)) {\n const formatted = formatBound(max);\n const nowError = t(RUI_I18N_KEYS.dateTimePicker.dateInFuture, 'The selected date cannot be in the future');\n const maxError = t(RUI_I18N_KEYS.dateTimePicker.dateAfterMax, { date: formatted }, `Date cannot be after ${formatted}`);\n const errorMessage = maxDate === 'now' ? nowError : maxError;\n set(internalErrorMessages, [...get(internalErrorMessages), errorMessage]);\n return false;\n }\n\n return true;\n }\n\n function emitUpdate(updatedModel: Dayjs): void {\n const typeMap = {\n 'date': () => updatedModel.toDate(),\n // an epoch is whole seconds; `millisecond` accuracy would otherwise emit a fraction\n 'epoch': () => Math.floor(updatedModel.valueOf() / MILLISECONDS),\n 'epoch-ms': () => updatedModel.valueOf(),\n } as const;\n\n set(modelValue, typeMap[type]() as ModelValueType<T>);\n }\n\n function updateModelValue(): void {\n if (!isDefined(selectedDate) || !isDefined(selectedTime)) {\n return;\n }\n\n // The segments are a wall-clock reading, so they are formatted and parsed\n // in the selected timezone. Mutating a `dayjs.tz()` built from the old\n // value instead would keep that value's UTC offset, and moving the date\n // across a DST boundary then shifted the time by an hour.\n const updatedModel = dayjs.tz(\n formatWallClock(get(selectedDate), get(selectedTime), accuracy),\n get(selectedTimezone),\n );\n\n if (!isDateValid(updatedModel)) {\n return;\n }\n\n emitUpdate(updatedModel);\n }\n\n function clear(): void {\n set(internalErrorMessages, []);\n set(selectedYear, undefined);\n set(selectedMonth, undefined);\n set(selectedDay, undefined);\n set(selectedHour, undefined);\n set(selectedMinute, undefined);\n set(selectedSecond, undefined);\n set(selectedMillisecond, undefined);\n set(modelValue, undefined as ModelValueType<T>);\n }\n\n function clampToAllowed(date: Dayjs): Dayjs {\n return clampToBounds(date, get(minAllowedDate), get(maxAllowedDate));\n }\n\n function applySegments(date: Dayjs): void {\n set(selectedYear, date.year());\n set(selectedMonth, date.month() + 1);\n set(selectedDay, date.date());\n set(selectedHour, date.hour());\n set(selectedMinute, date.minute());\n set(selectedSecond, includeSeconds(accuracy) ? date.second() : 0);\n set(selectedMillisecond, includeMilliseconds(accuracy) ? date.millisecond() : 0);\n }\n\n function setNow(): void {\n set(internalErrorMessages, []);\n\n const date = dayjs();\n set(now, date);\n applySegments(clampToAllowed(date));\n\n nextTick(() => {\n updateModelValue();\n });\n }\n\n /**\n * Moves the date part to today and leaves the time part alone, so a picked\n * time survives. Falls back to midnight when no time has been entered yet.\n */\n function setToday(): void {\n set(internalErrorMessages, []);\n\n const date = dayjs();\n set(now, date);\n\n const target = buildDateTime({\n day: date.date(),\n hour: get(selectedHour) ?? 0,\n millisecond: get(selectedMillisecond) ?? 0,\n minute: get(selectedMinute) ?? 0,\n month: date.month() + 1,\n second: get(selectedSecond) ?? 0,\n year: date.year(),\n }, accuracy, date);\n\n applySegments(clampToAllowed(target));\n\n nextTick(() => {\n updateModelValue();\n });\n }\n\n function updateSegments(date: Dayjs): void {\n const year = date.year();\n const month = date.month() + 1;\n const day = date.date();\n const hour = date.hour();\n const minute = date.minute();\n const second = includeSeconds(accuracy) ? date.second() : undefined;\n const millisecond = includeMilliseconds(accuracy) ? date.millisecond() : undefined;\n\n if (get(selectedYear) !== year)\n set(selectedYear, year);\n if (get(selectedMonth) !== month)\n set(selectedMonth, month);\n if (get(selectedDay) !== day)\n set(selectedDay, day);\n if (get(selectedHour) !== hour)\n set(selectedHour, hour);\n if (get(selectedMinute) !== minute)\n set(selectedMinute, minute);\n if (get(selectedSecond) !== second)\n set(selectedSecond, second);\n if (get(selectedMillisecond) !== millisecond)\n set(selectedMillisecond, millisecond);\n }\n\n const { ignoreUpdates } = watchIgnorable([selectedDate, selectedTime], ([newSelectedDate, newSelectedTime], [prevSelectedDate, prevSelectedTime]) => {\n const currentTimezone = get(selectedTimezone);\n const newDate = dayjs.tz(newSelectedDate, currentTimezone);\n const oldDate = dayjs.tz(prevSelectedDate, currentTimezone);\n const newTime = dayjs.tz(newSelectedTime, currentTimezone);\n const oldTime = dayjs.tz(prevSelectedTime, currentTimezone);\n\n if (newDate.isSame(oldDate) && newTime.isSame(oldTime)) {\n return;\n }\n\n set(now, dayjs());\n updateModelValue();\n });\n\n function updateInternalModel(value: ModelValueType<T>): void {\n ignoreUpdates(() => {\n const updatedValue = type === 'epoch' && typeof value === 'number'\n ? value * MILLISECONDS\n : value;\n const date = dayjs.tz(updatedValue, get(selectedTimezone));\n isDateValid(date);\n updateSegments(date);\n });\n }\n\n watch(selectedTimezone, (newTimezone: string | undefined) => {\n if (newTimezone && isDefined(selectedDate) && isDefined(selectedTime)) {\n set(now, dayjs());\n updateModelValue();\n }\n });\n\n watch(modelValue, (value) => {\n set(now, dayjs());\n if (value === undefined) {\n clear();\n }\n else {\n updateInternalModel(value);\n }\n });\n\n onMounted(() => {\n if (isDefined(modelValue)) {\n updateInternalModel(get(modelValue));\n }\n else if (!allowEmpty) {\n setNow();\n }\n });\n\n return {\n clear,\n getDateTime,\n internalErrorMessages,\n isDateValid,\n maxAllowedDate,\n minAllowedDate,\n now,\n segmentData,\n selectedDate,\n selectedDay,\n selectedHour,\n selectedMillisecond,\n selectedMinute,\n selectedMonth,\n selectedSecond,\n selectedTime,\n selectedTimezone,\n selectedYear,\n setNow,\n setToday,\n valueSet,\n };\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"use-date-time-selection.js","names":[],"sources":["../../../src/components/date-time-picker/use-date-time-selection.ts"],"sourcesContent":["import type { ComputedRef, Ref, WritableComputedRef } from 'vue';\nimport type { SegmentData } from '@/components/date-time-picker/types';\nimport type { TimeAccuracy } from '@/consts/time-accuracy';\nimport dayjs, { type Dayjs } from 'dayjs';\nimport { completePartialEntry, type PartialTimeMode } from '@/components/date-time-picker/partial-time';\nimport { buildDateTime, clampToBounds } from '@/components/date-time-picker/segment-utils';\nimport { useDateBounds } from '@/components/date-time-picker/use-date-bounds';\nimport { formatWallClock, guessTimezone, includeMilliseconds, includeSeconds } from '@/components/date-time-picker/utils';\nimport '@/components/date-time-picker/dayjs-setup';\n\ntype DateTimeModelType = 'date' | 'epoch-ms' | 'epoch';\n\ntype ModelValueType<T extends DateTimeModelType> =\n T extends 'date' ? Date | undefined :\n T extends 'epoch-ms' ? number | undefined :\n T extends 'epoch' ? number | undefined :\n Date | number | undefined;\n\ninterface DateTimeSelectionOptions<T extends DateTimeModelType> {\n modelValue: Ref<ModelValueType<T>>;\n type: T;\n accuracy: TimeAccuracy;\n minDate: Date | number | undefined;\n maxDate: Date | number | 'now' | undefined;\n allowEmpty: boolean;\n /**\n * The field's own format, so a bound named in an error message is written the\n * same way round as the value the user is looking at.\n */\n dateFormat: Ref<string>;\n /** See {@link completePartialEntry}; unset, an incomplete entry is not a value. */\n partialTime?: PartialTimeMode;\n}\n\ninterface DateTimeSelectionReturn {\n selectedYear: Ref<number | undefined>;\n selectedMonth: Ref<number | undefined>;\n selectedDay: Ref<number | undefined>;\n selectedHour: Ref<number | undefined>;\n selectedMinute: Ref<number | undefined>;\n selectedSecond: Ref<number | undefined>;\n selectedMillisecond: Ref<number | undefined>;\n selectedTimezone: Ref<string | undefined>;\n selectedDate: WritableComputedRef<Date | undefined>;\n selectedTime: WritableComputedRef<Date | undefined>;\n valueSet: ComputedRef<boolean>;\n internalErrorMessages: Ref<string[]>;\n now: Ref<Dayjs>;\n segmentData: SegmentData;\n minAllowedDate: ComputedRef<Date>;\n maxAllowedDate: ComputedRef<Date | undefined>;\n getDateTime: () => Dayjs;\n setNow: () => void;\n setToday: () => void;\n commitPartialTime: () => void;\n clear: () => void;\n isDateValid: (date: Dayjs) => boolean;\n}\n\nconst MILLISECONDS = 1000;\n\nexport function useDateTimeSelection<T extends DateTimeModelType>(\n options: DateTimeSelectionOptions<T>,\n): DateTimeSelectionReturn {\n const {\n accuracy,\n allowEmpty,\n dateFormat,\n maxDate,\n minDate,\n modelValue,\n partialTime,\n type,\n } = options;\n\n const selectedYear = ref<number | undefined>();\n const selectedMonth = ref<number | undefined>();\n const selectedDay = ref<number | undefined>();\n\n const selectedHour = ref<number | undefined>();\n const selectedMinute = ref<number | undefined>();\n const selectedSecond = ref<number | undefined>();\n const selectedMillisecond = ref<number | undefined>();\n const selectedTimezone = ref<string | undefined>(guessTimezone());\n\n const now = ref<Dayjs>(dayjs.tz(undefined, guessTimezone()));\n\n const { internalErrorMessages, isDateValid, maxAllowedDate, minAllowedDate } = useDateBounds({\n dateFormat,\n epochSeconds: type === 'epoch',\n maxDate,\n minDate,\n now,\n });\n\n const segmentData: SegmentData = {\n DD: selectedDay,\n HH: selectedHour,\n MM: selectedMonth,\n SSS: selectedMillisecond,\n YYYY: selectedYear,\n mm: selectedMinute,\n ss: selectedSecond,\n };\n\n const selectedDate = computed<Date | undefined>({\n get() {\n if (!(isDefined(selectedYear) && isDefined(selectedMonth) && isDefined(selectedDay))) {\n return undefined;\n }\n const date = new Date();\n date.setFullYear(get(selectedYear));\n // Set day to 1 first to prevent month overflow when today's day > days in target month\n // e.g., if today is Dec 30 and we set month to Feb, day 30 would overflow to March\n date.setDate(1);\n date.setMonth(get(selectedMonth) - 1);\n date.setDate(get(selectedDay));\n return date;\n },\n set(value?: Date) {\n set(selectedYear, value?.getFullYear());\n set(selectedMonth, value ? value.getMonth() + 1 : undefined);\n set(selectedDay, value?.getDate());\n },\n });\n\n const selectedTime = computed<Date | undefined>({\n get() {\n if (!(isDefined(selectedHour) && isDefined(selectedMinute))) {\n return undefined;\n }\n const date = new Date();\n date.setHours(\n get(selectedHour),\n get(selectedMinute),\n get(selectedSecond) ?? 0,\n get(selectedMillisecond) ?? 0,\n );\n return date;\n },\n set(value?: Date) {\n set(selectedHour, value?.getHours());\n set(selectedMinute, value?.getMinutes());\n set(selectedSecond, value?.getSeconds());\n set(selectedMillisecond, value?.getMilliseconds());\n },\n });\n\n const valueSet = computed<boolean>(() => isDefined(selectedDate) && isDefined(selectedTime));\n\n function getDateTime(): Dayjs {\n return buildDateTime({\n day: get(selectedDay),\n hour: get(selectedHour),\n millisecond: get(selectedMillisecond),\n minute: get(selectedMinute),\n month: get(selectedMonth),\n second: get(selectedSecond),\n year: get(selectedYear),\n }, accuracy);\n }\n\n function emitUpdate(updatedModel: Dayjs): void {\n const typeMap = {\n 'date': () => updatedModel.toDate(),\n // an epoch is whole seconds; `millisecond` accuracy would otherwise emit a fraction\n 'epoch': () => Math.floor(updatedModel.valueOf() / MILLISECONDS),\n 'epoch-ms': () => updatedModel.valueOf(),\n } as const;\n\n set(modelValue, typeMap[type]() as ModelValueType<T>);\n }\n\n function updateModelValue(): void {\n if (!isDefined(selectedDate) || !isDefined(selectedTime)) {\n return;\n }\n\n // The segments are a wall-clock reading, so they are formatted and parsed\n // in the selected timezone. Mutating a `dayjs.tz()` built from the old\n // value instead would keep that value's UTC offset, and moving the date\n // across a DST boundary then shifted the time by an hour.\n const updatedModel = dayjs.tz(\n formatWallClock(get(selectedDate), get(selectedTime), accuracy),\n get(selectedTimezone),\n );\n\n if (!isDateValid(updatedModel)) {\n return;\n }\n\n emitUpdate(updatedModel);\n }\n\n function clear(): void {\n set(internalErrorMessages, []);\n set(selectedYear, undefined);\n set(selectedMonth, undefined);\n set(selectedDay, undefined);\n set(selectedHour, undefined);\n set(selectedMinute, undefined);\n set(selectedSecond, undefined);\n set(selectedMillisecond, undefined);\n set(modelValue, undefined as ModelValueType<T>);\n }\n\n function clampToAllowed(date: Dayjs): Dayjs {\n return clampToBounds(date, get(minAllowedDate), get(maxAllowedDate));\n }\n\n function applySegments(date: Dayjs): void {\n set(selectedYear, date.year());\n set(selectedMonth, date.month() + 1);\n set(selectedDay, date.date());\n set(selectedHour, date.hour());\n set(selectedMinute, date.minute());\n set(selectedSecond, includeSeconds(accuracy) ? date.second() : 0);\n set(selectedMillisecond, includeMilliseconds(accuracy) ? date.millisecond() : 0);\n }\n\n function setNow(): void {\n set(internalErrorMessages, []);\n\n const date = dayjs();\n set(now, date);\n applySegments(clampToAllowed(date));\n\n nextTick(() => {\n updateModelValue();\n });\n }\n\n /**\n * Moves the date part to today and leaves the time part alone, so a picked\n * time survives. Falls back to midnight when no time has been entered yet.\n */\n function setToday(): void {\n set(internalErrorMessages, []);\n\n const date = dayjs();\n set(now, date);\n\n const target = buildDateTime({\n day: date.date(),\n hour: get(selectedHour) ?? 0,\n millisecond: get(selectedMillisecond) ?? 0,\n minute: get(selectedMinute) ?? 0,\n month: date.month() + 1,\n second: get(selectedSecond) ?? 0,\n year: date.year(),\n }, accuracy, date);\n\n applySegments(clampToAllowed(target));\n\n nextTick(() => {\n updateModelValue();\n });\n }\n\n function updateSegments(date: Dayjs): void {\n const year = date.year();\n const month = date.month() + 1;\n const day = date.date();\n const hour = date.hour();\n const minute = date.minute();\n const second = includeSeconds(accuracy) ? date.second() : undefined;\n const millisecond = includeMilliseconds(accuracy) ? date.millisecond() : undefined;\n\n if (get(selectedYear) !== year)\n set(selectedYear, year);\n if (get(selectedMonth) !== month)\n set(selectedMonth, month);\n if (get(selectedDay) !== day)\n set(selectedDay, day);\n if (get(selectedHour) !== hour)\n set(selectedHour, hour);\n if (get(selectedMinute) !== minute)\n set(selectedMinute, minute);\n if (get(selectedSecond) !== second)\n set(selectedSecond, second);\n if (get(selectedMillisecond) !== millisecond)\n set(selectedMillisecond, millisecond);\n }\n\n const { ignoreUpdates } = watchIgnorable([selectedDate, selectedTime], ([newSelectedDate, newSelectedTime], [prevSelectedDate, prevSelectedTime]) => {\n const currentTimezone = get(selectedTimezone);\n const newDate = dayjs.tz(newSelectedDate, currentTimezone);\n const oldDate = dayjs.tz(prevSelectedDate, currentTimezone);\n const newTime = dayjs.tz(newSelectedTime, currentTimezone);\n const oldTime = dayjs.tz(prevSelectedTime, currentTimezone);\n\n if (newDate.isSame(oldDate) && newTime.isSame(oldTime)) {\n return;\n }\n\n set(now, dayjs());\n updateModelValue();\n });\n\n /**\n * Fills in the segments an incomplete entry never reached and commits it, so\n * a bare date can become a value. Called when the user is done with the field\n * rather than on every keystroke, since a segment still being typed is not\n * yet one they left out. The fill is written back into the field, so the\n * value it decided on is the one on screen.\n */\n function commitPartialTime(): void {\n if (partialTime === undefined)\n return;\n\n const target = completePartialEntry({\n day: get(selectedDay),\n hour: get(selectedHour),\n millisecond: get(selectedMillisecond),\n minute: get(selectedMinute),\n month: get(selectedMonth),\n second: get(selectedSecond),\n year: get(selectedYear),\n }, {\n accuracy,\n maxDate: get(maxAllowedDate),\n minDate: get(minAllowedDate),\n mode: partialTime,\n });\n\n if (!target)\n return;\n\n // The model is written here rather than left to the watcher: this runs on blur and on enter,\n // and a consumer that closes the field on that key would drop a value the watcher only emits\n // on the next tick.\n ignoreUpdates(() => applySegments(target));\n updateModelValue();\n }\n\n function updateInternalModel(value: ModelValueType<T>): void {\n ignoreUpdates(() => {\n const updatedValue = type === 'epoch' && typeof value === 'number'\n ? value * MILLISECONDS\n : value;\n const date = dayjs.tz(updatedValue, get(selectedTimezone));\n isDateValid(date);\n updateSegments(date);\n });\n }\n\n watch(selectedTimezone, (newTimezone: string | undefined) => {\n if (newTimezone && isDefined(selectedDate) && isDefined(selectedTime)) {\n set(now, dayjs());\n updateModelValue();\n }\n });\n\n watch(modelValue, (value) => {\n set(now, dayjs());\n if (value === undefined) {\n clear();\n }\n else {\n updateInternalModel(value);\n }\n });\n\n onMounted(() => {\n if (isDefined(modelValue)) {\n updateInternalModel(get(modelValue));\n }\n else if (!allowEmpty) {\n setNow();\n }\n });\n\n return {\n clear,\n commitPartialTime,\n getDateTime,\n internalErrorMessages,\n isDateValid,\n maxAllowedDate,\n minAllowedDate,\n now,\n segmentData,\n selectedDate,\n selectedDay,\n selectedHour,\n selectedMillisecond,\n selectedMinute,\n selectedMonth,\n selectedSecond,\n selectedTime,\n selectedTimezone,\n selectedYear,\n setNow,\n setToday,\n valueSet,\n };\n}\n"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"RuiAutoComplete.js","names":[],"sources":["../../../../src/components/forms/auto-complete/RuiAutoComplete.vue"],"sourcesContent":["<script lang=\"ts\" setup generic=\"TValue, TItem\">\nimport type { VueClassValue } from '@/types/class-value';\nimport RuiButton from '@/components/buttons/button/RuiButton.vue';\nimport RuiChip from '@/components/chips/RuiChip.vue';\nimport { autoCompleteStyles, type AutoCompleteVariant } from '@/components/forms/auto-complete/auto-complete-styles';\nimport RuiIcon from '@/components/icons/RuiIcon.vue';\nimport RuiMenu, { type MenuProps } from '@/components/overlays/menu/RuiMenu.vue';\nimport RuiProgress from '@/components/progress/RuiProgress.vue';\nimport {\n type GroupBy,\n type ItemDisabled,\n type KeyOfType,\n useDropdownMenu,\n useDropdownOptionProperty,\n} from '@/composables/dropdown-menu';\nimport { type FloatingOptions, Placement } from '@/composables/floating';\nimport {\n useAutoCompleteFocus,\n useAutoCompleteKeyboardNavigation,\n useAutoCompleteSearch,\n useAutoCompleteValue,\n} from '@/composables/forms/auto-complete';\nimport { useFormTextDetail } from '@/utils/form-text-detail';\nimport { getNonRootAttrs, getRootAttrs, getTextToken } from '@/utils/helpers';\nimport { isEqual } from '@/utils/is-equal';\nimport { cn } from '@/utils/tv';\n\nexport type AutoCompleteModelValue<TValue> =\n TValue extends Array<infer U> ? U[] : TValue | undefined;\n\nexport interface RuiAutoCompleteClassNames {\n root?: VueClassValue;\n label?: VueClassValue;\n menu?: VueClassValue;\n}\n\nexport interface AutoCompleteProps<TValue, TItem> {\n options?: TItem[];\n keyAttr?: KeyOfType<TItem, TValue extends Array<infer U> ? U : TValue>;\n textAttr?: keyof TItem;\n disabled?: boolean;\n loading?: boolean;\n readOnly?: boolean;\n dense?: boolean;\n clearable?: boolean;\n label?: string;\n menuOptions?: MenuProps;\n classNames?: RuiAutoCompleteClassNames;\n /** @deprecated Use `classNames.label` instead */\n labelClass?: string;\n /** @deprecated Use `classNames.menu` instead */\n menuClass?: string;\n prependWidth?: number;\n appendWidth?: number;\n itemHeight?: number;\n variant?: AutoCompleteVariant;\n hint?: string;\n errorMessages?: string | string[];\n successMessages?: string | string[];\n hideDetails?: boolean;\n autoSelectFirst?: boolean;\n chips?: boolean;\n noFilter?: boolean;\n hideNoData?: boolean;\n noDataText?: string;\n /**\n * Custom search predicate. Receives the resolved group label as a third\n * argument when `groupBy` is set, so callers don't have to re-run the\n * `groupBy` resolver themselves.\n */\n filter?: (item: TItem, queryText: string, group?: string) => boolean;\n hideSelected?: boolean;\n placeholder?: string;\n returnObject?: boolean;\n customValue?: boolean;\n hideCustomValue?: boolean;\n required?: boolean;\n hideSearchInput?: boolean;\n hideSelectionWrapper?: boolean;\n groupBy?: GroupBy<TItem>;\n itemDisabled?: ItemDisabled<TItem>;\n /**\n * When true and `groupBy` is set, the default search predicate also matches\n * against the resolved group label. Items belonging to a group whose label\n * matches the query stay visible (group header included), even when the\n * items themselves don't match. No effect when a custom `filter` is supplied\n * or when `groupBy` is undefined.\n */\n searchIncludesGroupLabel?: boolean;\n}\n\ndefineOptions({\n name: 'RuiAutoComplete',\n inheritAttrs: false,\n});\n\nconst modelValue = defineModel<AutoCompleteModelValue<TValue>>({ required: true });\n\nconst searchInputModel = defineModel<string>('searchInput', { default: '' });\n\nconst {\n options = [],\n disabled = false,\n loading = false,\n readOnly = false,\n dense = false,\n clearable = false,\n hideDetails = false,\n chips = false,\n label = 'Select',\n menuOptions,\n classNames,\n labelClass,\n menuClass,\n variant = 'default',\n hint,\n keyAttr,\n textAttr,\n itemHeight,\n errorMessages = [],\n successMessages = [],\n autoSelectFirst = false,\n noFilter = false,\n hideNoData = false,\n noDataText = 'No data available',\n filter,\n hideSelected = false,\n placeholder = '',\n returnObject = false,\n customValue = false,\n hideCustomValue = false,\n required = false,\n hideSearchInput = false,\n hideSelectionWrapper = false,\n groupBy,\n itemDisabled,\n searchIncludesGroupLabel = false,\n} = defineProps<AutoCompleteProps<TValue, TItem>>();\n\nconst slots = defineSlots<{\n 'activator'?: (props: {\n disabled: boolean;\n value: TItem[];\n variant: string;\n readOnly: boolean;\n attrs: Record<string, unknown>;\n open: boolean;\n hasError: boolean;\n hasSuccess: boolean;\n }) => any;\n 'activator.label'?: (props: { value: TItem[] }) => any;\n 'selection.prepend'?: (props: { index: number; item: TItem }) => any;\n 'selection'?: (props: { index: number; item: TItem; chipAttrs: Record<string, unknown> }) => any;\n 'item.prepend'?: (props: { disabled: boolean; item: TItem; active: boolean }) => any;\n 'item'?: (props: { disabled: boolean; item: TItem; active: boolean }) => any;\n 'item.append'?: (props: { disabled: boolean; item: TItem; active: boolean }) => any;\n 'group-header'?: (props: { group: string; items: TItem[] }) => any;\n 'no-data'?: () => any;\n 'placeholder'?: (props: { disabled: boolean; readOnly: boolean }) => any;\n 'footer'?: () => any;\n}>();\n\nconst { getText, getIdentifier } = useDropdownOptionProperty<TValue, TItem>({\n keyAttr,\n textAttr,\n});\n\nconst textInput = useTemplateRef<HTMLInputElement>('textInput');\nconst activator = useTemplateRef<HTMLDivElement>('activator');\nconst menuRef = useTemplateRef<HTMLDivElement>('menuRef');\nconst menuWrapperRef = useTemplateRef<HTMLDivElement>('menuWrapperRef');\n\nconst { focused: activatorFocusedWithin } = useFocusWithin(activator);\nconst { focused: menuWrapperFocusedWithin } = useFocusWithin(menuWrapperRef);\nconst { focused: searchInputFocused } = useFocus(textInput);\nconst { focused: activatorFocused } = useFocus(activator);\n\nconst {\n internalSearch,\n filteredOptions,\n justOpened,\n updateInternalSearch,\n textValueToProperValue,\n} = useAutoCompleteSearch<TItem>(\n () => options,\n searchInputModel,\n {\n keyAttr: () => keyAttr,\n textAttr: () => textAttr,\n noFilter: () => noFilter,\n filter: () => filter,\n customValue: () => customValue,\n hideCustomValue: () => hideCustomValue,\n returnObject: () => returnObject,\n groupBy: () => groupBy,\n searchIncludesGroupLabel: () => searchIncludesGroupLabel,\n },\n);\n\nconst isOpen = ref<boolean>(false);\nconst isHovered = ref<boolean>(false);\n\n// Calculate multiple from modelValue directly to avoid circular dependency\nconst multiple = computed<boolean>(() => Array.isArray(get(modelValue)));\n\nconst shouldApplyValueAsSearch = computed<boolean>(\n () => !(slots.selection || get(multiple) || chips),\n);\n\nconst { value, setSelected } = useAutoCompleteValue<AutoCompleteModelValue<TValue>, TItem>(\n modelValue,\n () => options,\n {\n keyAttr: () => keyAttr,\n returnObject: () => returnObject,\n customValue: () => customValue,\n },\n {\n getIdentifier,\n getText,\n textValueToProperValue,\n shouldApplyValueAsSearch,\n isOpen,\n multiple,\n updateInternalSearch,\n },\n);\n\nconst resolvedItemHeight = itemHeight ?? (dense ? 30 : 48);\n\nconst {\n containerProps,\n wrapperProps,\n renderedData,\n menuWidth,\n isActiveItem,\n itemIndexInValue,\n highlightedIndex,\n moveHighlight,\n applyHighlighted,\n optionsWithSelectedHidden,\n userNavigated,\n groupedOptions,\n isGrouped,\n isItemDisabled,\n} = useDropdownMenu<TValue, TItem>({\n itemHeight: resolvedItemHeight,\n keyAttr,\n textAttr,\n options: filteredOptions,\n dense: () => dense,\n value,\n menuRef,\n setValue,\n autoSelectFirst,\n hideSelected,\n isOpen,\n getText,\n getIdentifier,\n groupBy: () => groupBy,\n itemDisabled: () => itemDisabled,\n});\n\nconst {\n focusedValueIndex,\n moveSelectedValueHighlight,\n onEnter,\n onInputDeletePressed,\n onTab,\n setValueFocus,\n} = useAutoCompleteKeyboardNavigation<TItem>(\n {\n chips: () => chips,\n customValue: () => customValue,\n multiple,\n },\n {\n activator,\n applyHighlighted,\n clear,\n filteredOptions,\n getText,\n highlightedIndex,\n internalSearch,\n isOpen,\n removeValue: (item: TItem): void => { setValue(item); },\n searchInputFocused,\n setSearchAsValue,\n userNavigated,\n value,\n },\n);\n\nconst {\n anyFocused: focusAnyFocused,\n inputClass: focusInputClass,\n onActivatorFocused: focusOnActivatorFocused,\n onInputFocused: focusOnInputFocused,\n setInputFocus: focusSetInputFocus,\n} = useAutoCompleteFocus(\n {\n customValue: () => customValue,\n disabled: () => disabled,\n shouldApplyValueAsSearch,\n },\n {\n activatorFocused,\n activatorFocusedWithin,\n focusedValueIndex,\n internalSearch,\n isOpen,\n justOpened,\n menuWrapperFocusedWithin,\n searchInputFocused,\n setSearchAsValue,\n textInput,\n updateInternalSearch,\n },\n);\n\nconst menuMinHeight = computed<number>(\n () => Math.min(5, get(optionsWithSelectedHidden).length) * resolvedItemHeight,\n);\n\nconst { hasError, hasSuccess } = useFormTextDetail(\n () => errorMessages,\n () => successMessages,\n);\n\nconst valueSet = computed<boolean>(() => get(value).length > 0);\n\n// True when the consumer's #placeholder slot is taking over the resting\n// content area, so the outlined/resting label must hide to avoid overlapping\n// the slot content.\nconst placeholderSlotActive = computed<boolean>(() => Boolean(slots.placeholder) && !get(valueSet) && !get(searchInputFocused));\n\nconst usedPlaceholder = computed<string>(() => {\n if (get(searchInputFocused))\n return placeholder;\n return '';\n});\n\nconst outlined = computed<boolean>(() => variant === 'outlined');\nconst float = computed<boolean>(() => (get(isOpen) || get(valueSet) || get(searchInputFocused)) && get(outlined));\n\nconst legendText = computed<string>(() => {\n if (!get(float) || !label)\n return '';\n return required ? `${label} ﹡` : label;\n});\n\nconst ui = computed<ReturnType<typeof autoCompleteStyles>>(() => autoCompleteStyles({\n filled: variant === 'filled',\n outlined: get(outlined),\n float: get(float),\n opened: get(isOpen),\n hovered: get(isHovered),\n dense,\n disabled,\n readonly: readOnly,\n hasError: get(hasError),\n hasSuccess: get(hasSuccess) && !get(hasError),\n}));\n\nconst highlightedClass = autoCompleteStyles({}).highlighted();\n\nfunction updateSearchInput(event: Event): void {\n const target = event.target;\n if (!(target instanceof HTMLInputElement))\n return;\n\n const value = target.value;\n set(isOpen, true);\n updateInternalSearch(value);\n set(justOpened, false);\n}\n\nasync function setValue(val: TItem, skipRefocused = false): Promise<void> {\n const isMultiple = get(multiple);\n\n if (isMultiple) {\n const newValue = [...get(value)];\n const indexInValue = itemIndexInValue(val);\n if (indexInValue === -1) {\n updateInternalSearch();\n newValue.push(val);\n }\n else {\n newValue.splice(indexInValue, 1);\n }\n set(value, newValue);\n }\n else {\n if (get(shouldApplyValueAsSearch))\n updateInternalSearch(getText(val));\n else updateInternalSearch();\n\n set(value, [val]);\n }\n\n if (!isMultiple) {\n if (!skipRefocused) {\n set(activatorFocused, true);\n get(activator)?.focus();\n await nextTick(() => {\n set(isOpen, false);\n });\n }\n else {\n set(isOpen, false);\n }\n }\n else if (!skipRefocused) {\n set(searchInputFocused, true);\n }\n}\n\nfunction setSearchAsValue(): void {\n const searchToBeValue = get(internalSearch);\n if (!searchToBeValue)\n return;\n\n const newValue: TItem = textValueToProperValue(searchToBeValue);\n setValue(newValue, true);\n}\n\nfunction clear(): void {\n updateInternalSearch();\n set(modelValue, (Array.isArray(get(modelValue)) ? [] : undefined) as AutoCompleteModelValue<TValue>);\n}\n\nfunction chipAttrs(item: TItem, index: number): Record<string, unknown> {\n return {\n 'data-index': index,\n 'data-value': getIdentifier(item),\n 'onKeydown': (event: KeyboardEvent): void => {\n const { key } = event;\n if (['Backspace', 'Delete'].includes(key)) {\n event.stopPropagation();\n event.preventDefault();\n // Alt/Option + delete converts the chip back into editable search text\n // instead of removing it outright. Only meaningful when custom values\n // are accepted, since the restored text has to be re-selectable.\n if (event.altKey && customValue) {\n const text = getText(item) ?? '';\n setValue(item);\n updateInternalSearch(text);\n }\n else {\n setValue(item);\n }\n }\n },\n 'onClick': (e: MouseEvent): void => {\n e.stopPropagation();\n setValueFocus(index);\n },\n 'onClick:close': (): void => {\n setValue(item);\n },\n };\n}\n\nfunction setSelectionRange(start: number, end: number): void {\n set(searchInputFocused, true);\n get(textInput)?.setSelectionRange?.(start, end);\n}\n\nfunction arrowClicked(event: MouseEvent): void {\n if (get(isOpen)) {\n set(isOpen, false);\n event.stopPropagation();\n }\n}\n\nfunction openMenu(): void {\n set(isOpen, true);\n}\n\nfunction closeMenu(): void {\n set(isOpen, false);\n}\n\n// Optimize options watcher with shallow comparison first\nwatch(() => options, (curr, old) => {\n if (curr === old || customValue)\n return;\n\n // Only do deep comparison if reference changed\n if (isEqual(curr, old))\n return;\n\n setSelected(get(value));\n});\n\nconst menuFloatingOptions = computed<FloatingOptions>(() => ({\n placement: Placement.bottomStart,\n ...menuOptions?.options,\n}));\n\ndefineExpose({\n closeMenu,\n focus: focusSetInputFocus,\n openMenu,\n setSelectionRange,\n});\n</script>\n\n<template>\n <RuiMenu\n v-model=\"isOpen\"\n v-bind=\"{ ...getRootAttrs($attrs, []), ...menuOptions }\"\n :class=\"ui.wrapper({ class: cn($attrs.class) })\"\n :options=\"menuFloatingOptions\"\n :close-on-content-click=\"false\"\n :full-width=\"true\"\n :persist-on-activator-click=\"true\"\n :menu-class=\"[\n { hidden: optionsWithSelectedHidden.length === 0 && customValue && !slots['no-data'] },\n menuOptions?.menuClass,\n ]\"\n :error-messages=\"errorMessages\"\n :success-messages=\"successMessages\"\n :hint=\"hint\"\n :dense=\"dense\"\n :show-details=\"!hideDetails\"\n :disabled=\"disabled\"\n disable-auto-focus\n >\n <template #activator=\"{ attrs, open, hasError: slotHasError, hasSuccess: slotHasSuccess }\">\n <slot\n name=\"activator\"\n v-bind=\"{ disabled, value, variant, readOnly, attrs, open, hasError: slotHasError, hasSuccess: slotHasSuccess }\"\n >\n <div\n ref=\"activator\"\n :class=\"ui.activator({ class: cn(classNames?.label) ?? labelClass })\"\n v-bind=\"{\n ...getNonRootAttrs($attrs, ['onClick', 'class']),\n ...(readOnly ? {} : attrs),\n }\"\n role=\"combobox\"\n :aria-expanded=\"open\"\n :aria-disabled=\"disabled || undefined\"\n :aria-readonly=\"readOnly || undefined\"\n :aria-required=\"required || undefined\"\n :aria-busy=\"loading || undefined\"\n data-id=\"activator\"\n :aria-invalid=\"hasError\"\n :tabindex=\"disabled || readOnly ? -1 : 0\"\n @mouseenter=\"isHovered = true\"\n @mouseleave=\"isHovered = false\"\n @click=\"focusSetInputFocus()\"\n @focus=\"focusOnActivatorFocused()\"\n @keydown.enter=\"onEnter($event)\"\n @keydown.tab=\"onTab($event)\"\n @keydown.left=\"moveSelectedValueHighlight($event, false)\"\n @keydown.right=\"moveSelectedValueHighlight($event, true)\"\n @keydown.up.prevent=\"moveHighlight(true)\"\n @keydown.down.prevent=\"moveHighlight(false)\"\n @keydown.home.prevent=\"highlightedIndex = 0\"\n @keydown.end.prevent=\"highlightedIndex = optionsWithSelectedHidden.length - 1\"\n >\n <span\n v-if=\"(outlined || (!valueSet && !searchInputFocused)) && !placeholderSlotActive\"\n :class=\"[\n ui.label(),\n { 'pr-2': !valueSet && !open && outlined },\n ]\"\n >\n <slot\n name=\"activator.label\"\n v-bind=\"{ value }\"\n >\n {{ label }}\n </slot>\n <span\n v-if=\"required\"\n :class=\"ui.required()\"\n >\n ﹡\n </span>\n </span>\n <div\n data-id=\"value\"\n :class=\"ui.value()\"\n >\n <!--\n Placeholder slot occupies the same flex space the input takes\n when focused, so transitioning into the focused state doesn't\n shift the activator's content. pointer-events-none is required\n so clicks pass through to the activator (otherwise the slot\n swallows the first click and the menu needs two taps to open).\n -->\n <div\n v-if=\"!valueSet && !searchInputFocused && slots.placeholder\"\n data-id=\"placeholder\"\n class=\"flex-1 min-w-0 pointer-events-none\"\n >\n <slot\n name=\"placeholder\"\n v-bind=\"{ disabled, readOnly }\"\n />\n </div>\n <template\n v-for=\"(item, i) in value\"\n :key=\"getIdentifier(item)\"\n >\n <RuiChip\n v-if=\"chips\"\n :key=\"getTextToken(getIdentifier(item))\"\n tabindex=\"-1\"\n :size=\"dense ? 'sm' : 'md'\"\n closeable\n :class=\"{ 'leading-3': dense }\"\n clickable\n v-bind=\"chipAttrs(item, i)\"\n >\n <div class=\"flex\">\n <slot\n name=\"selection.prepend\"\n :index=\"i\"\n v-bind=\"{ item }\"\n />\n <slot\n :index=\"i\"\n name=\"selection\"\n v-bind=\"{ item, chipAttrs: chipAttrs(item, i) }\"\n >\n {{ getText(item) }}\n </slot>\n </div>\n </RuiChip>\n <div\n v-else-if=\"\n multiple\n || (!searchInputFocused && (slots['selection.prepend'] || slots.selection))\n \"\n :class=\"hideSelectionWrapper ? 'contents' : 'flex'\"\n >\n <slot\n name=\"selection.prepend\"\n :index=\"i\"\n v-bind=\"{ item }\"\n />\n <slot\n v-if=\"multiple || slots.selection\"\n :index=\"i\"\n name=\"selection\"\n v-bind=\"{ item, chipAttrs: chipAttrs(item, i) }\"\n >\n {{ getText(item) }}\n </slot>\n </div>\n </template>\n <input\n ref=\"textInput\"\n :disabled=\"disabled\"\n :value=\"internalSearch\"\n class=\"bg-transparent outline-none\"\n type=\"text\"\n :placeholder=\"usedPlaceholder\"\n :class=\"[focusInputClass, { hidden: hideSearchInput }]\"\n :aria-invalid=\"hasError\"\n aria-autocomplete=\"list\"\n @keydown.delete=\"onInputDeletePressed()\"\n @input.stop=\"updateSearchInput($event)\"\n @focus=\"focusOnInputFocused()\"\n />\n </div>\n\n <RuiButton\n v-if=\"clearable && valueSet && !disabled\"\n variant=\"text\"\n icon\n size=\"sm\"\n tabindex=\"-1\"\n color=\"error\"\n data-id=\"clear\"\n :class=\"[\n ui.clear(),\n focusAnyFocused && '!visible',\n { 'mr-2': !dense },\n ]\"\n @click.stop.prevent=\"clear()\"\n >\n <RuiIcon\n name=\"lu-x\"\n size=\"18\"\n />\n </RuiButton>\n\n <span\n :class=\"ui.iconWrapper()\"\n @click=\"arrowClicked($event)\"\n >\n <RuiIcon\n :class=\"ui.icon()\"\n :size=\"dense ? 16 : 24\"\n name=\"lu-chevron-down\"\n />\n </span>\n\n <RuiProgress\n v-if=\"loading\"\n :class=\"ui.progress()\"\n color=\"primary\"\n thickness=\"3\"\n variant=\"indeterminate\"\n />\n </div>\n <fieldset\n v-if=\"outlined\"\n :class=\"ui.fieldset()\"\n >\n <legend :class=\"ui.legend()\">\n {{ legendText }}\n </legend>\n </fieldset>\n </slot>\n </template>\n <template #default=\"{ width }\">\n <div ref=\"menuWrapperRef\">\n <div\n v-if=\"optionsWithSelectedHidden.length > 0\"\n :ref=\"containerProps.ref\"\n :class=\"ui.menu({ class: cn(classNames?.menu) ?? menuClass })\"\n :style=\"[containerProps.style, { width: `${width}px`, minWidth: menuWidth, minHeight: `${menuMinHeight}px` }]\"\n @scroll=\"containerProps.onScroll\"\n @keydown.up.prevent=\"moveHighlight(true)\"\n @keydown.down.prevent=\"moveHighlight(false)\"\n >\n <div\n v-if=\"isGrouped\"\n ref=\"menuRef\"\n >\n <template\n v-for=\"(bucket, bucketIndex) in groupedOptions\"\n :key=\"bucket.group || `group-${bucketIndex}`\"\n >\n <div\n class=\"sticky top-0 z-10 bg-white dark:bg-rui-grey-900\"\n data-id=\"group-header\"\n >\n <slot\n name=\"group-header\"\n v-bind=\"{ group: bucket.group, items: bucket.items }\"\n >\n <div class=\"px-3 py-1 text-xs uppercase tracking-wide text-rui-text-secondary\">\n {{ bucket.group }}\n </div>\n </slot>\n </div>\n <RuiButton\n v-for=\"item in bucket.items\"\n :key=\"getIdentifier(item)?.toString()\"\n :active=\"isActiveItem(item)\"\n :aria-selected=\"isActiveItem(item)\"\n :size=\"dense ? 'sm' : undefined\"\n :disabled=\"isItemDisabled(item)\"\n tabindex=\"0\"\n variant=\"list\"\n :data-highlighted=\"optionsWithSelectedHidden.indexOf(item) === highlightedIndex\"\n :data-disabled=\"isItemDisabled(item) || undefined\"\n :class=\"{\n [highlightedClass]: !isActiveItem(item) && optionsWithSelectedHidden.indexOf(item) === highlightedIndex,\n }\"\n @click=\"!isItemDisabled(item) && setValue(item)\"\n >\n <template #prepend>\n <slot\n name=\"item.prepend\"\n v-bind=\"{ disabled: isItemDisabled(item), item, active: isActiveItem(item) }\"\n />\n </template>\n <slot\n name=\"item\"\n v-bind=\"{ disabled: isItemDisabled(item), item, active: isActiveItem(item) }\"\n >\n {{ getText(item) }}\n </slot>\n <template #append>\n <slot\n name=\"item.append\"\n v-bind=\"{ disabled: isItemDisabled(item), item, active: isActiveItem(item) }\"\n />\n </template>\n </RuiButton>\n </template>\n </div>\n <div\n v-else\n v-bind=\"wrapperProps\"\n ref=\"menuRef\"\n >\n <RuiButton\n v-for=\"{ item, _index } in renderedData\"\n :key=\"getIdentifier(item)?.toString()\"\n :active=\"isActiveItem(item)\"\n :aria-selected=\"isActiveItem(item)\"\n :size=\"dense ? 'sm' : undefined\"\n :disabled=\"isItemDisabled(item)\"\n tabindex=\"0\"\n variant=\"list\"\n :data-highlighted=\"highlightedIndex === _index\"\n :data-disabled=\"isItemDisabled(item) || undefined\"\n :class=\"{\n [highlightedClass]: !isActiveItem(item) && highlightedIndex === _index,\n }\"\n @click=\"!isItemDisabled(item) && setValue(item)\"\n >\n <template #prepend>\n <slot\n name=\"item.prepend\"\n v-bind=\"{ disabled: isItemDisabled(item), item, active: isActiveItem(item) }\"\n />\n </template>\n <slot\n name=\"item\"\n v-bind=\"{ disabled: isItemDisabled(item), item, active: isActiveItem(item) }\"\n >\n {{ getText(item) }}\n </slot>\n <template #append>\n <slot\n name=\"item.append\"\n v-bind=\"{ disabled: isItemDisabled(item), item, active: isActiveItem(item) }\"\n />\n </template>\n </RuiButton>\n </div>\n </div>\n\n <div\n v-else-if=\"!hideNoData\"\n :style=\"{ width: `${width}px`, minWidth: menuWidth }\"\n :class=\"classNames?.menu ?? menuClass\"\n >\n <slot name=\"no-data\">\n <div\n v-if=\"!customValue\"\n class=\"p-4\"\n data-id=\"no-data\"\n >\n {{ noDataText }}\n </div>\n </slot>\n </div>\n <!--\n The scroll container above reserves ~15px on the right for the\n scrollbar gutter. We render an equivalent right-padding here so the\n footer's content aligns with the option rows' content rather than\n the menu's outer edge. `pr-[var(--rui-scrollbar-gutter,15px)]`\n lets consumers override if their theme reserves a different width.\n -->\n <div\n v-if=\"slots.footer\"\n class=\"bg-white dark:bg-rui-grey-900 border-t border-black/[0.12] dark:border-white/[0.12] pr-[var(--rui-scrollbar-gutter,15px)] -mb-2\"\n data-id=\"footer\"\n >\n <slot name=\"footer\" />\n </div>\n </div>\n </template>\n </RuiMenu>\n</template>\n"],"mappings":""}
|
|
1
|
+
{"version":3,"file":"RuiAutoComplete.js","names":[],"sources":["../../../../src/components/forms/auto-complete/RuiAutoComplete.vue"],"sourcesContent":["<script lang=\"ts\" setup generic=\"TValue, TItem\">\nimport type { VueClassValue } from '@/types/class-value';\nimport RuiButton from '@/components/buttons/button/RuiButton.vue';\nimport RuiChip from '@/components/chips/RuiChip.vue';\nimport { autoCompleteStyles, type AutoCompleteVariant } from '@/components/forms/auto-complete/auto-complete-styles';\nimport RuiIcon from '@/components/icons/RuiIcon.vue';\nimport RuiMenu, { type MenuProps } from '@/components/overlays/menu/RuiMenu.vue';\nimport RuiProgress from '@/components/progress/RuiProgress.vue';\nimport {\n type GroupBy,\n type ItemDisabled,\n type KeyOfType,\n useDropdownMenu,\n useDropdownOptionProperty,\n} from '@/composables/dropdown-menu';\nimport { type FloatingOptions, Placement } from '@/composables/floating';\nimport {\n useAutoCompleteFocus,\n useAutoCompleteKeyboardNavigation,\n useAutoCompleteSearch,\n useAutoCompleteValue,\n} from '@/composables/forms/auto-complete';\nimport { useFormTextDetail } from '@/utils/form-text-detail';\nimport { getNonRootAttrs, getRootAttrs, getTextToken } from '@/utils/helpers';\nimport { isEqual } from '@/utils/is-equal';\nimport { cn } from '@/utils/tv';\n\nexport type AutoCompleteModelValue<TValue> =\n TValue extends Array<infer U> ? U[] : TValue | undefined;\n\nexport interface RuiAutoCompleteClassNames {\n root?: VueClassValue;\n label?: VueClassValue;\n menu?: VueClassValue;\n}\n\nexport interface AutoCompleteProps<TValue, TItem> {\n options?: TItem[];\n keyAttr?: KeyOfType<TItem, TValue extends Array<infer U> ? U : TValue>;\n textAttr?: keyof TItem;\n disabled?: boolean;\n loading?: boolean;\n readOnly?: boolean;\n dense?: boolean;\n clearable?: boolean;\n label?: string;\n menuOptions?: MenuProps;\n classNames?: RuiAutoCompleteClassNames;\n /** @deprecated Use `classNames.label` instead */\n labelClass?: string;\n /** @deprecated Use `classNames.menu` instead */\n menuClass?: string;\n prependWidth?: number;\n appendWidth?: number;\n itemHeight?: number;\n variant?: AutoCompleteVariant;\n hint?: string;\n errorMessages?: string | string[];\n successMessages?: string | string[];\n hideDetails?: boolean;\n autoSelectFirst?: boolean;\n chips?: boolean;\n noFilter?: boolean;\n hideNoData?: boolean;\n noDataText?: string;\n /**\n * Custom search predicate. Receives the resolved group label as a third\n * argument when `groupBy` is set, so callers don't have to re-run the\n * `groupBy` resolver themselves.\n */\n filter?: (item: TItem, queryText: string, group?: string) => boolean;\n hideSelected?: boolean;\n placeholder?: string;\n returnObject?: boolean;\n customValue?: boolean;\n hideCustomValue?: boolean;\n required?: boolean;\n hideSearchInput?: boolean;\n hideSelectionWrapper?: boolean;\n groupBy?: GroupBy<TItem>;\n itemDisabled?: ItemDisabled<TItem>;\n /**\n * When true and `groupBy` is set, the default search predicate also matches\n * against the resolved group label. Items belonging to a group whose label\n * matches the query stay visible (group header included), even when the\n * items themselves don't match. No effect when a custom `filter` is supplied\n * or when `groupBy` is undefined.\n */\n searchIncludesGroupLabel?: boolean;\n}\n\ndefineOptions({\n name: 'RuiAutoComplete',\n inheritAttrs: false,\n});\n\nconst modelValue = defineModel<AutoCompleteModelValue<TValue>>({ required: true });\n\nconst searchInputModel = defineModel<string>('searchInput', { default: '' });\n\nconst {\n options = [],\n disabled = false,\n loading = false,\n readOnly = false,\n dense = false,\n clearable = false,\n hideDetails = false,\n chips = false,\n label = 'Select',\n menuOptions,\n classNames,\n labelClass,\n menuClass,\n variant = 'default',\n hint,\n keyAttr,\n textAttr,\n itemHeight,\n errorMessages = [],\n successMessages = [],\n autoSelectFirst = false,\n noFilter = false,\n hideNoData = false,\n noDataText = 'No data available',\n filter,\n hideSelected = false,\n placeholder = '',\n returnObject = false,\n customValue = false,\n hideCustomValue = false,\n required = false,\n hideSearchInput = false,\n hideSelectionWrapper = false,\n groupBy,\n itemDisabled,\n searchIncludesGroupLabel = false,\n} = defineProps<AutoCompleteProps<TValue, TItem>>();\n\nconst slots = defineSlots<{\n 'activator'?: (props: {\n disabled: boolean;\n value: TItem[];\n variant: string;\n readOnly: boolean;\n attrs: Record<string, unknown>;\n open: boolean;\n hasError: boolean;\n hasSuccess: boolean;\n }) => any;\n 'activator.label'?: (props: { value: TItem[] }) => any;\n 'selection.prepend'?: (props: { index: number; item: TItem }) => any;\n 'selection'?: (props: { index: number; item: TItem; chipAttrs: Record<string, unknown> }) => any;\n 'item.prepend'?: (props: { disabled: boolean; item: TItem; active: boolean }) => any;\n 'item'?: (props: { disabled: boolean; item: TItem; active: boolean }) => any;\n 'item.append'?: (props: { disabled: boolean; item: TItem; active: boolean }) => any;\n 'group-header'?: (props: { group: string; items: TItem[] }) => any;\n 'no-data'?: () => any;\n 'placeholder'?: (props: { disabled: boolean; readOnly: boolean }) => any;\n 'footer'?: () => any;\n}>();\n\nconst { getText, getIdentifier } = useDropdownOptionProperty<TValue, TItem>({\n keyAttr,\n textAttr,\n});\n\nconst textInput = useTemplateRef<HTMLInputElement>('textInput');\nconst activator = useTemplateRef<HTMLDivElement>('activator');\nconst menuRef = useTemplateRef<HTMLDivElement>('menuRef');\nconst menuWrapperRef = useTemplateRef<HTMLDivElement>('menuWrapperRef');\n\nconst { focused: activatorFocusedWithin } = useFocusWithin(activator);\nconst { focused: menuWrapperFocusedWithin } = useFocusWithin(menuWrapperRef);\nconst { focused: searchInputFocused } = useFocus(textInput);\nconst { focused: activatorFocused } = useFocus(activator);\n\nconst {\n internalSearch,\n filteredOptions,\n justOpened,\n updateInternalSearch,\n textValueToProperValue,\n} = useAutoCompleteSearch<TItem>(\n () => options,\n searchInputModel,\n {\n keyAttr: () => keyAttr,\n textAttr: () => textAttr,\n noFilter: () => noFilter,\n filter: () => filter,\n customValue: () => customValue,\n hideCustomValue: () => hideCustomValue,\n returnObject: () => returnObject,\n groupBy: () => groupBy,\n searchIncludesGroupLabel: () => searchIncludesGroupLabel,\n },\n);\n\nconst isOpen = ref<boolean>(false);\nconst isHovered = ref<boolean>(false);\n\n// Calculate multiple from modelValue directly to avoid circular dependency\nconst multiple = computed<boolean>(() => Array.isArray(get(modelValue)));\n\nconst shouldApplyValueAsSearch = computed<boolean>(\n () => !(slots.selection || get(multiple) || chips),\n);\n\nconst { resolveIn, value, setSelected } = useAutoCompleteValue<AutoCompleteModelValue<TValue>, TItem>(\n modelValue,\n () => options,\n {\n keyAttr: () => keyAttr,\n returnObject: () => returnObject,\n customValue: () => customValue,\n },\n {\n getIdentifier,\n getText,\n textValueToProperValue,\n shouldApplyValueAsSearch,\n isOpen,\n multiple,\n updateInternalSearch,\n },\n);\n\nconst resolvedItemHeight = itemHeight ?? (dense ? 30 : 48);\n\nconst {\n containerProps,\n wrapperProps,\n renderedData,\n menuWidth,\n isActiveItem,\n itemIndexInValue,\n highlightedIndex,\n moveHighlight,\n applyHighlighted,\n optionsWithSelectedHidden,\n userNavigated,\n groupedOptions,\n isGrouped,\n isItemDisabled,\n} = useDropdownMenu<TValue, TItem>({\n itemHeight: resolvedItemHeight,\n keyAttr,\n textAttr,\n options: filteredOptions,\n dense: () => dense,\n value,\n menuRef,\n setValue,\n autoSelectFirst,\n hideSelected,\n isOpen,\n getText,\n getIdentifier,\n groupBy: () => groupBy,\n itemDisabled: () => itemDisabled,\n});\n\nconst {\n focusedValueIndex,\n moveSelectedValueHighlight,\n onEnter,\n onInputDeletePressed,\n onTab,\n setValueFocus,\n} = useAutoCompleteKeyboardNavigation<TItem>(\n {\n chips: () => chips,\n customValue: () => customValue,\n multiple,\n },\n {\n activator,\n applyHighlighted,\n clear,\n filteredOptions,\n getText,\n highlightedIndex,\n internalSearch,\n isOpen,\n removeValue: (item: TItem): void => { setValue(item); },\n searchInputFocused,\n setSearchAsValue,\n userNavigated,\n value,\n },\n);\n\nconst {\n anyFocused: focusAnyFocused,\n inputClass: focusInputClass,\n onActivatorFocused: focusOnActivatorFocused,\n onInputFocused: focusOnInputFocused,\n setInputFocus: focusSetInputFocus,\n} = useAutoCompleteFocus(\n {\n customValue: () => customValue,\n disabled: () => disabled,\n shouldApplyValueAsSearch,\n },\n {\n activatorFocused,\n activatorFocusedWithin,\n focusedValueIndex,\n internalSearch,\n isOpen,\n justOpened,\n menuWrapperFocusedWithin,\n searchInputFocused,\n setSearchAsValue,\n textInput,\n updateInternalSearch,\n },\n);\n\nconst menuMinHeight = computed<number>(\n () => Math.min(5, get(optionsWithSelectedHidden).length) * resolvedItemHeight,\n);\n\nconst { hasError, hasSuccess } = useFormTextDetail(\n () => errorMessages,\n () => successMessages,\n);\n\nconst valueSet = computed<boolean>(() => get(value).length > 0);\n\n// True when the consumer's #placeholder slot is taking over the resting\n// content area, so the outlined/resting label must hide to avoid overlapping\n// the slot content.\nconst placeholderSlotActive = computed<boolean>(() => Boolean(slots.placeholder) && !get(valueSet) && !get(searchInputFocused));\n\nconst usedPlaceholder = computed<string>(() => {\n if (get(searchInputFocused))\n return placeholder;\n return '';\n});\n\nconst outlined = computed<boolean>(() => variant === 'outlined');\nconst float = computed<boolean>(() => (get(isOpen) || get(valueSet) || get(searchInputFocused)) && get(outlined));\n\nconst legendText = computed<string>(() => {\n if (!get(float) || !label)\n return '';\n return required ? `${label} ﹡` : label;\n});\n\nconst ui = computed<ReturnType<typeof autoCompleteStyles>>(() => autoCompleteStyles({\n filled: variant === 'filled',\n outlined: get(outlined),\n float: get(float),\n opened: get(isOpen),\n hovered: get(isHovered),\n dense,\n disabled,\n readonly: readOnly,\n hasError: get(hasError),\n hasSuccess: get(hasSuccess) && !get(hasError),\n}));\n\nconst highlightedClass = autoCompleteStyles({}).highlighted();\n\nfunction updateSearchInput(event: Event): void {\n const target = event.target;\n if (!(target instanceof HTMLInputElement))\n return;\n\n const value = target.value;\n set(isOpen, true);\n updateInternalSearch(value);\n set(justOpened, false);\n}\n\nasync function setValue(val: TItem, skipRefocused = false): Promise<void> {\n const isMultiple = get(multiple);\n\n if (isMultiple) {\n const newValue = [...get(value)];\n const indexInValue = itemIndexInValue(val);\n if (indexInValue === -1) {\n updateInternalSearch();\n newValue.push(val);\n }\n else {\n newValue.splice(indexInValue, 1);\n }\n set(value, newValue);\n }\n else {\n if (get(shouldApplyValueAsSearch))\n updateInternalSearch(getText(val));\n else updateInternalSearch();\n\n set(value, [val]);\n }\n\n if (!isMultiple) {\n if (!skipRefocused) {\n set(activatorFocused, true);\n get(activator)?.focus();\n await nextTick(() => {\n set(isOpen, false);\n });\n }\n else {\n set(isOpen, false);\n }\n }\n else if (!skipRefocused) {\n set(searchInputFocused, true);\n }\n}\n\nfunction setSearchAsValue(): void {\n const searchToBeValue = get(internalSearch);\n if (!searchToBeValue)\n return;\n\n const newValue: TItem = textValueToProperValue(searchToBeValue);\n setValue(newValue, true);\n}\n\nfunction clear(): void {\n updateInternalSearch();\n set(modelValue, (Array.isArray(get(modelValue)) ? [] : undefined) as AutoCompleteModelValue<TValue>);\n}\n\nfunction chipAttrs(item: TItem, index: number): Record<string, unknown> {\n return {\n 'data-index': index,\n 'data-value': getIdentifier(item),\n 'onKeydown': (event: KeyboardEvent): void => {\n const { key } = event;\n if (['Backspace', 'Delete'].includes(key)) {\n event.stopPropagation();\n event.preventDefault();\n // Alt/Option + delete converts the chip back into editable search text\n // instead of removing it outright. Only meaningful when custom values\n // are accepted, since the restored text has to be re-selectable.\n if (event.altKey && customValue) {\n const text = getText(item) ?? '';\n setValue(item);\n updateInternalSearch(text);\n }\n else {\n setValue(item);\n }\n }\n },\n 'onClick': (e: MouseEvent): void => {\n e.stopPropagation();\n setValueFocus(index);\n },\n 'onClick:close': (): void => {\n setValue(item);\n },\n };\n}\n\nfunction setSelectionRange(start: number, end: number): void {\n set(searchInputFocused, true);\n get(textInput)?.setSelectionRange?.(start, end);\n}\n\nfunction arrowClicked(event: MouseEvent): void {\n if (get(isOpen)) {\n set(isOpen, false);\n event.stopPropagation();\n }\n}\n\nfunction openMenu(): void {\n set(isOpen, true);\n}\n\nfunction closeMenu(): void {\n set(isOpen, false);\n}\n\n// Optimize options watcher with shallow comparison first\nwatch(() => options, (curr, old) => {\n if (curr === old || customValue)\n return;\n\n // Only do deep comparison if reference changed\n if (isEqual(curr, old))\n return;\n\n // Only reconcile a selection the previous options could actually resolve. A value\n // that was already unresolvable is one the consumer set before its options arrived\n // (async lists start empty), so clearing it here would discard a legitimate value.\n if (!get(multiple) && resolveIn(old).length === 0)\n return;\n\n setSelected(get(value));\n});\n\nconst menuFloatingOptions = computed<FloatingOptions>(() => ({\n placement: Placement.bottomStart,\n ...menuOptions?.options,\n}));\n\ndefineExpose({\n closeMenu,\n focus: focusSetInputFocus,\n openMenu,\n setSelectionRange,\n});\n</script>\n\n<template>\n <RuiMenu\n v-model=\"isOpen\"\n v-bind=\"{ ...getRootAttrs($attrs, []), ...menuOptions }\"\n :class=\"ui.wrapper({ class: cn($attrs.class) })\"\n :options=\"menuFloatingOptions\"\n :close-on-content-click=\"false\"\n :full-width=\"true\"\n :persist-on-activator-click=\"true\"\n :menu-class=\"[\n { hidden: optionsWithSelectedHidden.length === 0 && customValue && !slots['no-data'] },\n menuOptions?.menuClass,\n ]\"\n :error-messages=\"errorMessages\"\n :success-messages=\"successMessages\"\n :hint=\"hint\"\n :dense=\"dense\"\n :show-details=\"!hideDetails\"\n :disabled=\"disabled\"\n disable-auto-focus\n >\n <template #activator=\"{ attrs, open, hasError: slotHasError, hasSuccess: slotHasSuccess }\">\n <slot\n name=\"activator\"\n v-bind=\"{ disabled, value, variant, readOnly, attrs, open, hasError: slotHasError, hasSuccess: slotHasSuccess }\"\n >\n <div\n ref=\"activator\"\n :class=\"ui.activator({ class: cn(classNames?.label) ?? labelClass })\"\n v-bind=\"{\n ...getNonRootAttrs($attrs, ['onClick', 'class']),\n ...(readOnly ? {} : attrs),\n }\"\n role=\"combobox\"\n :aria-expanded=\"open\"\n :aria-disabled=\"disabled || undefined\"\n :aria-readonly=\"readOnly || undefined\"\n :aria-required=\"required || undefined\"\n :aria-busy=\"loading || undefined\"\n data-id=\"activator\"\n :aria-invalid=\"hasError\"\n :tabindex=\"disabled || readOnly ? -1 : 0\"\n @mouseenter=\"isHovered = true\"\n @mouseleave=\"isHovered = false\"\n @click=\"focusSetInputFocus()\"\n @focus=\"focusOnActivatorFocused()\"\n @keydown.enter=\"onEnter($event)\"\n @keydown.tab=\"onTab($event)\"\n @keydown.left=\"moveSelectedValueHighlight($event, false)\"\n @keydown.right=\"moveSelectedValueHighlight($event, true)\"\n @keydown.up.prevent=\"moveHighlight(true)\"\n @keydown.down.prevent=\"moveHighlight(false)\"\n @keydown.home.prevent=\"highlightedIndex = 0\"\n @keydown.end.prevent=\"highlightedIndex = optionsWithSelectedHidden.length - 1\"\n >\n <span\n v-if=\"(outlined || (!valueSet && !searchInputFocused)) && !placeholderSlotActive\"\n :class=\"[\n ui.label(),\n { 'pr-2': !valueSet && !open && outlined },\n ]\"\n >\n <slot\n name=\"activator.label\"\n v-bind=\"{ value }\"\n >\n {{ label }}\n </slot>\n <span\n v-if=\"required\"\n :class=\"ui.required()\"\n >\n ﹡\n </span>\n </span>\n <div\n data-id=\"value\"\n :class=\"ui.value()\"\n >\n <!--\n Placeholder slot occupies the same flex space the input takes\n when focused, so transitioning into the focused state doesn't\n shift the activator's content. pointer-events-none is required\n so clicks pass through to the activator (otherwise the slot\n swallows the first click and the menu needs two taps to open).\n -->\n <div\n v-if=\"!valueSet && !searchInputFocused && slots.placeholder\"\n data-id=\"placeholder\"\n class=\"flex-1 min-w-0 pointer-events-none\"\n >\n <slot\n name=\"placeholder\"\n v-bind=\"{ disabled, readOnly }\"\n />\n </div>\n <template\n v-for=\"(item, i) in value\"\n :key=\"getIdentifier(item)\"\n >\n <RuiChip\n v-if=\"chips\"\n :key=\"getTextToken(getIdentifier(item))\"\n tabindex=\"-1\"\n :size=\"dense ? 'sm' : 'md'\"\n closeable\n :class=\"{ 'leading-3': dense }\"\n clickable\n v-bind=\"chipAttrs(item, i)\"\n >\n <div class=\"flex\">\n <slot\n name=\"selection.prepend\"\n :index=\"i\"\n v-bind=\"{ item }\"\n />\n <slot\n :index=\"i\"\n name=\"selection\"\n v-bind=\"{ item, chipAttrs: chipAttrs(item, i) }\"\n >\n {{ getText(item) }}\n </slot>\n </div>\n </RuiChip>\n <div\n v-else-if=\"\n multiple\n || (!searchInputFocused && (slots['selection.prepend'] || slots.selection))\n \"\n :class=\"hideSelectionWrapper ? 'contents' : 'flex'\"\n >\n <slot\n name=\"selection.prepend\"\n :index=\"i\"\n v-bind=\"{ item }\"\n />\n <slot\n v-if=\"multiple || slots.selection\"\n :index=\"i\"\n name=\"selection\"\n v-bind=\"{ item, chipAttrs: chipAttrs(item, i) }\"\n >\n {{ getText(item) }}\n </slot>\n </div>\n </template>\n <input\n ref=\"textInput\"\n :disabled=\"disabled\"\n :value=\"internalSearch\"\n class=\"bg-transparent outline-none\"\n type=\"text\"\n :placeholder=\"usedPlaceholder\"\n :class=\"[focusInputClass, { hidden: hideSearchInput }]\"\n :aria-invalid=\"hasError\"\n aria-autocomplete=\"list\"\n @keydown.delete=\"onInputDeletePressed()\"\n @input.stop=\"updateSearchInput($event)\"\n @focus=\"focusOnInputFocused()\"\n />\n </div>\n\n <RuiButton\n v-if=\"clearable && valueSet && !disabled\"\n variant=\"text\"\n icon\n size=\"sm\"\n tabindex=\"-1\"\n color=\"error\"\n data-id=\"clear\"\n :class=\"[\n ui.clear(),\n focusAnyFocused && '!visible',\n { 'mr-2': !dense },\n ]\"\n @click.stop.prevent=\"clear()\"\n >\n <RuiIcon\n name=\"lu-x\"\n size=\"18\"\n />\n </RuiButton>\n\n <span\n :class=\"ui.iconWrapper()\"\n @click=\"arrowClicked($event)\"\n >\n <RuiIcon\n :class=\"ui.icon()\"\n :size=\"dense ? 16 : 24\"\n name=\"lu-chevron-down\"\n />\n </span>\n\n <RuiProgress\n v-if=\"loading\"\n :class=\"ui.progress()\"\n color=\"primary\"\n thickness=\"3\"\n variant=\"indeterminate\"\n />\n </div>\n <fieldset\n v-if=\"outlined\"\n :class=\"ui.fieldset()\"\n >\n <legend :class=\"ui.legend()\">\n {{ legendText }}\n </legend>\n </fieldset>\n </slot>\n </template>\n <template #default=\"{ width }\">\n <div ref=\"menuWrapperRef\">\n <div\n v-if=\"optionsWithSelectedHidden.length > 0\"\n :ref=\"containerProps.ref\"\n :class=\"ui.menu({ class: cn(classNames?.menu) ?? menuClass })\"\n :style=\"[containerProps.style, { width: `${width}px`, minWidth: menuWidth, minHeight: `${menuMinHeight}px` }]\"\n @scroll=\"containerProps.onScroll\"\n @keydown.up.prevent=\"moveHighlight(true)\"\n @keydown.down.prevent=\"moveHighlight(false)\"\n >\n <div\n v-if=\"isGrouped\"\n ref=\"menuRef\"\n >\n <template\n v-for=\"(bucket, bucketIndex) in groupedOptions\"\n :key=\"bucket.group || `group-${bucketIndex}`\"\n >\n <div\n class=\"sticky top-0 z-10 bg-white dark:bg-rui-grey-900\"\n data-id=\"group-header\"\n >\n <slot\n name=\"group-header\"\n v-bind=\"{ group: bucket.group, items: bucket.items }\"\n >\n <div class=\"px-3 py-1 text-xs uppercase tracking-wide text-rui-text-secondary\">\n {{ bucket.group }}\n </div>\n </slot>\n </div>\n <RuiButton\n v-for=\"item in bucket.items\"\n :key=\"getIdentifier(item)?.toString()\"\n :active=\"isActiveItem(item)\"\n :aria-selected=\"isActiveItem(item)\"\n :size=\"dense ? 'sm' : undefined\"\n :disabled=\"isItemDisabled(item)\"\n tabindex=\"0\"\n variant=\"list\"\n :data-highlighted=\"optionsWithSelectedHidden.indexOf(item) === highlightedIndex\"\n :data-disabled=\"isItemDisabled(item) || undefined\"\n :class=\"{\n [highlightedClass]: !isActiveItem(item) && optionsWithSelectedHidden.indexOf(item) === highlightedIndex,\n }\"\n @click=\"!isItemDisabled(item) && setValue(item)\"\n >\n <template #prepend>\n <slot\n name=\"item.prepend\"\n v-bind=\"{ disabled: isItemDisabled(item), item, active: isActiveItem(item) }\"\n />\n </template>\n <slot\n name=\"item\"\n v-bind=\"{ disabled: isItemDisabled(item), item, active: isActiveItem(item) }\"\n >\n {{ getText(item) }}\n </slot>\n <template #append>\n <slot\n name=\"item.append\"\n v-bind=\"{ disabled: isItemDisabled(item), item, active: isActiveItem(item) }\"\n />\n </template>\n </RuiButton>\n </template>\n </div>\n <div\n v-else\n v-bind=\"wrapperProps\"\n ref=\"menuRef\"\n >\n <RuiButton\n v-for=\"{ item, _index } in renderedData\"\n :key=\"getIdentifier(item)?.toString()\"\n :active=\"isActiveItem(item)\"\n :aria-selected=\"isActiveItem(item)\"\n :size=\"dense ? 'sm' : undefined\"\n :disabled=\"isItemDisabled(item)\"\n tabindex=\"0\"\n variant=\"list\"\n :data-highlighted=\"highlightedIndex === _index\"\n :data-disabled=\"isItemDisabled(item) || undefined\"\n :class=\"{\n [highlightedClass]: !isActiveItem(item) && highlightedIndex === _index,\n }\"\n @click=\"!isItemDisabled(item) && setValue(item)\"\n >\n <template #prepend>\n <slot\n name=\"item.prepend\"\n v-bind=\"{ disabled: isItemDisabled(item), item, active: isActiveItem(item) }\"\n />\n </template>\n <slot\n name=\"item\"\n v-bind=\"{ disabled: isItemDisabled(item), item, active: isActiveItem(item) }\"\n >\n {{ getText(item) }}\n </slot>\n <template #append>\n <slot\n name=\"item.append\"\n v-bind=\"{ disabled: isItemDisabled(item), item, active: isActiveItem(item) }\"\n />\n </template>\n </RuiButton>\n </div>\n </div>\n\n <div\n v-else-if=\"!hideNoData\"\n :style=\"{ width: `${width}px`, minWidth: menuWidth }\"\n :class=\"classNames?.menu ?? menuClass\"\n >\n <slot name=\"no-data\">\n <div\n v-if=\"!customValue\"\n class=\"p-4\"\n data-id=\"no-data\"\n >\n {{ noDataText }}\n </div>\n </slot>\n </div>\n <!--\n The scroll container above reserves ~15px on the right for the\n scrollbar gutter. We render an equivalent right-padding here so the\n footer's content aligns with the option rows' content rather than\n the menu's outer edge. `pr-[var(--rui-scrollbar-gutter,15px)]`\n lets consumers override if their theme reserves a different width.\n -->\n <div\n v-if=\"slots.footer\"\n class=\"bg-white dark:bg-rui-grey-900 border-t border-black/[0.12] dark:border-white/[0.12] pr-[var(--rui-scrollbar-gutter,15px)] -mb-2\"\n data-id=\"footer\"\n >\n <slot name=\"footer\" />\n </div>\n </div>\n </template>\n </RuiMenu>\n</template>\n"],"mappings":""}
|
package/dist/components/forms/auto-complete/RuiAutoComplete.vue_vue_type_script_setup_true_lang.js
CHANGED
|
@@ -190,7 +190,7 @@ var RuiAutoComplete_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*/
|
|
|
190
190
|
const isHovered = ref(false);
|
|
191
191
|
const multiple = computed(() => Array.isArray(get$1(modelValue)));
|
|
192
192
|
const shouldApplyValueAsSearch = computed(() => !(slots.selection || get$1(multiple) || __props.chips));
|
|
193
|
-
const { value, setSelected } = useAutoCompleteValue(modelValue, () => __props.options, {
|
|
193
|
+
const { resolveIn, value, setSelected } = useAutoCompleteValue(modelValue, () => __props.options, {
|
|
194
194
|
keyAttr: () => __props.keyAttr,
|
|
195
195
|
returnObject: () => __props.returnObject,
|
|
196
196
|
customValue: () => __props.customValue
|
|
@@ -371,6 +371,7 @@ var RuiAutoComplete_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*/
|
|
|
371
371
|
watch(() => __props.options, (curr, old) => {
|
|
372
372
|
if (curr === old || __props.customValue) return;
|
|
373
373
|
if (isEqual(curr, old)) return;
|
|
374
|
+
if (!get$1(multiple) && resolveIn(old).length === 0) return;
|
|
374
375
|
setSelected(get$1(value));
|
|
375
376
|
});
|
|
376
377
|
const menuFloatingOptions = computed(() => ({
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"RuiAutoComplete.vue_vue_type_script_setup_true_lang.js","names":["$attrs"],"sources":["../../../../src/components/forms/auto-complete/RuiAutoComplete.vue"],"sourcesContent":["<script lang=\"ts\" setup generic=\"TValue, TItem\">\nimport type { VueClassValue } from '@/types/class-value';\nimport RuiButton from '@/components/buttons/button/RuiButton.vue';\nimport RuiChip from '@/components/chips/RuiChip.vue';\nimport { autoCompleteStyles, type AutoCompleteVariant } from '@/components/forms/auto-complete/auto-complete-styles';\nimport RuiIcon from '@/components/icons/RuiIcon.vue';\nimport RuiMenu, { type MenuProps } from '@/components/overlays/menu/RuiMenu.vue';\nimport RuiProgress from '@/components/progress/RuiProgress.vue';\nimport {\n type GroupBy,\n type ItemDisabled,\n type KeyOfType,\n useDropdownMenu,\n useDropdownOptionProperty,\n} from '@/composables/dropdown-menu';\nimport { type FloatingOptions, Placement } from '@/composables/floating';\nimport {\n useAutoCompleteFocus,\n useAutoCompleteKeyboardNavigation,\n useAutoCompleteSearch,\n useAutoCompleteValue,\n} from '@/composables/forms/auto-complete';\nimport { useFormTextDetail } from '@/utils/form-text-detail';\nimport { getNonRootAttrs, getRootAttrs, getTextToken } from '@/utils/helpers';\nimport { isEqual } from '@/utils/is-equal';\nimport { cn } from '@/utils/tv';\n\nexport type AutoCompleteModelValue<TValue> =\n TValue extends Array<infer U> ? U[] : TValue | undefined;\n\nexport interface RuiAutoCompleteClassNames {\n root?: VueClassValue;\n label?: VueClassValue;\n menu?: VueClassValue;\n}\n\nexport interface AutoCompleteProps<TValue, TItem> {\n options?: TItem[];\n keyAttr?: KeyOfType<TItem, TValue extends Array<infer U> ? U : TValue>;\n textAttr?: keyof TItem;\n disabled?: boolean;\n loading?: boolean;\n readOnly?: boolean;\n dense?: boolean;\n clearable?: boolean;\n label?: string;\n menuOptions?: MenuProps;\n classNames?: RuiAutoCompleteClassNames;\n /** @deprecated Use `classNames.label` instead */\n labelClass?: string;\n /** @deprecated Use `classNames.menu` instead */\n menuClass?: string;\n prependWidth?: number;\n appendWidth?: number;\n itemHeight?: number;\n variant?: AutoCompleteVariant;\n hint?: string;\n errorMessages?: string | string[];\n successMessages?: string | string[];\n hideDetails?: boolean;\n autoSelectFirst?: boolean;\n chips?: boolean;\n noFilter?: boolean;\n hideNoData?: boolean;\n noDataText?: string;\n /**\n * Custom search predicate. Receives the resolved group label as a third\n * argument when `groupBy` is set, so callers don't have to re-run the\n * `groupBy` resolver themselves.\n */\n filter?: (item: TItem, queryText: string, group?: string) => boolean;\n hideSelected?: boolean;\n placeholder?: string;\n returnObject?: boolean;\n customValue?: boolean;\n hideCustomValue?: boolean;\n required?: boolean;\n hideSearchInput?: boolean;\n hideSelectionWrapper?: boolean;\n groupBy?: GroupBy<TItem>;\n itemDisabled?: ItemDisabled<TItem>;\n /**\n * When true and `groupBy` is set, the default search predicate also matches\n * against the resolved group label. Items belonging to a group whose label\n * matches the query stay visible (group header included), even when the\n * items themselves don't match. No effect when a custom `filter` is supplied\n * or when `groupBy` is undefined.\n */\n searchIncludesGroupLabel?: boolean;\n}\n\ndefineOptions({\n name: 'RuiAutoComplete',\n inheritAttrs: false,\n});\n\nconst modelValue = defineModel<AutoCompleteModelValue<TValue>>({ required: true });\n\nconst searchInputModel = defineModel<string>('searchInput', { default: '' });\n\nconst {\n options = [],\n disabled = false,\n loading = false,\n readOnly = false,\n dense = false,\n clearable = false,\n hideDetails = false,\n chips = false,\n label = 'Select',\n menuOptions,\n classNames,\n labelClass,\n menuClass,\n variant = 'default',\n hint,\n keyAttr,\n textAttr,\n itemHeight,\n errorMessages = [],\n successMessages = [],\n autoSelectFirst = false,\n noFilter = false,\n hideNoData = false,\n noDataText = 'No data available',\n filter,\n hideSelected = false,\n placeholder = '',\n returnObject = false,\n customValue = false,\n hideCustomValue = false,\n required = false,\n hideSearchInput = false,\n hideSelectionWrapper = false,\n groupBy,\n itemDisabled,\n searchIncludesGroupLabel = false,\n} = defineProps<AutoCompleteProps<TValue, TItem>>();\n\nconst slots = defineSlots<{\n 'activator'?: (props: {\n disabled: boolean;\n value: TItem[];\n variant: string;\n readOnly: boolean;\n attrs: Record<string, unknown>;\n open: boolean;\n hasError: boolean;\n hasSuccess: boolean;\n }) => any;\n 'activator.label'?: (props: { value: TItem[] }) => any;\n 'selection.prepend'?: (props: { index: number; item: TItem }) => any;\n 'selection'?: (props: { index: number; item: TItem; chipAttrs: Record<string, unknown> }) => any;\n 'item.prepend'?: (props: { disabled: boolean; item: TItem; active: boolean }) => any;\n 'item'?: (props: { disabled: boolean; item: TItem; active: boolean }) => any;\n 'item.append'?: (props: { disabled: boolean; item: TItem; active: boolean }) => any;\n 'group-header'?: (props: { group: string; items: TItem[] }) => any;\n 'no-data'?: () => any;\n 'placeholder'?: (props: { disabled: boolean; readOnly: boolean }) => any;\n 'footer'?: () => any;\n}>();\n\nconst { getText, getIdentifier } = useDropdownOptionProperty<TValue, TItem>({\n keyAttr,\n textAttr,\n});\n\nconst textInput = useTemplateRef<HTMLInputElement>('textInput');\nconst activator = useTemplateRef<HTMLDivElement>('activator');\nconst menuRef = useTemplateRef<HTMLDivElement>('menuRef');\nconst menuWrapperRef = useTemplateRef<HTMLDivElement>('menuWrapperRef');\n\nconst { focused: activatorFocusedWithin } = useFocusWithin(activator);\nconst { focused: menuWrapperFocusedWithin } = useFocusWithin(menuWrapperRef);\nconst { focused: searchInputFocused } = useFocus(textInput);\nconst { focused: activatorFocused } = useFocus(activator);\n\nconst {\n internalSearch,\n filteredOptions,\n justOpened,\n updateInternalSearch,\n textValueToProperValue,\n} = useAutoCompleteSearch<TItem>(\n () => options,\n searchInputModel,\n {\n keyAttr: () => keyAttr,\n textAttr: () => textAttr,\n noFilter: () => noFilter,\n filter: () => filter,\n customValue: () => customValue,\n hideCustomValue: () => hideCustomValue,\n returnObject: () => returnObject,\n groupBy: () => groupBy,\n searchIncludesGroupLabel: () => searchIncludesGroupLabel,\n },\n);\n\nconst isOpen = ref<boolean>(false);\nconst isHovered = ref<boolean>(false);\n\n// Calculate multiple from modelValue directly to avoid circular dependency\nconst multiple = computed<boolean>(() => Array.isArray(get(modelValue)));\n\nconst shouldApplyValueAsSearch = computed<boolean>(\n () => !(slots.selection || get(multiple) || chips),\n);\n\nconst { value, setSelected } = useAutoCompleteValue<AutoCompleteModelValue<TValue>, TItem>(\n modelValue,\n () => options,\n {\n keyAttr: () => keyAttr,\n returnObject: () => returnObject,\n customValue: () => customValue,\n },\n {\n getIdentifier,\n getText,\n textValueToProperValue,\n shouldApplyValueAsSearch,\n isOpen,\n multiple,\n updateInternalSearch,\n },\n);\n\nconst resolvedItemHeight = itemHeight ?? (dense ? 30 : 48);\n\nconst {\n containerProps,\n wrapperProps,\n renderedData,\n menuWidth,\n isActiveItem,\n itemIndexInValue,\n highlightedIndex,\n moveHighlight,\n applyHighlighted,\n optionsWithSelectedHidden,\n userNavigated,\n groupedOptions,\n isGrouped,\n isItemDisabled,\n} = useDropdownMenu<TValue, TItem>({\n itemHeight: resolvedItemHeight,\n keyAttr,\n textAttr,\n options: filteredOptions,\n dense: () => dense,\n value,\n menuRef,\n setValue,\n autoSelectFirst,\n hideSelected,\n isOpen,\n getText,\n getIdentifier,\n groupBy: () => groupBy,\n itemDisabled: () => itemDisabled,\n});\n\nconst {\n focusedValueIndex,\n moveSelectedValueHighlight,\n onEnter,\n onInputDeletePressed,\n onTab,\n setValueFocus,\n} = useAutoCompleteKeyboardNavigation<TItem>(\n {\n chips: () => chips,\n customValue: () => customValue,\n multiple,\n },\n {\n activator,\n applyHighlighted,\n clear,\n filteredOptions,\n getText,\n highlightedIndex,\n internalSearch,\n isOpen,\n removeValue: (item: TItem): void => { setValue(item); },\n searchInputFocused,\n setSearchAsValue,\n userNavigated,\n value,\n },\n);\n\nconst {\n anyFocused: focusAnyFocused,\n inputClass: focusInputClass,\n onActivatorFocused: focusOnActivatorFocused,\n onInputFocused: focusOnInputFocused,\n setInputFocus: focusSetInputFocus,\n} = useAutoCompleteFocus(\n {\n customValue: () => customValue,\n disabled: () => disabled,\n shouldApplyValueAsSearch,\n },\n {\n activatorFocused,\n activatorFocusedWithin,\n focusedValueIndex,\n internalSearch,\n isOpen,\n justOpened,\n menuWrapperFocusedWithin,\n searchInputFocused,\n setSearchAsValue,\n textInput,\n updateInternalSearch,\n },\n);\n\nconst menuMinHeight = computed<number>(\n () => Math.min(5, get(optionsWithSelectedHidden).length) * resolvedItemHeight,\n);\n\nconst { hasError, hasSuccess } = useFormTextDetail(\n () => errorMessages,\n () => successMessages,\n);\n\nconst valueSet = computed<boolean>(() => get(value).length > 0);\n\n// True when the consumer's #placeholder slot is taking over the resting\n// content area, so the outlined/resting label must hide to avoid overlapping\n// the slot content.\nconst placeholderSlotActive = computed<boolean>(() => Boolean(slots.placeholder) && !get(valueSet) && !get(searchInputFocused));\n\nconst usedPlaceholder = computed<string>(() => {\n if (get(searchInputFocused))\n return placeholder;\n return '';\n});\n\nconst outlined = computed<boolean>(() => variant === 'outlined');\nconst float = computed<boolean>(() => (get(isOpen) || get(valueSet) || get(searchInputFocused)) && get(outlined));\n\nconst legendText = computed<string>(() => {\n if (!get(float) || !label)\n return '';\n return required ? `${label} ﹡` : label;\n});\n\nconst ui = computed<ReturnType<typeof autoCompleteStyles>>(() => autoCompleteStyles({\n filled: variant === 'filled',\n outlined: get(outlined),\n float: get(float),\n opened: get(isOpen),\n hovered: get(isHovered),\n dense,\n disabled,\n readonly: readOnly,\n hasError: get(hasError),\n hasSuccess: get(hasSuccess) && !get(hasError),\n}));\n\nconst highlightedClass = autoCompleteStyles({}).highlighted();\n\nfunction updateSearchInput(event: Event): void {\n const target = event.target;\n if (!(target instanceof HTMLInputElement))\n return;\n\n const value = target.value;\n set(isOpen, true);\n updateInternalSearch(value);\n set(justOpened, false);\n}\n\nasync function setValue(val: TItem, skipRefocused = false): Promise<void> {\n const isMultiple = get(multiple);\n\n if (isMultiple) {\n const newValue = [...get(value)];\n const indexInValue = itemIndexInValue(val);\n if (indexInValue === -1) {\n updateInternalSearch();\n newValue.push(val);\n }\n else {\n newValue.splice(indexInValue, 1);\n }\n set(value, newValue);\n }\n else {\n if (get(shouldApplyValueAsSearch))\n updateInternalSearch(getText(val));\n else updateInternalSearch();\n\n set(value, [val]);\n }\n\n if (!isMultiple) {\n if (!skipRefocused) {\n set(activatorFocused, true);\n get(activator)?.focus();\n await nextTick(() => {\n set(isOpen, false);\n });\n }\n else {\n set(isOpen, false);\n }\n }\n else if (!skipRefocused) {\n set(searchInputFocused, true);\n }\n}\n\nfunction setSearchAsValue(): void {\n const searchToBeValue = get(internalSearch);\n if (!searchToBeValue)\n return;\n\n const newValue: TItem = textValueToProperValue(searchToBeValue);\n setValue(newValue, true);\n}\n\nfunction clear(): void {\n updateInternalSearch();\n set(modelValue, (Array.isArray(get(modelValue)) ? [] : undefined) as AutoCompleteModelValue<TValue>);\n}\n\nfunction chipAttrs(item: TItem, index: number): Record<string, unknown> {\n return {\n 'data-index': index,\n 'data-value': getIdentifier(item),\n 'onKeydown': (event: KeyboardEvent): void => {\n const { key } = event;\n if (['Backspace', 'Delete'].includes(key)) {\n event.stopPropagation();\n event.preventDefault();\n // Alt/Option + delete converts the chip back into editable search text\n // instead of removing it outright. Only meaningful when custom values\n // are accepted, since the restored text has to be re-selectable.\n if (event.altKey && customValue) {\n const text = getText(item) ?? '';\n setValue(item);\n updateInternalSearch(text);\n }\n else {\n setValue(item);\n }\n }\n },\n 'onClick': (e: MouseEvent): void => {\n e.stopPropagation();\n setValueFocus(index);\n },\n 'onClick:close': (): void => {\n setValue(item);\n },\n };\n}\n\nfunction setSelectionRange(start: number, end: number): void {\n set(searchInputFocused, true);\n get(textInput)?.setSelectionRange?.(start, end);\n}\n\nfunction arrowClicked(event: MouseEvent): void {\n if (get(isOpen)) {\n set(isOpen, false);\n event.stopPropagation();\n }\n}\n\nfunction openMenu(): void {\n set(isOpen, true);\n}\n\nfunction closeMenu(): void {\n set(isOpen, false);\n}\n\n// Optimize options watcher with shallow comparison first\nwatch(() => options, (curr, old) => {\n if (curr === old || customValue)\n return;\n\n // Only do deep comparison if reference changed\n if (isEqual(curr, old))\n return;\n\n setSelected(get(value));\n});\n\nconst menuFloatingOptions = computed<FloatingOptions>(() => ({\n placement: Placement.bottomStart,\n ...menuOptions?.options,\n}));\n\ndefineExpose({\n closeMenu,\n focus: focusSetInputFocus,\n openMenu,\n setSelectionRange,\n});\n</script>\n\n<template>\n <RuiMenu\n v-model=\"isOpen\"\n v-bind=\"{ ...getRootAttrs($attrs, []), ...menuOptions }\"\n :class=\"ui.wrapper({ class: cn($attrs.class) })\"\n :options=\"menuFloatingOptions\"\n :close-on-content-click=\"false\"\n :full-width=\"true\"\n :persist-on-activator-click=\"true\"\n :menu-class=\"[\n { hidden: optionsWithSelectedHidden.length === 0 && customValue && !slots['no-data'] },\n menuOptions?.menuClass,\n ]\"\n :error-messages=\"errorMessages\"\n :success-messages=\"successMessages\"\n :hint=\"hint\"\n :dense=\"dense\"\n :show-details=\"!hideDetails\"\n :disabled=\"disabled\"\n disable-auto-focus\n >\n <template #activator=\"{ attrs, open, hasError: slotHasError, hasSuccess: slotHasSuccess }\">\n <slot\n name=\"activator\"\n v-bind=\"{ disabled, value, variant, readOnly, attrs, open, hasError: slotHasError, hasSuccess: slotHasSuccess }\"\n >\n <div\n ref=\"activator\"\n :class=\"ui.activator({ class: cn(classNames?.label) ?? labelClass })\"\n v-bind=\"{\n ...getNonRootAttrs($attrs, ['onClick', 'class']),\n ...(readOnly ? {} : attrs),\n }\"\n role=\"combobox\"\n :aria-expanded=\"open\"\n :aria-disabled=\"disabled || undefined\"\n :aria-readonly=\"readOnly || undefined\"\n :aria-required=\"required || undefined\"\n :aria-busy=\"loading || undefined\"\n data-id=\"activator\"\n :aria-invalid=\"hasError\"\n :tabindex=\"disabled || readOnly ? -1 : 0\"\n @mouseenter=\"isHovered = true\"\n @mouseleave=\"isHovered = false\"\n @click=\"focusSetInputFocus()\"\n @focus=\"focusOnActivatorFocused()\"\n @keydown.enter=\"onEnter($event)\"\n @keydown.tab=\"onTab($event)\"\n @keydown.left=\"moveSelectedValueHighlight($event, false)\"\n @keydown.right=\"moveSelectedValueHighlight($event, true)\"\n @keydown.up.prevent=\"moveHighlight(true)\"\n @keydown.down.prevent=\"moveHighlight(false)\"\n @keydown.home.prevent=\"highlightedIndex = 0\"\n @keydown.end.prevent=\"highlightedIndex = optionsWithSelectedHidden.length - 1\"\n >\n <span\n v-if=\"(outlined || (!valueSet && !searchInputFocused)) && !placeholderSlotActive\"\n :class=\"[\n ui.label(),\n { 'pr-2': !valueSet && !open && outlined },\n ]\"\n >\n <slot\n name=\"activator.label\"\n v-bind=\"{ value }\"\n >\n {{ label }}\n </slot>\n <span\n v-if=\"required\"\n :class=\"ui.required()\"\n >\n ﹡\n </span>\n </span>\n <div\n data-id=\"value\"\n :class=\"ui.value()\"\n >\n <!--\n Placeholder slot occupies the same flex space the input takes\n when focused, so transitioning into the focused state doesn't\n shift the activator's content. pointer-events-none is required\n so clicks pass through to the activator (otherwise the slot\n swallows the first click and the menu needs two taps to open).\n -->\n <div\n v-if=\"!valueSet && !searchInputFocused && slots.placeholder\"\n data-id=\"placeholder\"\n class=\"flex-1 min-w-0 pointer-events-none\"\n >\n <slot\n name=\"placeholder\"\n v-bind=\"{ disabled, readOnly }\"\n />\n </div>\n <template\n v-for=\"(item, i) in value\"\n :key=\"getIdentifier(item)\"\n >\n <RuiChip\n v-if=\"chips\"\n :key=\"getTextToken(getIdentifier(item))\"\n tabindex=\"-1\"\n :size=\"dense ? 'sm' : 'md'\"\n closeable\n :class=\"{ 'leading-3': dense }\"\n clickable\n v-bind=\"chipAttrs(item, i)\"\n >\n <div class=\"flex\">\n <slot\n name=\"selection.prepend\"\n :index=\"i\"\n v-bind=\"{ item }\"\n />\n <slot\n :index=\"i\"\n name=\"selection\"\n v-bind=\"{ item, chipAttrs: chipAttrs(item, i) }\"\n >\n {{ getText(item) }}\n </slot>\n </div>\n </RuiChip>\n <div\n v-else-if=\"\n multiple\n || (!searchInputFocused && (slots['selection.prepend'] || slots.selection))\n \"\n :class=\"hideSelectionWrapper ? 'contents' : 'flex'\"\n >\n <slot\n name=\"selection.prepend\"\n :index=\"i\"\n v-bind=\"{ item }\"\n />\n <slot\n v-if=\"multiple || slots.selection\"\n :index=\"i\"\n name=\"selection\"\n v-bind=\"{ item, chipAttrs: chipAttrs(item, i) }\"\n >\n {{ getText(item) }}\n </slot>\n </div>\n </template>\n <input\n ref=\"textInput\"\n :disabled=\"disabled\"\n :value=\"internalSearch\"\n class=\"bg-transparent outline-none\"\n type=\"text\"\n :placeholder=\"usedPlaceholder\"\n :class=\"[focusInputClass, { hidden: hideSearchInput }]\"\n :aria-invalid=\"hasError\"\n aria-autocomplete=\"list\"\n @keydown.delete=\"onInputDeletePressed()\"\n @input.stop=\"updateSearchInput($event)\"\n @focus=\"focusOnInputFocused()\"\n />\n </div>\n\n <RuiButton\n v-if=\"clearable && valueSet && !disabled\"\n variant=\"text\"\n icon\n size=\"sm\"\n tabindex=\"-1\"\n color=\"error\"\n data-id=\"clear\"\n :class=\"[\n ui.clear(),\n focusAnyFocused && '!visible',\n { 'mr-2': !dense },\n ]\"\n @click.stop.prevent=\"clear()\"\n >\n <RuiIcon\n name=\"lu-x\"\n size=\"18\"\n />\n </RuiButton>\n\n <span\n :class=\"ui.iconWrapper()\"\n @click=\"arrowClicked($event)\"\n >\n <RuiIcon\n :class=\"ui.icon()\"\n :size=\"dense ? 16 : 24\"\n name=\"lu-chevron-down\"\n />\n </span>\n\n <RuiProgress\n v-if=\"loading\"\n :class=\"ui.progress()\"\n color=\"primary\"\n thickness=\"3\"\n variant=\"indeterminate\"\n />\n </div>\n <fieldset\n v-if=\"outlined\"\n :class=\"ui.fieldset()\"\n >\n <legend :class=\"ui.legend()\">\n {{ legendText }}\n </legend>\n </fieldset>\n </slot>\n </template>\n <template #default=\"{ width }\">\n <div ref=\"menuWrapperRef\">\n <div\n v-if=\"optionsWithSelectedHidden.length > 0\"\n :ref=\"containerProps.ref\"\n :class=\"ui.menu({ class: cn(classNames?.menu) ?? menuClass })\"\n :style=\"[containerProps.style, { width: `${width}px`, minWidth: menuWidth, minHeight: `${menuMinHeight}px` }]\"\n @scroll=\"containerProps.onScroll\"\n @keydown.up.prevent=\"moveHighlight(true)\"\n @keydown.down.prevent=\"moveHighlight(false)\"\n >\n <div\n v-if=\"isGrouped\"\n ref=\"menuRef\"\n >\n <template\n v-for=\"(bucket, bucketIndex) in groupedOptions\"\n :key=\"bucket.group || `group-${bucketIndex}`\"\n >\n <div\n class=\"sticky top-0 z-10 bg-white dark:bg-rui-grey-900\"\n data-id=\"group-header\"\n >\n <slot\n name=\"group-header\"\n v-bind=\"{ group: bucket.group, items: bucket.items }\"\n >\n <div class=\"px-3 py-1 text-xs uppercase tracking-wide text-rui-text-secondary\">\n {{ bucket.group }}\n </div>\n </slot>\n </div>\n <RuiButton\n v-for=\"item in bucket.items\"\n :key=\"getIdentifier(item)?.toString()\"\n :active=\"isActiveItem(item)\"\n :aria-selected=\"isActiveItem(item)\"\n :size=\"dense ? 'sm' : undefined\"\n :disabled=\"isItemDisabled(item)\"\n tabindex=\"0\"\n variant=\"list\"\n :data-highlighted=\"optionsWithSelectedHidden.indexOf(item) === highlightedIndex\"\n :data-disabled=\"isItemDisabled(item) || undefined\"\n :class=\"{\n [highlightedClass]: !isActiveItem(item) && optionsWithSelectedHidden.indexOf(item) === highlightedIndex,\n }\"\n @click=\"!isItemDisabled(item) && setValue(item)\"\n >\n <template #prepend>\n <slot\n name=\"item.prepend\"\n v-bind=\"{ disabled: isItemDisabled(item), item, active: isActiveItem(item) }\"\n />\n </template>\n <slot\n name=\"item\"\n v-bind=\"{ disabled: isItemDisabled(item), item, active: isActiveItem(item) }\"\n >\n {{ getText(item) }}\n </slot>\n <template #append>\n <slot\n name=\"item.append\"\n v-bind=\"{ disabled: isItemDisabled(item), item, active: isActiveItem(item) }\"\n />\n </template>\n </RuiButton>\n </template>\n </div>\n <div\n v-else\n v-bind=\"wrapperProps\"\n ref=\"menuRef\"\n >\n <RuiButton\n v-for=\"{ item, _index } in renderedData\"\n :key=\"getIdentifier(item)?.toString()\"\n :active=\"isActiveItem(item)\"\n :aria-selected=\"isActiveItem(item)\"\n :size=\"dense ? 'sm' : undefined\"\n :disabled=\"isItemDisabled(item)\"\n tabindex=\"0\"\n variant=\"list\"\n :data-highlighted=\"highlightedIndex === _index\"\n :data-disabled=\"isItemDisabled(item) || undefined\"\n :class=\"{\n [highlightedClass]: !isActiveItem(item) && highlightedIndex === _index,\n }\"\n @click=\"!isItemDisabled(item) && setValue(item)\"\n >\n <template #prepend>\n <slot\n name=\"item.prepend\"\n v-bind=\"{ disabled: isItemDisabled(item), item, active: isActiveItem(item) }\"\n />\n </template>\n <slot\n name=\"item\"\n v-bind=\"{ disabled: isItemDisabled(item), item, active: isActiveItem(item) }\"\n >\n {{ getText(item) }}\n </slot>\n <template #append>\n <slot\n name=\"item.append\"\n v-bind=\"{ disabled: isItemDisabled(item), item, active: isActiveItem(item) }\"\n />\n </template>\n </RuiButton>\n </div>\n </div>\n\n <div\n v-else-if=\"!hideNoData\"\n :style=\"{ width: `${width}px`, minWidth: menuWidth }\"\n :class=\"classNames?.menu ?? menuClass\"\n >\n <slot name=\"no-data\">\n <div\n v-if=\"!customValue\"\n class=\"p-4\"\n data-id=\"no-data\"\n >\n {{ noDataText }}\n </div>\n </slot>\n </div>\n <!--\n The scroll container above reserves ~15px on the right for the\n scrollbar gutter. We render an equivalent right-padding here so the\n footer's content aligns with the option rows' content rather than\n the menu's outer edge. `pr-[var(--rui-scrollbar-gutter,15px)]`\n lets consumers override if their theme reserves a different width.\n -->\n <div\n v-if=\"slots.footer\"\n class=\"bg-white dark:bg-rui-grey-900 border-t border-black/[0.12] dark:border-white/[0.12] pr-[var(--rui-scrollbar-gutter,15px)] -mb-2\"\n data-id=\"footer\"\n >\n <slot name=\"footer\" />\n </div>\n </div>\n </template>\n </RuiMenu>\n</template>\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"RuiAutoComplete.vue_vue_type_script_setup_true_lang.js","names":["$attrs"],"sources":["../../../../src/components/forms/auto-complete/RuiAutoComplete.vue"],"sourcesContent":["<script lang=\"ts\" setup generic=\"TValue, TItem\">\nimport type { VueClassValue } from '@/types/class-value';\nimport RuiButton from '@/components/buttons/button/RuiButton.vue';\nimport RuiChip from '@/components/chips/RuiChip.vue';\nimport { autoCompleteStyles, type AutoCompleteVariant } from '@/components/forms/auto-complete/auto-complete-styles';\nimport RuiIcon from '@/components/icons/RuiIcon.vue';\nimport RuiMenu, { type MenuProps } from '@/components/overlays/menu/RuiMenu.vue';\nimport RuiProgress from '@/components/progress/RuiProgress.vue';\nimport {\n type GroupBy,\n type ItemDisabled,\n type KeyOfType,\n useDropdownMenu,\n useDropdownOptionProperty,\n} from '@/composables/dropdown-menu';\nimport { type FloatingOptions, Placement } from '@/composables/floating';\nimport {\n useAutoCompleteFocus,\n useAutoCompleteKeyboardNavigation,\n useAutoCompleteSearch,\n useAutoCompleteValue,\n} from '@/composables/forms/auto-complete';\nimport { useFormTextDetail } from '@/utils/form-text-detail';\nimport { getNonRootAttrs, getRootAttrs, getTextToken } from '@/utils/helpers';\nimport { isEqual } from '@/utils/is-equal';\nimport { cn } from '@/utils/tv';\n\nexport type AutoCompleteModelValue<TValue> =\n TValue extends Array<infer U> ? U[] : TValue | undefined;\n\nexport interface RuiAutoCompleteClassNames {\n root?: VueClassValue;\n label?: VueClassValue;\n menu?: VueClassValue;\n}\n\nexport interface AutoCompleteProps<TValue, TItem> {\n options?: TItem[];\n keyAttr?: KeyOfType<TItem, TValue extends Array<infer U> ? U : TValue>;\n textAttr?: keyof TItem;\n disabled?: boolean;\n loading?: boolean;\n readOnly?: boolean;\n dense?: boolean;\n clearable?: boolean;\n label?: string;\n menuOptions?: MenuProps;\n classNames?: RuiAutoCompleteClassNames;\n /** @deprecated Use `classNames.label` instead */\n labelClass?: string;\n /** @deprecated Use `classNames.menu` instead */\n menuClass?: string;\n prependWidth?: number;\n appendWidth?: number;\n itemHeight?: number;\n variant?: AutoCompleteVariant;\n hint?: string;\n errorMessages?: string | string[];\n successMessages?: string | string[];\n hideDetails?: boolean;\n autoSelectFirst?: boolean;\n chips?: boolean;\n noFilter?: boolean;\n hideNoData?: boolean;\n noDataText?: string;\n /**\n * Custom search predicate. Receives the resolved group label as a third\n * argument when `groupBy` is set, so callers don't have to re-run the\n * `groupBy` resolver themselves.\n */\n filter?: (item: TItem, queryText: string, group?: string) => boolean;\n hideSelected?: boolean;\n placeholder?: string;\n returnObject?: boolean;\n customValue?: boolean;\n hideCustomValue?: boolean;\n required?: boolean;\n hideSearchInput?: boolean;\n hideSelectionWrapper?: boolean;\n groupBy?: GroupBy<TItem>;\n itemDisabled?: ItemDisabled<TItem>;\n /**\n * When true and `groupBy` is set, the default search predicate also matches\n * against the resolved group label. Items belonging to a group whose label\n * matches the query stay visible (group header included), even when the\n * items themselves don't match. No effect when a custom `filter` is supplied\n * or when `groupBy` is undefined.\n */\n searchIncludesGroupLabel?: boolean;\n}\n\ndefineOptions({\n name: 'RuiAutoComplete',\n inheritAttrs: false,\n});\n\nconst modelValue = defineModel<AutoCompleteModelValue<TValue>>({ required: true });\n\nconst searchInputModel = defineModel<string>('searchInput', { default: '' });\n\nconst {\n options = [],\n disabled = false,\n loading = false,\n readOnly = false,\n dense = false,\n clearable = false,\n hideDetails = false,\n chips = false,\n label = 'Select',\n menuOptions,\n classNames,\n labelClass,\n menuClass,\n variant = 'default',\n hint,\n keyAttr,\n textAttr,\n itemHeight,\n errorMessages = [],\n successMessages = [],\n autoSelectFirst = false,\n noFilter = false,\n hideNoData = false,\n noDataText = 'No data available',\n filter,\n hideSelected = false,\n placeholder = '',\n returnObject = false,\n customValue = false,\n hideCustomValue = false,\n required = false,\n hideSearchInput = false,\n hideSelectionWrapper = false,\n groupBy,\n itemDisabled,\n searchIncludesGroupLabel = false,\n} = defineProps<AutoCompleteProps<TValue, TItem>>();\n\nconst slots = defineSlots<{\n 'activator'?: (props: {\n disabled: boolean;\n value: TItem[];\n variant: string;\n readOnly: boolean;\n attrs: Record<string, unknown>;\n open: boolean;\n hasError: boolean;\n hasSuccess: boolean;\n }) => any;\n 'activator.label'?: (props: { value: TItem[] }) => any;\n 'selection.prepend'?: (props: { index: number; item: TItem }) => any;\n 'selection'?: (props: { index: number; item: TItem; chipAttrs: Record<string, unknown> }) => any;\n 'item.prepend'?: (props: { disabled: boolean; item: TItem; active: boolean }) => any;\n 'item'?: (props: { disabled: boolean; item: TItem; active: boolean }) => any;\n 'item.append'?: (props: { disabled: boolean; item: TItem; active: boolean }) => any;\n 'group-header'?: (props: { group: string; items: TItem[] }) => any;\n 'no-data'?: () => any;\n 'placeholder'?: (props: { disabled: boolean; readOnly: boolean }) => any;\n 'footer'?: () => any;\n}>();\n\nconst { getText, getIdentifier } = useDropdownOptionProperty<TValue, TItem>({\n keyAttr,\n textAttr,\n});\n\nconst textInput = useTemplateRef<HTMLInputElement>('textInput');\nconst activator = useTemplateRef<HTMLDivElement>('activator');\nconst menuRef = useTemplateRef<HTMLDivElement>('menuRef');\nconst menuWrapperRef = useTemplateRef<HTMLDivElement>('menuWrapperRef');\n\nconst { focused: activatorFocusedWithin } = useFocusWithin(activator);\nconst { focused: menuWrapperFocusedWithin } = useFocusWithin(menuWrapperRef);\nconst { focused: searchInputFocused } = useFocus(textInput);\nconst { focused: activatorFocused } = useFocus(activator);\n\nconst {\n internalSearch,\n filteredOptions,\n justOpened,\n updateInternalSearch,\n textValueToProperValue,\n} = useAutoCompleteSearch<TItem>(\n () => options,\n searchInputModel,\n {\n keyAttr: () => keyAttr,\n textAttr: () => textAttr,\n noFilter: () => noFilter,\n filter: () => filter,\n customValue: () => customValue,\n hideCustomValue: () => hideCustomValue,\n returnObject: () => returnObject,\n groupBy: () => groupBy,\n searchIncludesGroupLabel: () => searchIncludesGroupLabel,\n },\n);\n\nconst isOpen = ref<boolean>(false);\nconst isHovered = ref<boolean>(false);\n\n// Calculate multiple from modelValue directly to avoid circular dependency\nconst multiple = computed<boolean>(() => Array.isArray(get(modelValue)));\n\nconst shouldApplyValueAsSearch = computed<boolean>(\n () => !(slots.selection || get(multiple) || chips),\n);\n\nconst { resolveIn, value, setSelected } = useAutoCompleteValue<AutoCompleteModelValue<TValue>, TItem>(\n modelValue,\n () => options,\n {\n keyAttr: () => keyAttr,\n returnObject: () => returnObject,\n customValue: () => customValue,\n },\n {\n getIdentifier,\n getText,\n textValueToProperValue,\n shouldApplyValueAsSearch,\n isOpen,\n multiple,\n updateInternalSearch,\n },\n);\n\nconst resolvedItemHeight = itemHeight ?? (dense ? 30 : 48);\n\nconst {\n containerProps,\n wrapperProps,\n renderedData,\n menuWidth,\n isActiveItem,\n itemIndexInValue,\n highlightedIndex,\n moveHighlight,\n applyHighlighted,\n optionsWithSelectedHidden,\n userNavigated,\n groupedOptions,\n isGrouped,\n isItemDisabled,\n} = useDropdownMenu<TValue, TItem>({\n itemHeight: resolvedItemHeight,\n keyAttr,\n textAttr,\n options: filteredOptions,\n dense: () => dense,\n value,\n menuRef,\n setValue,\n autoSelectFirst,\n hideSelected,\n isOpen,\n getText,\n getIdentifier,\n groupBy: () => groupBy,\n itemDisabled: () => itemDisabled,\n});\n\nconst {\n focusedValueIndex,\n moveSelectedValueHighlight,\n onEnter,\n onInputDeletePressed,\n onTab,\n setValueFocus,\n} = useAutoCompleteKeyboardNavigation<TItem>(\n {\n chips: () => chips,\n customValue: () => customValue,\n multiple,\n },\n {\n activator,\n applyHighlighted,\n clear,\n filteredOptions,\n getText,\n highlightedIndex,\n internalSearch,\n isOpen,\n removeValue: (item: TItem): void => { setValue(item); },\n searchInputFocused,\n setSearchAsValue,\n userNavigated,\n value,\n },\n);\n\nconst {\n anyFocused: focusAnyFocused,\n inputClass: focusInputClass,\n onActivatorFocused: focusOnActivatorFocused,\n onInputFocused: focusOnInputFocused,\n setInputFocus: focusSetInputFocus,\n} = useAutoCompleteFocus(\n {\n customValue: () => customValue,\n disabled: () => disabled,\n shouldApplyValueAsSearch,\n },\n {\n activatorFocused,\n activatorFocusedWithin,\n focusedValueIndex,\n internalSearch,\n isOpen,\n justOpened,\n menuWrapperFocusedWithin,\n searchInputFocused,\n setSearchAsValue,\n textInput,\n updateInternalSearch,\n },\n);\n\nconst menuMinHeight = computed<number>(\n () => Math.min(5, get(optionsWithSelectedHidden).length) * resolvedItemHeight,\n);\n\nconst { hasError, hasSuccess } = useFormTextDetail(\n () => errorMessages,\n () => successMessages,\n);\n\nconst valueSet = computed<boolean>(() => get(value).length > 0);\n\n// True when the consumer's #placeholder slot is taking over the resting\n// content area, so the outlined/resting label must hide to avoid overlapping\n// the slot content.\nconst placeholderSlotActive = computed<boolean>(() => Boolean(slots.placeholder) && !get(valueSet) && !get(searchInputFocused));\n\nconst usedPlaceholder = computed<string>(() => {\n if (get(searchInputFocused))\n return placeholder;\n return '';\n});\n\nconst outlined = computed<boolean>(() => variant === 'outlined');\nconst float = computed<boolean>(() => (get(isOpen) || get(valueSet) || get(searchInputFocused)) && get(outlined));\n\nconst legendText = computed<string>(() => {\n if (!get(float) || !label)\n return '';\n return required ? `${label} ﹡` : label;\n});\n\nconst ui = computed<ReturnType<typeof autoCompleteStyles>>(() => autoCompleteStyles({\n filled: variant === 'filled',\n outlined: get(outlined),\n float: get(float),\n opened: get(isOpen),\n hovered: get(isHovered),\n dense,\n disabled,\n readonly: readOnly,\n hasError: get(hasError),\n hasSuccess: get(hasSuccess) && !get(hasError),\n}));\n\nconst highlightedClass = autoCompleteStyles({}).highlighted();\n\nfunction updateSearchInput(event: Event): void {\n const target = event.target;\n if (!(target instanceof HTMLInputElement))\n return;\n\n const value = target.value;\n set(isOpen, true);\n updateInternalSearch(value);\n set(justOpened, false);\n}\n\nasync function setValue(val: TItem, skipRefocused = false): Promise<void> {\n const isMultiple = get(multiple);\n\n if (isMultiple) {\n const newValue = [...get(value)];\n const indexInValue = itemIndexInValue(val);\n if (indexInValue === -1) {\n updateInternalSearch();\n newValue.push(val);\n }\n else {\n newValue.splice(indexInValue, 1);\n }\n set(value, newValue);\n }\n else {\n if (get(shouldApplyValueAsSearch))\n updateInternalSearch(getText(val));\n else updateInternalSearch();\n\n set(value, [val]);\n }\n\n if (!isMultiple) {\n if (!skipRefocused) {\n set(activatorFocused, true);\n get(activator)?.focus();\n await nextTick(() => {\n set(isOpen, false);\n });\n }\n else {\n set(isOpen, false);\n }\n }\n else if (!skipRefocused) {\n set(searchInputFocused, true);\n }\n}\n\nfunction setSearchAsValue(): void {\n const searchToBeValue = get(internalSearch);\n if (!searchToBeValue)\n return;\n\n const newValue: TItem = textValueToProperValue(searchToBeValue);\n setValue(newValue, true);\n}\n\nfunction clear(): void {\n updateInternalSearch();\n set(modelValue, (Array.isArray(get(modelValue)) ? [] : undefined) as AutoCompleteModelValue<TValue>);\n}\n\nfunction chipAttrs(item: TItem, index: number): Record<string, unknown> {\n return {\n 'data-index': index,\n 'data-value': getIdentifier(item),\n 'onKeydown': (event: KeyboardEvent): void => {\n const { key } = event;\n if (['Backspace', 'Delete'].includes(key)) {\n event.stopPropagation();\n event.preventDefault();\n // Alt/Option + delete converts the chip back into editable search text\n // instead of removing it outright. Only meaningful when custom values\n // are accepted, since the restored text has to be re-selectable.\n if (event.altKey && customValue) {\n const text = getText(item) ?? '';\n setValue(item);\n updateInternalSearch(text);\n }\n else {\n setValue(item);\n }\n }\n },\n 'onClick': (e: MouseEvent): void => {\n e.stopPropagation();\n setValueFocus(index);\n },\n 'onClick:close': (): void => {\n setValue(item);\n },\n };\n}\n\nfunction setSelectionRange(start: number, end: number): void {\n set(searchInputFocused, true);\n get(textInput)?.setSelectionRange?.(start, end);\n}\n\nfunction arrowClicked(event: MouseEvent): void {\n if (get(isOpen)) {\n set(isOpen, false);\n event.stopPropagation();\n }\n}\n\nfunction openMenu(): void {\n set(isOpen, true);\n}\n\nfunction closeMenu(): void {\n set(isOpen, false);\n}\n\n// Optimize options watcher with shallow comparison first\nwatch(() => options, (curr, old) => {\n if (curr === old || customValue)\n return;\n\n // Only do deep comparison if reference changed\n if (isEqual(curr, old))\n return;\n\n // Only reconcile a selection the previous options could actually resolve. A value\n // that was already unresolvable is one the consumer set before its options arrived\n // (async lists start empty), so clearing it here would discard a legitimate value.\n if (!get(multiple) && resolveIn(old).length === 0)\n return;\n\n setSelected(get(value));\n});\n\nconst menuFloatingOptions = computed<FloatingOptions>(() => ({\n placement: Placement.bottomStart,\n ...menuOptions?.options,\n}));\n\ndefineExpose({\n closeMenu,\n focus: focusSetInputFocus,\n openMenu,\n setSelectionRange,\n});\n</script>\n\n<template>\n <RuiMenu\n v-model=\"isOpen\"\n v-bind=\"{ ...getRootAttrs($attrs, []), ...menuOptions }\"\n :class=\"ui.wrapper({ class: cn($attrs.class) })\"\n :options=\"menuFloatingOptions\"\n :close-on-content-click=\"false\"\n :full-width=\"true\"\n :persist-on-activator-click=\"true\"\n :menu-class=\"[\n { hidden: optionsWithSelectedHidden.length === 0 && customValue && !slots['no-data'] },\n menuOptions?.menuClass,\n ]\"\n :error-messages=\"errorMessages\"\n :success-messages=\"successMessages\"\n :hint=\"hint\"\n :dense=\"dense\"\n :show-details=\"!hideDetails\"\n :disabled=\"disabled\"\n disable-auto-focus\n >\n <template #activator=\"{ attrs, open, hasError: slotHasError, hasSuccess: slotHasSuccess }\">\n <slot\n name=\"activator\"\n v-bind=\"{ disabled, value, variant, readOnly, attrs, open, hasError: slotHasError, hasSuccess: slotHasSuccess }\"\n >\n <div\n ref=\"activator\"\n :class=\"ui.activator({ class: cn(classNames?.label) ?? labelClass })\"\n v-bind=\"{\n ...getNonRootAttrs($attrs, ['onClick', 'class']),\n ...(readOnly ? {} : attrs),\n }\"\n role=\"combobox\"\n :aria-expanded=\"open\"\n :aria-disabled=\"disabled || undefined\"\n :aria-readonly=\"readOnly || undefined\"\n :aria-required=\"required || undefined\"\n :aria-busy=\"loading || undefined\"\n data-id=\"activator\"\n :aria-invalid=\"hasError\"\n :tabindex=\"disabled || readOnly ? -1 : 0\"\n @mouseenter=\"isHovered = true\"\n @mouseleave=\"isHovered = false\"\n @click=\"focusSetInputFocus()\"\n @focus=\"focusOnActivatorFocused()\"\n @keydown.enter=\"onEnter($event)\"\n @keydown.tab=\"onTab($event)\"\n @keydown.left=\"moveSelectedValueHighlight($event, false)\"\n @keydown.right=\"moveSelectedValueHighlight($event, true)\"\n @keydown.up.prevent=\"moveHighlight(true)\"\n @keydown.down.prevent=\"moveHighlight(false)\"\n @keydown.home.prevent=\"highlightedIndex = 0\"\n @keydown.end.prevent=\"highlightedIndex = optionsWithSelectedHidden.length - 1\"\n >\n <span\n v-if=\"(outlined || (!valueSet && !searchInputFocused)) && !placeholderSlotActive\"\n :class=\"[\n ui.label(),\n { 'pr-2': !valueSet && !open && outlined },\n ]\"\n >\n <slot\n name=\"activator.label\"\n v-bind=\"{ value }\"\n >\n {{ label }}\n </slot>\n <span\n v-if=\"required\"\n :class=\"ui.required()\"\n >\n ﹡\n </span>\n </span>\n <div\n data-id=\"value\"\n :class=\"ui.value()\"\n >\n <!--\n Placeholder slot occupies the same flex space the input takes\n when focused, so transitioning into the focused state doesn't\n shift the activator's content. pointer-events-none is required\n so clicks pass through to the activator (otherwise the slot\n swallows the first click and the menu needs two taps to open).\n -->\n <div\n v-if=\"!valueSet && !searchInputFocused && slots.placeholder\"\n data-id=\"placeholder\"\n class=\"flex-1 min-w-0 pointer-events-none\"\n >\n <slot\n name=\"placeholder\"\n v-bind=\"{ disabled, readOnly }\"\n />\n </div>\n <template\n v-for=\"(item, i) in value\"\n :key=\"getIdentifier(item)\"\n >\n <RuiChip\n v-if=\"chips\"\n :key=\"getTextToken(getIdentifier(item))\"\n tabindex=\"-1\"\n :size=\"dense ? 'sm' : 'md'\"\n closeable\n :class=\"{ 'leading-3': dense }\"\n clickable\n v-bind=\"chipAttrs(item, i)\"\n >\n <div class=\"flex\">\n <slot\n name=\"selection.prepend\"\n :index=\"i\"\n v-bind=\"{ item }\"\n />\n <slot\n :index=\"i\"\n name=\"selection\"\n v-bind=\"{ item, chipAttrs: chipAttrs(item, i) }\"\n >\n {{ getText(item) }}\n </slot>\n </div>\n </RuiChip>\n <div\n v-else-if=\"\n multiple\n || (!searchInputFocused && (slots['selection.prepend'] || slots.selection))\n \"\n :class=\"hideSelectionWrapper ? 'contents' : 'flex'\"\n >\n <slot\n name=\"selection.prepend\"\n :index=\"i\"\n v-bind=\"{ item }\"\n />\n <slot\n v-if=\"multiple || slots.selection\"\n :index=\"i\"\n name=\"selection\"\n v-bind=\"{ item, chipAttrs: chipAttrs(item, i) }\"\n >\n {{ getText(item) }}\n </slot>\n </div>\n </template>\n <input\n ref=\"textInput\"\n :disabled=\"disabled\"\n :value=\"internalSearch\"\n class=\"bg-transparent outline-none\"\n type=\"text\"\n :placeholder=\"usedPlaceholder\"\n :class=\"[focusInputClass, { hidden: hideSearchInput }]\"\n :aria-invalid=\"hasError\"\n aria-autocomplete=\"list\"\n @keydown.delete=\"onInputDeletePressed()\"\n @input.stop=\"updateSearchInput($event)\"\n @focus=\"focusOnInputFocused()\"\n />\n </div>\n\n <RuiButton\n v-if=\"clearable && valueSet && !disabled\"\n variant=\"text\"\n icon\n size=\"sm\"\n tabindex=\"-1\"\n color=\"error\"\n data-id=\"clear\"\n :class=\"[\n ui.clear(),\n focusAnyFocused && '!visible',\n { 'mr-2': !dense },\n ]\"\n @click.stop.prevent=\"clear()\"\n >\n <RuiIcon\n name=\"lu-x\"\n size=\"18\"\n />\n </RuiButton>\n\n <span\n :class=\"ui.iconWrapper()\"\n @click=\"arrowClicked($event)\"\n >\n <RuiIcon\n :class=\"ui.icon()\"\n :size=\"dense ? 16 : 24\"\n name=\"lu-chevron-down\"\n />\n </span>\n\n <RuiProgress\n v-if=\"loading\"\n :class=\"ui.progress()\"\n color=\"primary\"\n thickness=\"3\"\n variant=\"indeterminate\"\n />\n </div>\n <fieldset\n v-if=\"outlined\"\n :class=\"ui.fieldset()\"\n >\n <legend :class=\"ui.legend()\">\n {{ legendText }}\n </legend>\n </fieldset>\n </slot>\n </template>\n <template #default=\"{ width }\">\n <div ref=\"menuWrapperRef\">\n <div\n v-if=\"optionsWithSelectedHidden.length > 0\"\n :ref=\"containerProps.ref\"\n :class=\"ui.menu({ class: cn(classNames?.menu) ?? menuClass })\"\n :style=\"[containerProps.style, { width: `${width}px`, minWidth: menuWidth, minHeight: `${menuMinHeight}px` }]\"\n @scroll=\"containerProps.onScroll\"\n @keydown.up.prevent=\"moveHighlight(true)\"\n @keydown.down.prevent=\"moveHighlight(false)\"\n >\n <div\n v-if=\"isGrouped\"\n ref=\"menuRef\"\n >\n <template\n v-for=\"(bucket, bucketIndex) in groupedOptions\"\n :key=\"bucket.group || `group-${bucketIndex}`\"\n >\n <div\n class=\"sticky top-0 z-10 bg-white dark:bg-rui-grey-900\"\n data-id=\"group-header\"\n >\n <slot\n name=\"group-header\"\n v-bind=\"{ group: bucket.group, items: bucket.items }\"\n >\n <div class=\"px-3 py-1 text-xs uppercase tracking-wide text-rui-text-secondary\">\n {{ bucket.group }}\n </div>\n </slot>\n </div>\n <RuiButton\n v-for=\"item in bucket.items\"\n :key=\"getIdentifier(item)?.toString()\"\n :active=\"isActiveItem(item)\"\n :aria-selected=\"isActiveItem(item)\"\n :size=\"dense ? 'sm' : undefined\"\n :disabled=\"isItemDisabled(item)\"\n tabindex=\"0\"\n variant=\"list\"\n :data-highlighted=\"optionsWithSelectedHidden.indexOf(item) === highlightedIndex\"\n :data-disabled=\"isItemDisabled(item) || undefined\"\n :class=\"{\n [highlightedClass]: !isActiveItem(item) && optionsWithSelectedHidden.indexOf(item) === highlightedIndex,\n }\"\n @click=\"!isItemDisabled(item) && setValue(item)\"\n >\n <template #prepend>\n <slot\n name=\"item.prepend\"\n v-bind=\"{ disabled: isItemDisabled(item), item, active: isActiveItem(item) }\"\n />\n </template>\n <slot\n name=\"item\"\n v-bind=\"{ disabled: isItemDisabled(item), item, active: isActiveItem(item) }\"\n >\n {{ getText(item) }}\n </slot>\n <template #append>\n <slot\n name=\"item.append\"\n v-bind=\"{ disabled: isItemDisabled(item), item, active: isActiveItem(item) }\"\n />\n </template>\n </RuiButton>\n </template>\n </div>\n <div\n v-else\n v-bind=\"wrapperProps\"\n ref=\"menuRef\"\n >\n <RuiButton\n v-for=\"{ item, _index } in renderedData\"\n :key=\"getIdentifier(item)?.toString()\"\n :active=\"isActiveItem(item)\"\n :aria-selected=\"isActiveItem(item)\"\n :size=\"dense ? 'sm' : undefined\"\n :disabled=\"isItemDisabled(item)\"\n tabindex=\"0\"\n variant=\"list\"\n :data-highlighted=\"highlightedIndex === _index\"\n :data-disabled=\"isItemDisabled(item) || undefined\"\n :class=\"{\n [highlightedClass]: !isActiveItem(item) && highlightedIndex === _index,\n }\"\n @click=\"!isItemDisabled(item) && setValue(item)\"\n >\n <template #prepend>\n <slot\n name=\"item.prepend\"\n v-bind=\"{ disabled: isItemDisabled(item), item, active: isActiveItem(item) }\"\n />\n </template>\n <slot\n name=\"item\"\n v-bind=\"{ disabled: isItemDisabled(item), item, active: isActiveItem(item) }\"\n >\n {{ getText(item) }}\n </slot>\n <template #append>\n <slot\n name=\"item.append\"\n v-bind=\"{ disabled: isItemDisabled(item), item, active: isActiveItem(item) }\"\n />\n </template>\n </RuiButton>\n </div>\n </div>\n\n <div\n v-else-if=\"!hideNoData\"\n :style=\"{ width: `${width}px`, minWidth: menuWidth }\"\n :class=\"classNames?.menu ?? menuClass\"\n >\n <slot name=\"no-data\">\n <div\n v-if=\"!customValue\"\n class=\"p-4\"\n data-id=\"no-data\"\n >\n {{ noDataText }}\n </div>\n </slot>\n </div>\n <!--\n The scroll container above reserves ~15px on the right for the\n scrollbar gutter. We render an equivalent right-padding here so the\n footer's content aligns with the option rows' content rather than\n the menu's outer edge. `pr-[var(--rui-scrollbar-gutter,15px)]`\n lets consumers override if their theme reserves a different width.\n -->\n <div\n v-if=\"slots.footer\"\n class=\"bg-white dark:bg-rui-grey-900 border-t border-black/[0.12] dark:border-white/[0.12] pr-[var(--rui-scrollbar-gutter,15px)] -mb-2\"\n data-id=\"footer\"\n >\n <slot name=\"footer\" />\n </div>\n </div>\n </template>\n </RuiMenu>\n</template>\n"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"RuiDataTableCell.js","names":[],"sources":["../../../../src/components/tables/data-table/RuiDataTableCell.vue"],"sourcesContent":["<script lang=\"ts\" setup generic=\"T extends object\">\nimport type { TableColumn } from '@/components/tables/RuiTableHead.vue';\nimport { useDataTableColumns, useDataTableExpansion, useDataTableStyling } from '@/components/tables/data-table/context';\nimport RuiExpandButton from '@/components/tables/RuiExpandButton.vue';\n\nconst { column } = defineProps<{\n column: TableColumn<T>;\n row: T;\n index: number;\n rowId?: T[keyof T];\n}>();\n\ndefineSlots<{\n default?: (props: { column: TableColumn<T>; row: T; index: number }) => any;\n}>();\n\nconst { cellValue, columnAttr } = useDataTableColumns<T>();\nconst { isExpanded, onToggleExpand } = useDataTableExpansion<T>();\nconst { isMobile } = useDataTableStyling();\n\nconst mobileLabel = computed<string>(() => {\n const label = column[columnAttr];\n return label === undefined || label === null ? '' : String(label);\n});\nconst showMobileLabel = computed<boolean>(() => get(isMobile) && column.key !== 'expand' && get(mobileLabel).length > 0);\n</script>\n\n<template>\n <td\n :class=\"[\n column.tdClass,\n column.cellClass,\n isMobile ? 'flex items-start justify-between gap-4 text-right' : '',\n ]\"\n :colspan=\"column.colspan ?? 1\"\n :rowspan=\"column.rowspan ?? 1\"\n >\n <span\n v-if=\"showMobileLabel\"\n aria-hidden=\"true\"\n class=\"shrink-0 text-left font-medium text-rui-text-secondary\"\n data-id=\"cell-label\"\n >\n {{ mobileLabel }}\n </span>\n <div :class=\"isMobile ? 'min-w-0 ml-auto' : 'contents'\">\n <slot\n :column=\"column\"\n :row=\"row\"\n :index=\"index\"\n >\n <RuiExpandButton\n v-if=\"column.key === 'expand'\"\n :expanded=\"rowId !== undefined && isExpanded(rowId)\"\n @click=\"onToggleExpand(row)\"\n />\n <template v-else>\n {{ cellValue(row, column.key) }}\n </template>\n </slot>\n </div>\n </td>\n</template>\n"],"mappings":""}
|
|
1
|
+
{"version":3,"file":"RuiDataTableCell.js","names":[],"sources":["../../../../src/components/tables/data-table/RuiDataTableCell.vue"],"sourcesContent":["<script lang=\"ts\" setup generic=\"T extends object\">\nimport type { TableColumn } from '@/components/tables/RuiTableHead.vue';\nimport { useDataTableColumns, useDataTableExpansion, useDataTableStyling } from '@/components/tables/data-table/context';\nimport RuiExpandButton from '@/components/tables/RuiExpandButton.vue';\n\nconst { column } = defineProps<{\n column: TableColumn<T>;\n row: T;\n index: number;\n rowId?: T[keyof T];\n}>();\n\ndefineSlots<{\n default?: (props: { column: TableColumn<T>; row: T; index: number }) => any;\n}>();\n\nconst { cellValue, columnAttr, itemSlotKeys } = useDataTableColumns<T>();\nconst { isExpanded, onToggleExpand } = useDataTableExpansion<T>();\nconst { isMobile } = useDataTableStyling();\n\n// The built-in toggle is only a fallback for consumers that do not drive the\n// expand column themselves. A consumer that provides `#item.expand` owns the\n// decision per row, including rendering nothing for a row that cannot expand,\n// so slot emptiness must not fall back to a toggle they deliberately withheld.\nconst ownsExpandColumn = computed<boolean>(() => itemSlotKeys.has('expand'));\n\nconst mobileLabel = computed<string>(() => {\n const label = column[columnAttr];\n return label === undefined || label === null ? '' : String(label);\n});\nconst showMobileLabel = computed<boolean>(() => get(isMobile) && column.key !== 'expand' && get(mobileLabel).length > 0);\n</script>\n\n<template>\n <td\n :class=\"[\n column.tdClass,\n column.cellClass,\n isMobile ? 'flex items-start justify-between gap-4 text-right' : '',\n ]\"\n :colspan=\"column.colspan ?? 1\"\n :rowspan=\"column.rowspan ?? 1\"\n >\n <span\n v-if=\"showMobileLabel\"\n aria-hidden=\"true\"\n class=\"shrink-0 text-left font-medium text-rui-text-secondary\"\n data-id=\"cell-label\"\n >\n {{ mobileLabel }}\n </span>\n <div :class=\"isMobile ? 'min-w-0 ml-auto' : 'contents'\">\n <slot\n :column=\"column\"\n :row=\"row\"\n :index=\"index\"\n >\n <RuiExpandButton\n v-if=\"column.key === 'expand' && !ownsExpandColumn\"\n :expanded=\"rowId !== undefined && isExpanded(rowId)\"\n @click=\"onToggleExpand(row)\"\n />\n <template v-else-if=\"column.key !== 'expand'\">\n {{ cellValue(row, column.key) }}\n </template>\n </slot>\n </div>\n </td>\n</template>\n"],"mappings":""}
|