@rotki/ui-library 3.0.0-beta.5 → 3.1.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/accordions/accordion/RuiAccordion.js.map +1 -1
- package/dist/components/accordions/accordion/RuiAccordion.vue_vue_type_script_setup_true_lang.js +13 -2
- package/dist/components/accordions/accordion/RuiAccordion.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/alerts/RuiAlert.vue.d.ts +2 -2
- package/dist/components/buttons/button/RuiButton.js.map +1 -1
- package/dist/components/buttons/button/RuiButton.vue.d.ts +7 -1
- package/dist/components/buttons/button/RuiButton.vue_vue_type_script_setup_true_lang.js +4 -2
- package/dist/components/buttons/button/RuiButton.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/buttons/button/button-props.d.ts +3 -0
- package/dist/components/buttons/button/button-props.js +2 -1
- package/dist/components/buttons/button/button-props.js.map +1 -1
- package/dist/components/buttons/button/button-styles.d.ts +75 -0
- package/dist/components/buttons/button/button-styles.js +135 -4
- package/dist/components/buttons/button/button-styles.js.map +1 -1
- package/dist/components/cards/RuiCard.js.map +1 -1
- package/dist/components/cards/RuiCard.vue.d.ts +6 -0
- package/dist/components/cards/RuiCard.vue_vue_type_script_setup_true_lang.js +14 -2
- package/dist/components/cards/RuiCard.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/chips/RuiChip.js.map +1 -1
- package/dist/components/chips/RuiChip.vue_vue_type_script_setup_true_lang.js +14 -0
- package/dist/components/chips/RuiChip.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/chips/chip-props.d.ts +2 -0
- package/dist/components/chips/chip-props.js +3 -0
- package/dist/components/chips/chip-props.js.map +1 -1
- package/dist/components/color-picker/RuiColorInput.js.map +1 -1
- package/dist/components/color-picker/RuiColorInput.vue_vue_type_script_setup_true_lang.js +2 -2
- package/dist/components/color-picker/RuiColorInput.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/date-time-picker/RuiDateTimePicker.js.map +1 -1
- package/dist/components/date-time-picker/RuiDateTimePicker.vue.d.ts +4 -0
- package/dist/components/date-time-picker/RuiDateTimePicker.vue_vue_type_script_setup_true_lang.js +6 -2
- package/dist/components/date-time-picker/RuiDateTimePicker.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/date-time-picker/RuiDateTimePickerMenu.js.map +1 -1
- package/dist/components/date-time-picker/RuiDateTimePickerMenu.vue_vue_type_script_setup_true_lang.js +1 -1
- package/dist/components/date-time-picker/RuiDateTimePickerMenu.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/date-time-picker/RuiTimezoneSelect.js.map +1 -1
- package/dist/components/date-time-picker/RuiTimezoneSelect.vue.d.ts +4 -0
- package/dist/components/date-time-picker/RuiTimezoneSelect.vue_vue_type_script_setup_true_lang.js +3 -2
- package/dist/components/date-time-picker/RuiTimezoneSelect.vue_vue_type_script_setup_true_lang.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 +6 -2
- package/dist/components/forms/auto-complete/RuiAutoComplete.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/forms/auto-complete/RuiAutoCompleteSelection.js.map +1 -1
- package/dist/components/forms/auto-complete/RuiAutoCompleteSelection.vue_vue_type_script_setup_true_lang.js +9 -6
- package/dist/components/forms/auto-complete/RuiAutoCompleteSelection.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/forms/auto-complete/auto-complete-props.d.ts +4 -0
- package/dist/components/forms/category-picker/RuiCategoryPicker.js.map +1 -1
- package/dist/components/forms/category-picker/RuiCategoryPicker.vue.d.ts +4 -1
- package/dist/components/forms/category-picker/RuiCategoryPicker.vue_vue_type_script_setup_true_lang.js +7 -4
- package/dist/components/forms/category-picker/RuiCategoryPicker.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/forms/check-control-styles.d.ts +27 -0
- package/dist/components/forms/check-control-styles.js +26 -1
- package/dist/components/forms/check-control-styles.js.map +1 -1
- package/dist/components/forms/checkbox/RuiCheckbox.js.map +1 -1
- package/dist/components/forms/checkbox/RuiCheckbox.vue.d.ts +6 -0
- package/dist/components/forms/checkbox/RuiCheckbox.vue_vue_type_script_setup_true_lang.js +14 -4
- package/dist/components/forms/checkbox/RuiCheckbox.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/forms/checkbox/checkbox-group/RuiCheckboxGroup.js.map +1 -1
- package/dist/components/forms/checkbox/checkbox-group/RuiCheckboxGroup.vue.d.ts +4 -0
- package/dist/components/forms/checkbox/checkbox-group/RuiCheckboxGroup.vue_vue_type_script_setup_true_lang.js +7 -2
- package/dist/components/forms/checkbox/checkbox-group/RuiCheckboxGroup.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/forms/field-defaults/RuiFieldDefaults.js.map +1 -1
- package/dist/components/forms/field-defaults/RuiFieldDefaults.vue.d.ts +3 -0
- package/dist/components/forms/field-defaults/RuiFieldDefaults.vue_vue_type_script_setup_true_lang.js +8 -2
- package/dist/components/forms/field-defaults/RuiFieldDefaults.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/forms/file-upload/RuiFileUpload.js.map +1 -1
- package/dist/components/forms/file-upload/RuiFileUpload.vue.d.ts +4 -1
- package/dist/components/forms/file-upload/RuiFileUpload.vue_vue_type_script_setup_true_lang.js +10 -3
- package/dist/components/forms/file-upload/RuiFileUpload.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/forms/radio-button/radio/RuiRadio.js.map +1 -1
- package/dist/components/forms/radio-button/radio/RuiRadio.vue.d.ts +6 -0
- package/dist/components/forms/radio-button/radio/RuiRadio.vue_vue_type_script_setup_true_lang.js +14 -3
- package/dist/components/forms/radio-button/radio/RuiRadio.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/forms/radio-button/radio-group/RuiRadioGroup.js.map +1 -1
- package/dist/components/forms/radio-button/radio-group/RuiRadioGroup.vue.d.ts +4 -0
- package/dist/components/forms/radio-button/radio-group/RuiRadioGroup.vue_vue_type_script_setup_true_lang.js +7 -2
- package/dist/components/forms/radio-button/radio-group/RuiRadioGroup.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/forms/revealable-text-field/RuiRevealableTextField.js.map +1 -1
- package/dist/components/forms/revealable-text-field/RuiRevealableTextField.vue_vue_type_script_setup_true_lang.js +3 -2
- package/dist/components/forms/revealable-text-field/RuiRevealableTextField.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/forms/select/RuiMenuSelect.js.map +1 -1
- package/dist/components/forms/select/RuiMenuSelect.vue.d.ts +4 -0
- package/dist/components/forms/select/RuiMenuSelect.vue_vue_type_script_setup_true_lang.js +6 -2
- package/dist/components/forms/select/RuiMenuSelect.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/forms/slider/RuiSlider.js.map +1 -1
- package/dist/components/forms/slider/RuiSlider.vue.d.ts +4 -0
- package/dist/components/forms/slider/RuiSlider.vue_vue_type_script_setup_true_lang.js +6 -3
- package/dist/components/forms/slider/RuiSlider.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/forms/switch/RuiSwitch.js.map +1 -1
- package/dist/components/forms/switch/RuiSwitch.vue.d.ts +6 -0
- package/dist/components/forms/switch/RuiSwitch.vue_vue_type_script_setup_true_lang.js +25 -5
- package/dist/components/forms/switch/RuiSwitch.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/forms/text-area/RuiTextArea.js.map +1 -1
- package/dist/components/forms/text-area/RuiTextArea.vue.d.ts +4 -0
- package/dist/components/forms/text-area/RuiTextArea.vue_vue_type_script_setup_true_lang.js +6 -4
- package/dist/components/forms/text-area/RuiTextArea.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/forms/text-area/text-area-styles.d.ts +0 -1
- package/dist/components/forms/text-area/text-area-styles.js +0 -1
- package/dist/components/forms/text-area/text-area-styles.js.map +1 -1
- package/dist/components/forms/text-field/RuiTextField.js.map +1 -1
- package/dist/components/forms/text-field/RuiTextField.vue.d.ts +6 -2
- package/dist/components/forms/text-field/RuiTextField.vue_vue_type_script_setup_true_lang.js +6 -4
- package/dist/components/forms/text-field/RuiTextField.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/forms/text-field/text-field-styles.d.ts +0 -1
- package/dist/components/forms/text-field/text-field-styles.js +0 -1
- package/dist/components/forms/text-field/text-field-styles.js.map +1 -1
- package/dist/components/helpers/RuiFormTextDetail.js.map +1 -1
- package/dist/components/helpers/RuiFormTextDetail.vue.d.ts +14 -0
- package/dist/components/helpers/RuiFormTextDetail.vue_vue_type_script_setup_true_lang.js +41 -11
- package/dist/components/helpers/RuiFormTextDetail.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/loaders/RuiSkeletonBase.js.map +1 -1
- package/dist/components/loaders/RuiSkeletonBase.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/overlays/menu/RuiMenu.js.map +1 -1
- package/dist/components/overlays/menu/RuiMenu.vue.d.ts +3 -0
- package/dist/components/overlays/menu/RuiMenu.vue_vue_type_script_setup_true_lang.js +9 -1
- package/dist/components/overlays/menu/RuiMenu.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/tables/RuiDataTable.js.map +1 -1
- package/dist/components/tables/RuiDataTable.vue.d.ts +9 -0
- package/dist/components/tables/RuiDataTable.vue_vue_type_script_setup_true_lang.js +19 -7
- package/dist/components/tables/RuiDataTable.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/tables/RuiExpandButton.js.map +1 -1
- package/dist/components/tables/RuiExpandButton.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/tables/RuiTableHead.js.map +1 -1
- package/dist/components/tables/RuiTableHead.vue_vue_type_script_setup_true_lang.js +1 -1
- package/dist/components/tables/RuiTableHead.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/tables/RuiTablePagination.js.map +1 -1
- package/dist/components/tables/RuiTablePagination.vue_vue_type_script_setup_true_lang.js +3 -3
- package/dist/components/tables/RuiTablePagination.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/tables/data-table/RuiDataTableAppendRow.js.map +1 -1
- package/dist/components/tables/data-table/RuiDataTableAppendRow.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/tables/data-table/RuiDataTableBody.js.map +1 -1
- package/dist/components/tables/data-table/RuiDataTableBody.vue_vue_type_script_setup_true_lang.js +4 -3
- package/dist/components/tables/data-table/RuiDataTableBody.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 +4 -4
- package/dist/components/tables/data-table/RuiDataTableRow.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/tables/data-table-styles.d.ts +6 -0
- package/dist/components/tables/data-table-styles.js +15 -7
- package/dist/components/tables/data-table-styles.js.map +1 -1
- package/dist/components/tabs/tab-items/RuiTabItems.js.map +1 -1
- package/dist/components/tabs/tab-items/RuiTabItems.vue_vue_type_script_setup_true_lang.js +9 -2
- package/dist/components/tabs/tab-items/RuiTabItems.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/composables/clip-while-resizing.d.ts +15 -0
- package/dist/composables/clip-while-resizing.js +46 -0
- package/dist/composables/clip-while-resizing.js.map +1 -0
- package/dist/composables/defaults/field.d.ts +19 -3
- package/dist/composables/defaults/field.js +32 -6
- package/dist/composables/defaults/field.js.map +1 -1
- package/dist/composables/sticky-blocker.d.ts +20 -0
- package/dist/composables/sticky-blocker.js +62 -0
- package/dist/composables/sticky-blocker.js.map +1 -0
- package/dist/composables/tables/data-table/page-scroll.js +11 -1
- package/dist/composables/tables/data-table/page-scroll.js.map +1 -1
- package/dist/index.d.ts +6 -2
- package/dist/index.js.map +1 -1
- package/dist/style.css +352 -11
- package/dist/utils/development.d.ts +6 -0
- package/dist/utils/development.js +17 -0
- package/dist/utils/development.js.map +1 -0
- package/dist/utils/form-text-detail.d.ts +15 -0
- package/dist/utils/form-text-detail.js +11 -1
- package/dist/utils/form-text-detail.js.map +1 -1
- package/dist/web-types.json +242 -3
- package/package.json +1 -1
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { ContextColorsType } from '../../../consts/colors.js';
|
|
2
|
+
import { type FormDetails } from '../../../utils/form-text-detail.js';
|
|
2
3
|
export interface Props<TValue> {
|
|
3
4
|
value?: TValue;
|
|
4
5
|
disabled?: boolean;
|
|
@@ -8,7 +9,12 @@ export interface Props<TValue> {
|
|
|
8
9
|
hint?: string;
|
|
9
10
|
errorMessages?: string | string[];
|
|
10
11
|
successMessages?: string | string[];
|
|
12
|
+
/** The line under the control for its hint and messages: `reserve`, `auto` or `hidden`; falls back to the nearest `RuiFieldDefaults`, then the app default, then `reserve`. */
|
|
13
|
+
details?: FormDetails;
|
|
14
|
+
/** @deprecated Use `details="hidden"`. Removed in 4.0. */
|
|
11
15
|
hideDetails?: boolean;
|
|
16
|
+
/** Puts the hint or message under the label text instead of under the box, so a list of checkboxes keeps one text column. */
|
|
17
|
+
detailsUnderLabel?: boolean;
|
|
12
18
|
required?: boolean;
|
|
13
19
|
}
|
|
14
20
|
declare const __VLS_export: <TValue>(__VLS_props: NonNullable<Awaited<typeof __VLS_setup>>["props"], __VLS_ctx?: __VLS_PrettifyLocal<Pick<NonNullable<Awaited<typeof __VLS_setup>>, "attrs" | "emit" | "slots">>, __VLS_exposed?: NonNullable<Awaited<typeof __VLS_setup>>["expose"], __VLS_setup?: Promise<{
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { get, objectPick, set } from "../../../vendor/@vueuse/shared/dist/index.js";
|
|
2
|
-
import { useFormTextDetail } from "../../../utils/form-text-detail.js";
|
|
2
|
+
import { useFormDetails, useFormTextDetail } from "../../../utils/form-text-detail.js";
|
|
3
3
|
import RuiFormTextDetail_default from "../../helpers/RuiFormTextDetail.js";
|
|
4
4
|
import { getNonRootAttrs, getRootAttrs } from "../../../utils/helpers.js";
|
|
5
5
|
import { RuiCheckboxGroupContextKey } from "./checkbox-group/context.js";
|
|
@@ -41,10 +41,15 @@ var RuiCheckbox_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*/ defi
|
|
|
41
41
|
hint: { default: "" },
|
|
42
42
|
errorMessages: { default: () => [] },
|
|
43
43
|
successMessages: { default: () => [] },
|
|
44
|
+
details: { default: () => void 0 },
|
|
44
45
|
hideDetails: {
|
|
45
46
|
type: Boolean,
|
|
46
47
|
default: false
|
|
47
48
|
},
|
|
49
|
+
detailsUnderLabel: {
|
|
50
|
+
type: Boolean,
|
|
51
|
+
default: false
|
|
52
|
+
},
|
|
48
53
|
required: {
|
|
49
54
|
type: Boolean,
|
|
50
55
|
default: false
|
|
@@ -70,7 +75,7 @@ var RuiCheckbox_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*/ defi
|
|
|
70
75
|
const disabled = computed(() => __props.disabled || (group ? toValue(group.disabled) : false));
|
|
71
76
|
const color = computed(() => __props.color ?? (group ? toValue(group.color) : void 0));
|
|
72
77
|
const size = computed(() => __props.size ?? (group ? toValue(group.size) : void 0));
|
|
73
|
-
const
|
|
78
|
+
const detailsMode = useFormDetails(() => group !== void 0 && __props.value !== void 0 ? "hidden" : __props.details, () => __props.hideDetails);
|
|
74
79
|
const { hasError, validation } = useFormTextDetail(() => __props.errorMessages, () => __props.successMessages);
|
|
75
80
|
const internalModelValue = computed({
|
|
76
81
|
get: () => {
|
|
@@ -88,7 +93,8 @@ var RuiCheckbox_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*/ defi
|
|
|
88
93
|
disabled: get(disabled),
|
|
89
94
|
checked: get(internalModelValue) || get(indeterminate),
|
|
90
95
|
validation: get(validation),
|
|
91
|
-
color: get(color) ?? "grey"
|
|
96
|
+
color: get(color) ?? "grey",
|
|
97
|
+
underLabel: __props.detailsUnderLabel
|
|
92
98
|
}));
|
|
93
99
|
const markState = computed(() => {
|
|
94
100
|
if (get(indeterminate)) return "indeterminate";
|
|
@@ -138,12 +144,16 @@ var RuiCheckbox_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*/ defi
|
|
|
138
144
|
key: 0,
|
|
139
145
|
class: normalizeClass(unref(ui).label())
|
|
140
146
|
}, [renderSlot(_ctx.$slots, "default", {}, () => [createTextVNode(toDisplayString(__props.label), 1)]), __props.required ? (openBlock(), createElementBlock("span", _hoisted_6, " * ")) : createCommentVNode("", true)], 2)) : createCommentVNode("", true)
|
|
141
|
-
], 16, _hoisted_1),
|
|
147
|
+
], 16, _hoisted_1), unref(detailsMode) !== "hidden" ? (openBlock(), createBlock(RuiFormTextDetail_default, {
|
|
142
148
|
key: 0,
|
|
149
|
+
class: normalizeClass(unref(ui).details()),
|
|
150
|
+
details: unref(detailsMode),
|
|
143
151
|
"error-messages": __props.errorMessages,
|
|
144
152
|
"success-messages": __props.successMessages,
|
|
145
153
|
hint: __props.hint
|
|
146
154
|
}, null, 8, [
|
|
155
|
+
"class",
|
|
156
|
+
"details",
|
|
147
157
|
"error-messages",
|
|
148
158
|
"success-messages",
|
|
149
159
|
"hint"
|
package/dist/components/forms/checkbox/RuiCheckbox.vue_vue_type_script_setup_true_lang.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"RuiCheckbox.vue_vue_type_script_setup_true_lang.js","names":["$attrs","$slots","_useModel","_openBlock","_createElementBlock","_normalizeProps","_guardReactiveProps","_unref","_createElementVNode","_mergeProps","_withDirectives","_isRef","_vModelCheckbox","_normalizeClass","_createCommentVNode","_renderSlot","_createTextVNode","_toDisplayString","_createBlock","RuiFormTextDetail"],"sources":["../../../../src/components/forms/checkbox/RuiCheckbox.vue"],"sourcesContent":["<script lang=\"ts\" setup generic=\"TValue\">\nimport type { ContextColorsType } from '@/consts/colors';\nimport { objectPick } from '@vueuse/shared';\nimport { checkControlStyles } from '@/components/forms/check-control-styles';\nimport { RuiCheckboxGroupContextKey } from '@/components/forms/checkbox/checkbox-group/context';\nimport RuiFormTextDetail from '@/components/helpers/RuiFormTextDetail.vue';\nimport { useFormTextDetail } from '@/utils/form-text-detail';\nimport { getNonRootAttrs, getRootAttrs } from '@/utils/helpers';\n\nexport interface Props<TValue> {\n value?: TValue;\n disabled?: boolean;\n color?: ContextColorsType;\n size?: 'sm' | 'lg';\n label?: string;\n hint?: string;\n errorMessages?: string | string[];\n successMessages?: string | string[];\n hideDetails?: boolean;\n required?: boolean;\n}\n\ndefineOptions({\n name: 'RuiCheckbox',\n inheritAttrs: false,\n});\n\nconst modelValue = defineModel<boolean>({ default: false });\n\nconst indeterminate = defineModel<boolean>('indeterminate', { default: false });\n\nconst {\n value = undefined,\n disabled: disabledProp = false,\n color: colorProp = undefined,\n size: sizeProp = undefined,\n label = '',\n hint = '',\n errorMessages = [],\n successMessages = [],\n hideDetails = false,\n required = false,\n} = defineProps<Props<TValue>>();\n\ndefineSlots<{\n default?: () => any;\n}>();\n\nconst el = useTemplateRef<HTMLInputElement>('el');\n\nconst group = inject(RuiCheckboxGroupContextKey, undefined);\n\nconst disabled = computed<boolean>(() => disabledProp || (group ? toValue(group.disabled) : false));\nconst color = computed<ContextColorsType | undefined>(() => colorProp ?? (group ? toValue(group.color) : undefined));\nconst size = computed<'sm' | 'lg' | undefined>(() => sizeProp ?? (group ? toValue(group.size) : undefined));\n// Suppress per-child details inside a group so the group's own hint/error wins.\nconst
|
|
1
|
+
{"version":3,"file":"RuiCheckbox.vue_vue_type_script_setup_true_lang.js","names":["$attrs","$slots","_useModel","_openBlock","_createElementBlock","_normalizeProps","_guardReactiveProps","_unref","_createElementVNode","_mergeProps","_withDirectives","_isRef","_vModelCheckbox","_normalizeClass","_createCommentVNode","_renderSlot","_createTextVNode","_toDisplayString","_createBlock","RuiFormTextDetail"],"sources":["../../../../src/components/forms/checkbox/RuiCheckbox.vue"],"sourcesContent":["<script lang=\"ts\" setup generic=\"TValue\">\nimport type { ContextColorsType } from '@/consts/colors';\nimport { objectPick } from '@vueuse/shared';\nimport { checkControlStyles } from '@/components/forms/check-control-styles';\nimport { RuiCheckboxGroupContextKey } from '@/components/forms/checkbox/checkbox-group/context';\nimport RuiFormTextDetail from '@/components/helpers/RuiFormTextDetail.vue';\nimport { type FormDetails, useFormDetails, useFormTextDetail } from '@/utils/form-text-detail';\nimport { getNonRootAttrs, getRootAttrs } from '@/utils/helpers';\n\nexport interface Props<TValue> {\n value?: TValue;\n disabled?: boolean;\n color?: ContextColorsType;\n size?: 'sm' | 'lg';\n label?: string;\n hint?: string;\n errorMessages?: string | string[];\n successMessages?: string | string[];\n /** The line under the control for its hint and messages: `reserve`, `auto` or `hidden`; falls back to the nearest `RuiFieldDefaults`, then the app default, then `reserve`. */\n details?: FormDetails;\n /** @deprecated Use `details=\"hidden\"`. Removed in 4.0. */\n hideDetails?: boolean;\n /** Puts the hint or message under the label text instead of under the box, so a list of checkboxes keeps one text column. */\n detailsUnderLabel?: boolean;\n required?: boolean;\n}\n\ndefineOptions({\n name: 'RuiCheckbox',\n inheritAttrs: false,\n});\n\nconst modelValue = defineModel<boolean>({ default: false });\n\nconst indeterminate = defineModel<boolean>('indeterminate', { default: false });\n\nconst {\n value = undefined,\n disabled: disabledProp = false,\n color: colorProp = undefined,\n size: sizeProp = undefined,\n label = '',\n hint = '',\n errorMessages = [],\n successMessages = [],\n details = undefined,\n hideDetails = false,\n detailsUnderLabel = false,\n required = false,\n} = defineProps<Props<TValue>>();\n\ndefineSlots<{\n default?: () => any;\n}>();\n\nconst el = useTemplateRef<HTMLInputElement>('el');\n\nconst group = inject(RuiCheckboxGroupContextKey, undefined);\n\nconst disabled = computed<boolean>(() => disabledProp || (group ? toValue(group.disabled) : false));\nconst color = computed<ContextColorsType | undefined>(() => colorProp ?? (group ? toValue(group.color) : undefined));\nconst size = computed<'sm' | 'lg' | undefined>(() => sizeProp ?? (group ? toValue(group.size) : undefined));\n// Suppress per-child details inside a group so the group's own hint/error wins.\nconst detailsMode = useFormDetails(\n () => (group !== undefined && value !== undefined ? 'hidden' : details),\n () => hideDetails,\n);\n\nconst { hasError, validation } = useFormTextDetail(\n () => errorMessages,\n () => successMessages,\n);\n\nconst internalModelValue = computed<boolean>({\n get: () => {\n if (group && value !== undefined)\n return group.isChecked(value);\n return get(modelValue);\n },\n set: (checked: boolean) => {\n if (checked)\n set(indeterminate, false);\n if (group && value !== undefined)\n group.toggle(value, checked);\n else\n set(modelValue, checked);\n },\n});\n\nconst ui = computed<ReturnType<typeof checkControlStyles>>(() => checkControlStyles({\n size: get(size),\n disabled: get(disabled),\n checked: get(internalModelValue) || get(indeterminate),\n validation: get(validation),\n color: get(color) ?? 'grey',\n underLabel: detailsUnderLabel,\n}));\n\nconst markState = computed<'indeterminate' | 'checked' | 'unchecked'>(() => {\n if (get(indeterminate))\n return 'indeterminate';\n return get(internalModelValue) ? 'checked' : 'unchecked';\n});\n\nwatch(indeterminate, (val) => {\n const input = get(el);\n if (!input) {\n return;\n }\n input.checked = !val;\n input.value = val ? 'off' : 'on';\n});\n\nwatch(internalModelValue, (val) => {\n const input = get(el);\n if (input && input.checked !== val)\n input.checked = val;\n});\n</script>\n\n<template>\n <div v-bind=\"getRootAttrs($attrs)\">\n <label\n :class=\"ui.wrapper()\"\n v-bind=\"objectPick($attrs, ['onClick'])\"\n :data-disabled=\"disabled || undefined\"\n :data-checked=\"internalModelValue || indeterminate || undefined\"\n :data-error=\"hasError ? '' : undefined\"\n >\n <input\n ref=\"el\"\n v-model=\"internalModelValue\"\n type=\"checkbox\"\n :class=\"ui.input()\"\n :disabled=\"disabled\"\n :aria-invalid=\"hasError\"\n v-bind=\"getNonRootAttrs($attrs, ['onInput', 'onClick'])\"\n />\n <span :class=\"ui.control()\">\n <span\n :class=\"ui.mark()\"\n data-mark\n :data-state=\"markState\"\n >\n <svg\n v-if=\"markState !== 'unchecked'\"\n :class=\"ui.glyph()\"\n viewBox=\"0 0 12 12\"\n fill=\"none\"\n stroke=\"currentColor\"\n stroke-width=\"2.5\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n aria-hidden=\"true\"\n >\n <path\n v-if=\"markState === 'indeterminate'\"\n d=\"M3 6h6\"\n />\n <path\n v-else\n d=\"M2.5 6.2 5 8.6l4.5-5\"\n />\n </svg>\n </span>\n </span>\n <span\n v-if=\"label || $slots.default\"\n :class=\"ui.label()\"\n >\n <slot>{{ label }}</slot>\n <span\n v-if=\"required\"\n class=\"text-rui-error\"\n >\n *\n </span>\n </span>\n </label>\n <RuiFormTextDetail\n v-if=\"detailsMode !== 'hidden'\"\n :class=\"ui.details()\"\n :details=\"detailsMode\"\n :error-messages=\"errorMessages\"\n :success-messages=\"successMessages\"\n :hint=\"hint\"\n />\n </div>\n</template>\n"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"RuiCheckboxGroup.js","names":["_sfc_main"],"sources":["../../../../../src/components/forms/checkbox/checkbox-group/RuiCheckboxGroup.vue"],"sourcesContent":["<script lang=\"ts\" setup generic=\"TValue\">\nimport type { ContextColorsType } from '@/consts/colors';\nimport { RuiCheckboxGroupContextKey } from '@/components/forms/checkbox/checkbox-group/context';\nimport RuiFormTextDetail from '@/components/helpers/RuiFormTextDetail.vue';\n\nexport interface Props {\n inline?: boolean;\n label?: string;\n hint?: string;\n errorMessages?: string | string[];\n successMessages?: string | string[];\n hideDetails?: boolean;\n disabled?: boolean;\n color?: ContextColorsType;\n size?: 'sm' | 'lg';\n required?: boolean;\n}\n\ndefineOptions({\n name: 'RuiCheckboxGroup',\n inheritAttrs: false,\n});\n\nconst modelValue = defineModel<TValue[]>({ default: () => [] });\n\nconst {\n inline = false,\n label = '',\n hint = '',\n errorMessages = [],\n successMessages = [],\n hideDetails = false,\n disabled = false,\n color,\n size,\n required = false,\n} = defineProps<Props>();\n\ndefineSlots<{\n default?: () => any;\n}>();\n\nfunction arrayIncludes(arr: readonly TValue[], value: unknown): boolean {\n return Array.prototype.includes.call(arr, value);\n}\n\nprovide(RuiCheckboxGroupContextKey, {\n isChecked: (value: unknown): boolean => arrayIncludes(get(modelValue), value),\n toggle: (value: unknown, checked: boolean): void => {\n const current = get(modelValue);\n const present = arrayIncludes(current, value);\n if (checked === present)\n return;\n if (checked)\n set(modelValue, [...current, value as TValue]);\n else\n set(modelValue, current.filter(v => v !== value));\n },\n disabled: () => disabled,\n color: () => color,\n size: () => size,\n});\n</script>\n\n<template>\n <div\n role=\"group\"\n v-bind=\"$attrs\"\n >\n <div\n v-if=\"label\"\n class=\"text-rui-text text-sm/5 font-medium\"\n >\n {{ label }}\n <span\n v-if=\"required\"\n class=\"text-rui-error\"\n >\n *\n </span>\n </div>\n <div :class=\"{ 'flex between:ml-6 between:mr-0': inline }\">\n <slot />\n </div>\n <RuiFormTextDetail\n v-if=\"
|
|
1
|
+
{"version":3,"file":"RuiCheckboxGroup.js","names":["_sfc_main"],"sources":["../../../../../src/components/forms/checkbox/checkbox-group/RuiCheckboxGroup.vue"],"sourcesContent":["<script lang=\"ts\" setup generic=\"TValue\">\nimport type { ContextColorsType } from '@/consts/colors';\nimport { RuiCheckboxGroupContextKey } from '@/components/forms/checkbox/checkbox-group/context';\nimport RuiFormTextDetail from '@/components/helpers/RuiFormTextDetail.vue';\nimport { type FormDetails, useFormDetails } from '@/utils/form-text-detail';\n\nexport interface Props {\n inline?: boolean;\n label?: string;\n hint?: string;\n errorMessages?: string | string[];\n successMessages?: string | string[];\n /** The line under the group for its hint and messages: `reserve`, `auto` or `hidden`; falls back to the nearest `RuiFieldDefaults`, then the app default, then `reserve`. */\n details?: FormDetails;\n /** @deprecated Use `details=\"hidden\"`. Removed in 4.0. */\n hideDetails?: boolean;\n disabled?: boolean;\n color?: ContextColorsType;\n size?: 'sm' | 'lg';\n required?: boolean;\n}\n\ndefineOptions({\n name: 'RuiCheckboxGroup',\n inheritAttrs: false,\n});\n\nconst modelValue = defineModel<TValue[]>({ default: () => [] });\n\nconst {\n inline = false,\n label = '',\n hint = '',\n errorMessages = [],\n successMessages = [],\n details = undefined,\n hideDetails = false,\n disabled = false,\n color,\n size,\n required = false,\n} = defineProps<Props>();\n\ndefineSlots<{\n default?: () => any;\n}>();\n\nconst detailsMode = useFormDetails(() => details, () => hideDetails);\n\nfunction arrayIncludes(arr: readonly TValue[], value: unknown): boolean {\n return Array.prototype.includes.call(arr, value);\n}\n\nprovide(RuiCheckboxGroupContextKey, {\n isChecked: (value: unknown): boolean => arrayIncludes(get(modelValue), value),\n toggle: (value: unknown, checked: boolean): void => {\n const current = get(modelValue);\n const present = arrayIncludes(current, value);\n if (checked === present)\n return;\n if (checked)\n set(modelValue, [...current, value as TValue]);\n else\n set(modelValue, current.filter(v => v !== value));\n },\n disabled: () => disabled,\n color: () => color,\n size: () => size,\n});\n</script>\n\n<template>\n <div\n role=\"group\"\n v-bind=\"$attrs\"\n >\n <div\n v-if=\"label\"\n class=\"text-rui-text text-sm/5 font-medium\"\n >\n {{ label }}\n <span\n v-if=\"required\"\n class=\"text-rui-error\"\n >\n *\n </span>\n </div>\n <div :class=\"{ 'flex between:ml-6 between:mr-0': inline }\">\n <slot />\n </div>\n <RuiFormTextDetail\n v-if=\"detailsMode !== 'hidden'\"\n :details=\"detailsMode\"\n :error-messages=\"errorMessages\"\n :success-messages=\"successMessages\"\n :hint=\"hint\"\n />\n </div>\n</template>\n"],"mappings":""}
|
|
@@ -1,10 +1,14 @@
|
|
|
1
1
|
import type { ContextColorsType } from '../../../../consts/colors.js';
|
|
2
|
+
import { type FormDetails } from '../../../../utils/form-text-detail.js';
|
|
2
3
|
export interface Props {
|
|
3
4
|
inline?: boolean;
|
|
4
5
|
label?: string;
|
|
5
6
|
hint?: string;
|
|
6
7
|
errorMessages?: string | string[];
|
|
7
8
|
successMessages?: string | string[];
|
|
9
|
+
/** The line under the group for its hint and messages: `reserve`, `auto` or `hidden`; falls back to the nearest `RuiFieldDefaults`, then the app default, then `reserve`. */
|
|
10
|
+
details?: FormDetails;
|
|
11
|
+
/** @deprecated Use `details="hidden"`. Removed in 4.0. */
|
|
8
12
|
hideDetails?: boolean;
|
|
9
13
|
disabled?: boolean;
|
|
10
14
|
color?: ContextColorsType;
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import { get, set } from "../../../../vendor/@vueuse/shared/dist/index.js";
|
|
2
|
+
import { useFormDetails } from "../../../../utils/form-text-detail.js";
|
|
2
3
|
import RuiFormTextDetail_default from "../../../helpers/RuiFormTextDetail.js";
|
|
3
4
|
import { RuiCheckboxGroupContextKey } from "./context.js";
|
|
4
|
-
import { createBlock, createCommentVNode, createElementBlock, createElementVNode, createTextVNode, defineComponent, mergeModels, mergeProps, normalizeClass, openBlock, provide, renderSlot, toDisplayString, useModel } from "vue";
|
|
5
|
+
import { createBlock, createCommentVNode, createElementBlock, createElementVNode, createTextVNode, defineComponent, mergeModels, mergeProps, normalizeClass, openBlock, provide, renderSlot, toDisplayString, unref, useModel } from "vue";
|
|
5
6
|
//#region src/components/forms/checkbox/checkbox-group/RuiCheckboxGroup.vue?vue&type=script&setup=true&lang.ts
|
|
6
7
|
var _hoisted_1 = {
|
|
7
8
|
key: 0,
|
|
@@ -24,6 +25,7 @@ var RuiCheckboxGroup_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*/
|
|
|
24
25
|
hint: { default: "" },
|
|
25
26
|
errorMessages: { default: () => [] },
|
|
26
27
|
successMessages: { default: () => [] },
|
|
28
|
+
details: { default: () => void 0 },
|
|
27
29
|
hideDetails: {
|
|
28
30
|
type: Boolean,
|
|
29
31
|
default: false
|
|
@@ -45,6 +47,7 @@ var RuiCheckboxGroup_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*/
|
|
|
45
47
|
emits: ["update:modelValue"],
|
|
46
48
|
setup(__props) {
|
|
47
49
|
const modelValue = useModel(__props, "modelValue");
|
|
50
|
+
const detailsMode = useFormDetails(() => __props.details, () => __props.hideDetails);
|
|
48
51
|
function arrayIncludes(arr, value) {
|
|
49
52
|
return Array.prototype.includes.call(arr, value);
|
|
50
53
|
}
|
|
@@ -64,12 +67,14 @@ var RuiCheckboxGroup_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*/
|
|
|
64
67
|
return openBlock(), createElementBlock("div", mergeProps({ role: "group" }, _ctx.$attrs), [
|
|
65
68
|
__props.label ? (openBlock(), createElementBlock("div", _hoisted_1, [createTextVNode(toDisplayString(__props.label) + " ", 1), __props.required ? (openBlock(), createElementBlock("span", _hoisted_2, " * ")) : createCommentVNode("", true)])) : createCommentVNode("", true),
|
|
66
69
|
createElementVNode("div", { class: normalizeClass({ "flex between:ml-6 between:mr-0": __props.inline }) }, [renderSlot(_ctx.$slots, "default")], 2),
|
|
67
|
-
|
|
70
|
+
unref(detailsMode) !== "hidden" ? (openBlock(), createBlock(RuiFormTextDetail_default, {
|
|
68
71
|
key: 1,
|
|
72
|
+
details: unref(detailsMode),
|
|
69
73
|
"error-messages": __props.errorMessages,
|
|
70
74
|
"success-messages": __props.successMessages,
|
|
71
75
|
hint: __props.hint
|
|
72
76
|
}, null, 8, [
|
|
77
|
+
"details",
|
|
73
78
|
"error-messages",
|
|
74
79
|
"success-messages",
|
|
75
80
|
"hint"
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"RuiCheckboxGroup.vue_vue_type_script_setup_true_lang.js","names":["$attrs","_useModel","_openBlock","_createElementBlock","_mergeProps","_createTextVNode","_toDisplayString","_createCommentVNode","_createElementVNode","_normalizeClass","_renderSlot","_createBlock","RuiFormTextDetail"],"sources":["../../../../../src/components/forms/checkbox/checkbox-group/RuiCheckboxGroup.vue"],"sourcesContent":["<script lang=\"ts\" setup generic=\"TValue\">\nimport type { ContextColorsType } from '@/consts/colors';\nimport { RuiCheckboxGroupContextKey } from '@/components/forms/checkbox/checkbox-group/context';\nimport RuiFormTextDetail from '@/components/helpers/RuiFormTextDetail.vue';\n\nexport interface Props {\n inline?: boolean;\n label?: string;\n hint?: string;\n errorMessages?: string | string[];\n successMessages?: string | string[];\n hideDetails?: boolean;\n disabled?: boolean;\n color?: ContextColorsType;\n size?: 'sm' | 'lg';\n required?: boolean;\n}\n\ndefineOptions({\n name: 'RuiCheckboxGroup',\n inheritAttrs: false,\n});\n\nconst modelValue = defineModel<TValue[]>({ default: () => [] });\n\nconst {\n inline = false,\n label = '',\n hint = '',\n errorMessages = [],\n successMessages = [],\n hideDetails = false,\n disabled = false,\n color,\n size,\n required = false,\n} = defineProps<Props>();\n\ndefineSlots<{\n default?: () => any;\n}>();\n\nfunction arrayIncludes(arr: readonly TValue[], value: unknown): boolean {\n return Array.prototype.includes.call(arr, value);\n}\n\nprovide(RuiCheckboxGroupContextKey, {\n isChecked: (value: unknown): boolean => arrayIncludes(get(modelValue), value),\n toggle: (value: unknown, checked: boolean): void => {\n const current = get(modelValue);\n const present = arrayIncludes(current, value);\n if (checked === present)\n return;\n if (checked)\n set(modelValue, [...current, value as TValue]);\n else\n set(modelValue, current.filter(v => v !== value));\n },\n disabled: () => disabled,\n color: () => color,\n size: () => size,\n});\n</script>\n\n<template>\n <div\n role=\"group\"\n v-bind=\"$attrs\"\n >\n <div\n v-if=\"label\"\n class=\"text-rui-text text-sm/5 font-medium\"\n >\n {{ label }}\n <span\n v-if=\"required\"\n class=\"text-rui-error\"\n >\n *\n </span>\n </div>\n <div :class=\"{ 'flex between:ml-6 between:mr-0': inline }\">\n <slot />\n </div>\n <RuiFormTextDetail\n v-if=\"
|
|
1
|
+
{"version":3,"file":"RuiCheckboxGroup.vue_vue_type_script_setup_true_lang.js","names":["$attrs","_useModel","_openBlock","_createElementBlock","_mergeProps","_createTextVNode","_toDisplayString","_createCommentVNode","_createElementVNode","_normalizeClass","_renderSlot","_unref","_createBlock","RuiFormTextDetail"],"sources":["../../../../../src/components/forms/checkbox/checkbox-group/RuiCheckboxGroup.vue"],"sourcesContent":["<script lang=\"ts\" setup generic=\"TValue\">\nimport type { ContextColorsType } from '@/consts/colors';\nimport { RuiCheckboxGroupContextKey } from '@/components/forms/checkbox/checkbox-group/context';\nimport RuiFormTextDetail from '@/components/helpers/RuiFormTextDetail.vue';\nimport { type FormDetails, useFormDetails } from '@/utils/form-text-detail';\n\nexport interface Props {\n inline?: boolean;\n label?: string;\n hint?: string;\n errorMessages?: string | string[];\n successMessages?: string | string[];\n /** The line under the group for its hint and messages: `reserve`, `auto` or `hidden`; falls back to the nearest `RuiFieldDefaults`, then the app default, then `reserve`. */\n details?: FormDetails;\n /** @deprecated Use `details=\"hidden\"`. Removed in 4.0. */\n hideDetails?: boolean;\n disabled?: boolean;\n color?: ContextColorsType;\n size?: 'sm' | 'lg';\n required?: boolean;\n}\n\ndefineOptions({\n name: 'RuiCheckboxGroup',\n inheritAttrs: false,\n});\n\nconst modelValue = defineModel<TValue[]>({ default: () => [] });\n\nconst {\n inline = false,\n label = '',\n hint = '',\n errorMessages = [],\n successMessages = [],\n details = undefined,\n hideDetails = false,\n disabled = false,\n color,\n size,\n required = false,\n} = defineProps<Props>();\n\ndefineSlots<{\n default?: () => any;\n}>();\n\nconst detailsMode = useFormDetails(() => details, () => hideDetails);\n\nfunction arrayIncludes(arr: readonly TValue[], value: unknown): boolean {\n return Array.prototype.includes.call(arr, value);\n}\n\nprovide(RuiCheckboxGroupContextKey, {\n isChecked: (value: unknown): boolean => arrayIncludes(get(modelValue), value),\n toggle: (value: unknown, checked: boolean): void => {\n const current = get(modelValue);\n const present = arrayIncludes(current, value);\n if (checked === present)\n return;\n if (checked)\n set(modelValue, [...current, value as TValue]);\n else\n set(modelValue, current.filter(v => v !== value));\n },\n disabled: () => disabled,\n color: () => color,\n size: () => size,\n});\n</script>\n\n<template>\n <div\n role=\"group\"\n v-bind=\"$attrs\"\n >\n <div\n v-if=\"label\"\n class=\"text-rui-text text-sm/5 font-medium\"\n >\n {{ label }}\n <span\n v-if=\"required\"\n class=\"text-rui-error\"\n >\n *\n </span>\n </div>\n <div :class=\"{ 'flex between:ml-6 between:mr-0': inline }\">\n <slot />\n </div>\n <RuiFormTextDetail\n v-if=\"detailsMode !== 'hidden'\"\n :details=\"detailsMode\"\n :error-messages=\"errorMessages\"\n :success-messages=\"successMessages\"\n :hint=\"hint\"\n />\n </div>\n</template>\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EA2BA,MAAM,aAAaC,SAAqB,SAAA,YAAsB;EAoB9D,MAAM,cAAc,qBAAqB,QAAA,eAAe,QAAA,WAAW;EAEnE,SAAS,cAAc,KAAwB,OAAyB;GACtE,OAAO,MAAM,UAAU,SAAS,KAAK,KAAK,KAAK;EACjD;EAEA,QAAQ,4BAA4B;GAClC,YAAY,UAA4B,cAAc,IAAI,UAAU,GAAG,KAAK;GAC5E,SAAS,OAAgB,YAA2B;IAClD,MAAM,UAAU,IAAI,UAAU;IAE9B,IAAI,YADY,cAAc,SAAS,KACvB,GACd;IACF,IAAI,SACF,IAAI,YAAY,CAAC,GAAG,SAAS,KAAe,CAAC;SAE7C,IAAI,YAAY,QAAQ,QAAO,MAAK,MAAM,KAAK,CAAC;GACpD;GACA,gBAAgB,QAAA;GAChB,aAAa,QAAA;GACb,YAAY,QAAA;EACd,CAAC;;GAIC,OAAAC,UAAA,GAAAC,mBA0BM,OA1BNC,WA0BM,EAzBJ,MAAK,QAAO,GACJJ,KAAAA,MAAM,GAAA;IAGN,QAAA,SADRE,UAAA,GAAAC,mBAWM,OAXN,YAWM,CAPDE,gBAAAC,gBAAA,QAAA,KAAK,IAAG,KACX,CAAA,GACQ,QAAA,YADRJ,UAAA,GAAAC,mBAKO,QALP,YAGC,KAED,KAAA,mBAAA,IAAA,IAAA,CAAA,CAAA,KAAA,mBAAA,IAAA,IAAA;IAEFK,mBAEM,OAAA,EAFA,OAAKC,eAAA,EAAA,kCAAsC,QAAA,OAAM,CAAA,EAAA,GAAA,CACrDC,WAAQ,KAAA,QAAA,SAAA,CAAA,GAAA,CAAA;IAGFC,MAAA,WAAA,MAAW,YADnBT,UAAA,GAAAU,YAMEC,2BAAA;;KAJC,SAASF,MAAA,WAAA;KACT,kBAAgB,QAAA;KAChB,oBAAkB,QAAA;KAClB,MAAM,QAAA"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"RuiFieldDefaults.js","names":["_sfc_main"],"sources":["../../../../src/components/forms/field-defaults/RuiFieldDefaults.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport { type LabelPlacement, provideFieldDefaults } from '@/composables/defaults/field';\n\nexport interface FieldDefaultsProps {\n labelPlacement?: LabelPlacement;\n}\n\ndefineOptions({\n name: 'RuiFieldDefaults',\n});\n\nconst { labelPlacement } = defineProps<FieldDefaultsProps>();\n\ndefineSlots<{\n default?: () => any;\n}>();\n\nprovideFieldDefaults(() => labelPlacement);\n</script>\n\n<template>\n <slot />\n</template>\n"],"mappings":""}
|
|
1
|
+
{"version":3,"file":"RuiFieldDefaults.js","names":["_sfc_main"],"sources":["../../../../src/components/forms/field-defaults/RuiFieldDefaults.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport type { FormDetails } from '@/utils/form-text-detail';\nimport { type LabelPlacement, provideFieldDefaults } from '@/composables/defaults/field';\n\nexport interface FieldDefaultsProps {\n labelPlacement?: LabelPlacement;\n /** The details mode for every form control below; a control's own `details` still wins. */\n details?: FormDetails;\n}\n\ndefineOptions({\n name: 'RuiFieldDefaults',\n});\n\nconst { labelPlacement, details } = defineProps<FieldDefaultsProps>();\n\ndefineSlots<{\n default?: () => any;\n}>();\n\nprovideFieldDefaults({\n labelPlacement: () => labelPlacement,\n details: () => details,\n});\n</script>\n\n<template>\n <slot />\n</template>\n"],"mappings":""}
|
|
@@ -1,6 +1,9 @@
|
|
|
1
|
+
import type { FormDetails } from '../../../utils/form-text-detail.js';
|
|
1
2
|
import { type LabelPlacement } from '../../../composables/defaults/field.js';
|
|
2
3
|
export interface FieldDefaultsProps {
|
|
3
4
|
labelPlacement?: LabelPlacement;
|
|
5
|
+
/** The details mode for every form control below; a control's own `details` still wins. */
|
|
6
|
+
details?: FormDetails;
|
|
4
7
|
}
|
|
5
8
|
type __VLS_Slots = {
|
|
6
9
|
default?: () => any;
|
package/dist/components/forms/field-defaults/RuiFieldDefaults.vue_vue_type_script_setup_true_lang.js
CHANGED
|
@@ -4,9 +4,15 @@ import { defineComponent, renderSlot } from "vue";
|
|
|
4
4
|
var RuiFieldDefaults_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*/ defineComponent({
|
|
5
5
|
name: "RuiFieldDefaults",
|
|
6
6
|
__name: "RuiFieldDefaults",
|
|
7
|
-
props: {
|
|
7
|
+
props: {
|
|
8
|
+
labelPlacement: {},
|
|
9
|
+
details: {}
|
|
10
|
+
},
|
|
8
11
|
setup(__props) {
|
|
9
|
-
provideFieldDefaults(
|
|
12
|
+
provideFieldDefaults({
|
|
13
|
+
labelPlacement: () => __props.labelPlacement,
|
|
14
|
+
details: () => __props.details
|
|
15
|
+
});
|
|
10
16
|
return (_ctx, _cache) => {
|
|
11
17
|
return renderSlot(_ctx.$slots, "default");
|
|
12
18
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"RuiFieldDefaults.vue_vue_type_script_setup_true_lang.js","names":["_renderSlot"],"sources":["../../../../src/components/forms/field-defaults/RuiFieldDefaults.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport { type LabelPlacement, provideFieldDefaults } from '@/composables/defaults/field';\n\nexport interface FieldDefaultsProps {\n labelPlacement?: LabelPlacement;\n}\n\ndefineOptions({\n name: 'RuiFieldDefaults',\n});\n\nconst { labelPlacement } = defineProps<FieldDefaultsProps>();\n\ndefineSlots<{\n default?: () => any;\n}>();\n\nprovideFieldDefaults(() => labelPlacement);\n</script>\n\n<template>\n <slot />\n</template>\n"],"mappings":"
|
|
1
|
+
{"version":3,"file":"RuiFieldDefaults.vue_vue_type_script_setup_true_lang.js","names":["_renderSlot"],"sources":["../../../../src/components/forms/field-defaults/RuiFieldDefaults.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport type { FormDetails } from '@/utils/form-text-detail';\nimport { type LabelPlacement, provideFieldDefaults } from '@/composables/defaults/field';\n\nexport interface FieldDefaultsProps {\n labelPlacement?: LabelPlacement;\n /** The details mode for every form control below; a control's own `details` still wins. */\n details?: FormDetails;\n}\n\ndefineOptions({\n name: 'RuiFieldDefaults',\n});\n\nconst { labelPlacement, details } = defineProps<FieldDefaultsProps>();\n\ndefineSlots<{\n default?: () => any;\n}>();\n\nprovideFieldDefaults({\n labelPlacement: () => labelPlacement,\n details: () => details,\n});\n</script>\n\n<template>\n <slot />\n</template>\n"],"mappings":";;;;;;;;;;;EAoBA,qBAAqB;GACnB,sBAAsB,QAAA;GACtB,eAAe,QAAA;EACjB,CAAC;;GAIC,OAAAA,WAAQ,KAAA,QAAA,SAAA"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"RuiFileUpload.js","names":["_sfc_main"],"sources":["../../../../src/components/forms/file-upload/RuiFileUpload.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport type { RuiIcons } from '@/icons';\nimport RuiFormTextDetail from '@/components/helpers/RuiFormTextDetail.vue';\nimport RuiIcon from '@/components/icons/RuiIcon.vue';\nimport RuiProgress from '@/components/progress/RuiProgress.vue';\nimport { useRuiI8n } from '@/composables/use-rui-i18n';\nimport { RUI_I18N_KEYS } from '@/i18n/keys';\nimport { useFormTextDetail } from '@/utils/form-text-detail';\nimport { formatFileSize, getNonRootAttrs, getRootAttrs } from '@/utils/helpers';\n\nexport interface FileUploadProps {\n /** File type filter (HTML accept attribute syntax). e.g. '.csv', 'image/*', '.png,.jpg' */\n accept?: string;\n /** Allow multiple file selection. */\n multiple?: boolean;\n disabled?: boolean;\n /** Shows a loading overlay (consumer-driven, e.g. while uploading). */\n loading?: boolean;\n /** Upload progress 0-100. When provided alongside `loading`, the overlay shows a determinate progress bar with percentage. */\n progress?: number;\n /** Renders the success state border/icon. Consumer toggles back to false. */\n uploaded?: boolean;\n /** Optional max size per file in bytes; failures emit `error` and set internal error state. */\n maxSize?: number;\n /** External error messages (e.g. from Vuelidate). Shown via the standard form detail block. */\n errorMessages?: string | string[];\n /** Hint text below the dropzone. */\n hint?: string;\n /** Disable drag-and-drop; the dropzone becomes click-only (matches rotki.com use case). */\n noDrop?: boolean;\n /**\n * Replace the default \"Drag and drop or \\{button\\}\" copy. With `noDrop` the default is empty, so only the button shows.\n * The defaults of this and the other texts, error messages included, come from the `rui.file_upload.*` translation keys.\n */\n uploadText?: string;\n /** Label shown on the inline button when no file is selected. */\n clickToUploadText?: string;\n /** Label shown on the inline button when one or more files are already selected. */\n replaceText?: string;\n /** Hide the bottom hint/error details strip. */\n hideDetails?: boolean;\n}\n\ndefineOptions({\n name: 'RuiFileUpload',\n inheritAttrs: false,\n});\n\nconst modelValue = defineModel<File | File[] | undefined>({ required: true });\n\nconst {\n accept = '*',\n multiple = false,\n disabled = false,\n loading = false,\n progress = undefined,\n uploaded = false,\n maxSize = undefined,\n errorMessages = [],\n hint = '',\n noDrop = false,\n uploadText = undefined,\n clickToUploadText = undefined,\n replaceText = undefined,\n hideDetails = false,\n} = defineProps<FileUploadProps>();\n\nconst emit = defineEmits<{\n error: [message: string];\n}>();\n\ndefineSlots<{\n preview?: (props: { files: File[]; remove: () => void }) => any;\n icon?: () => any;\n description?: () => any;\n}>();\n\nconst { t } = useRuiI8n();\n\nconst wrapper = useTemplateRef<HTMLDivElement>('wrapper');\nconst input = useTemplateRef<HTMLInputElement>('input');\nconst internalError = ref<string>('');\n\nconst files = computed<File[]>(() => {\n const value = get(modelValue);\n if (!value)\n return [];\n return Array.isArray(value) ? value : [value];\n});\n\nconst hasFiles = computed<boolean>(() => get(files).length > 0);\n\nconst progressLabel = computed<string | undefined>(() => {\n const value = progress;\n return value === undefined ? undefined : `${Math.round(value)}%`;\n});\n\nconst combinedErrorMessages = computed<string[]>(() => {\n const external = Array.isArray(errorMessages) ? errorMessages : [errorMessages];\n const internal = get(internalError);\n return [...external.filter(Boolean), ...(internal ? [internal] : [])];\n});\n\nconst { hasError } = useFormTextDetail(combinedErrorMessages, () => []);\n\n// a click-only dropzone does not offer dragging\nconst resolvedUploadText = computed<string>(() => uploadText ?? (noDrop ? '' : t(RUI_I18N_KEYS.fileUpload.dropPrefix, 'Drag and drop or')));\nconst resolvedClickToUploadText = computed<string>(() => clickToUploadText ?? t(RUI_I18N_KEYS.fileUpload.clickToUpload, 'click to upload'));\nconst resolvedReplaceText = computed<string>(() => replaceText ?? t(RUI_I18N_KEYS.fileUpload.replace, 'replace file'));\n\ntype WellTone = 'primary' | 'error' | 'success';\n\n// the icon well follows the dropzone's state, like its border\nconst wellTone = computed<WellTone>(() => {\n if (get(hasError))\n return 'error';\n if (uploaded)\n return 'success';\n return 'primary';\n});\n\nconst wellIcon = computed<RuiIcons>(() => {\n const tone = get(wellTone);\n if (tone === 'error')\n return 'lu-circle-alert';\n if (tone === 'success')\n return 'lu-circle-check';\n return 'lu-file-up';\n});\n\n// the 10% tint vanishes on a dark page, so dark doubles it\nconst wellClasses: Record<WellTone, string> = {\n primary: 'bg-rui-primary-soft dark:bg-rui-primary/20',\n error: 'bg-rui-error-soft dark:bg-rui-error/20',\n success: 'bg-rui-success-soft dark:bg-rui-success/20',\n};\n\nfunction matchesAccept(file: File, acceptString: string): boolean {\n if (!acceptString || acceptString === '*' || acceptString === '*/*')\n return true;\n\n const fileName = file.name.toLowerCase();\n const fileExtension = fileName.includes('.') ? fileName.slice(fileName.lastIndexOf('.')) : '';\n const fileType = file.type.toLowerCase();\n const acceptTypes = acceptString.split(',').map(value => value.trim().toLowerCase()).filter(Boolean);\n\n return acceptTypes.some((type) => {\n if (type.startsWith('.'))\n return type === fileExtension;\n if (type.endsWith('/*'))\n return fileType.startsWith(type.slice(0, -1));\n return type === fileType;\n });\n}\n\nfunction reportError(message: string): void {\n set(internalError, message);\n emit('error', message);\n}\n\nfunction clearInternalError(): void {\n set(internalError, '');\n}\n\nfunction validate(candidates: File[]): File[] | undefined {\n if (candidates.length === 0)\n return undefined;\n\n if (!multiple && candidates.length > 1) {\n reportError(t(RUI_I18N_KEYS.fileUpload.tooMany, 'Only one file can be uploaded'));\n return undefined;\n }\n\n for (const file of candidates) {\n if (!matchesAccept(file, accept)) {\n reportError(t(RUI_I18N_KEYS.fileUpload.wrongType, { accept }, `Only ${accept} files are allowed`));\n return undefined;\n }\n if (maxSize !== undefined && file.size > maxSize) {\n const size = formatFileSize(maxSize);\n reportError(t(RUI_I18N_KEYS.fileUpload.tooLarge, { name: file.name, size }, `${file.name} exceeds the maximum size of ${size}`));\n return undefined;\n }\n }\n\n return candidates;\n}\n\nfunction setFiles(candidates: File[]): void {\n const validated = validate(candidates);\n if (!validated)\n return;\n clearInternalError();\n set(modelValue, multiple ? validated : validated[0]);\n}\n\nfunction removeFile(target?: File): void {\n clearInternalError();\n const inputEl = get(input);\n if (inputEl)\n inputEl.value = '';\n\n if (!target || !multiple) {\n set(modelValue, multiple ? [] : undefined);\n return;\n }\n\n const remaining = get(files).filter(file => file !== target);\n set(modelValue, remaining);\n}\n\nfunction onSelect(event: Event): void {\n const target = event.target;\n if (!(target instanceof HTMLInputElement) || !target.files)\n return;\n const picked = Array.from(target.files);\n // the model owns the selection; a cleared input fires `change` for the same file again\n target.value = '';\n setFiles(picked);\n}\n\nfunction onDrop(dropped: File[] | null): void {\n if (disabled || noDrop || !dropped || dropped.length === 0)\n return;\n setFiles(dropped);\n}\n\nconst { isOverDropZone } = useDropZone(wrapper, {\n onDrop,\n});\n\nfunction openPicker(): void {\n if (disabled)\n return;\n get(input)?.click();\n}\n\nwatch(() => errorMessages, () => clearInternalError());\n\ndefineExpose({\n removeFile: () => removeFile(),\n openPicker,\n});\n</script>\n\n<template>\n <div v-bind=\"getRootAttrs($attrs)\">\n <div\n ref=\"wrapper\"\n class=\"p-4 border border-rui-outline dark:border-rui-neutral-400 rounded-rui-panel w-full relative border-dashed transition\"\n :class=\"{\n 'border-rui-primary! bg-rui-primary-subtle': isOverDropZone && !disabled && !noDrop,\n 'border-rui-error! border-solid! bg-rui-error-subtle': hasError,\n 'border-rui-success! border-solid! bg-rui-success-subtle': uploaded,\n 'opacity-60 pointer-events-none': disabled,\n }\"\n :data-disabled=\"disabled || undefined\"\n :data-uploaded=\"uploaded || undefined\"\n :data-error=\"hasError ? '' : undefined\"\n :data-drop-active=\"isOverDropZone || undefined\"\n >\n <div\n class=\"flex flex-col items-center justify-center gap-3\"\n :class=\"{ 'opacity-0': loading }\"\n >\n <slot\n name=\"preview\"\n :files=\"files\"\n :remove=\"() => removeFile()\"\n >\n <div\n v-if=\"hasFiles\"\n class=\"flex flex-col gap-1 w-full\"\n data-id=\"file-list\"\n >\n <div\n v-for=\"file in files\"\n :key=\"`${file.name}-${file.lastModified}`\"\n class=\"flex items-center gap-2 bg-rui-primary-soft dark:bg-rui-primary/20 rounded-full pl-3 pr-1 py-1\"\n data-id=\"file-item\"\n >\n <RuiIcon\n name=\"lu-file\"\n color=\"primary\"\n size=\"20\"\n />\n <div class=\"flex-1 overflow-hidden min-w-0\">\n <div\n class=\"text-sm leading-5 truncate\"\n :title=\"file.name\"\n >\n {{ file.name }}\n </div>\n <div class=\"text-rui-text-secondary text-xs leading-3\">\n {{ formatFileSize(file.size) }}\n </div>\n </div>\n <button\n type=\"button\"\n class=\"p-1 rounded-full hover:bg-rui-hover transition focus-visible:focus-ring outline-hidden\"\n :disabled=\"disabled\"\n :aria-label=\"t(RUI_I18N_KEYS.fileUpload.remove, { name: file.name }, `Remove ${file.name}`)\"\n data-id=\"remove-file\"\n @click=\"removeFile(file)\"\n >\n <RuiIcon\n name=\"lu-x\"\n size=\"16\"\n />\n </button>\n </div>\n </div>\n <div\n v-else\n class=\"h-10 w-10 rounded-full flex items-center justify-center\"\n :class=\"wellClasses[wellTone]\"\n data-id=\"icon-well\"\n >\n <slot name=\"icon\">\n <RuiIcon\n :name=\"wellIcon\"\n :color=\"wellTone\"\n />\n </slot>\n </div>\n </slot>\n\n <div class=\"text-center text-sm\">\n <slot name=\"description\">\n <div class=\"flex items-center justify-center gap-1 flex-wrap\">\n <span v-if=\"resolvedUploadText\">{{ resolvedUploadText }}</span>\n <button\n type=\"button\"\n class=\"text-rui-primary underline disabled:no-underline disabled:text-rui-text-disabled\"\n :class=\"{ 'first-letter:uppercase': !resolvedUploadText }\"\n :disabled=\"disabled\"\n data-id=\"open-picker\"\n @click=\"openPicker()\"\n >\n {{ hasFiles ? resolvedReplaceText : resolvedClickToUploadText }}\n </button>\n </div>\n </slot>\n </div>\n\n <input\n ref=\"input\"\n type=\"file\"\n :accept=\"accept\"\n :multiple=\"multiple\"\n :disabled=\"disabled\"\n hidden\n data-id=\"file-input\"\n v-bind=\"getNonRootAttrs($attrs, ['class', 'onChange'])\"\n @change=\"onSelect($event)\"\n />\n </div>\n\n <div\n v-if=\"loading\"\n class=\"flex flex-col items-center justify-center absolute h-full w-full top-0 left-0 gap-2\"\n data-id=\"loading\"\n >\n <RuiProgress\n circular\n :variant=\"progress === undefined ? 'indeterminate' : 'determinate'\"\n :value=\"progress\"\n color=\"primary\"\n size=\"40\"\n />\n <div\n v-if=\"progressLabel\"\n class=\"text-xs text-rui-text-secondary\"\n data-id=\"progress-label\"\n v-text=\"progressLabel\"\n />\n </div>\n </div>\n <RuiFormTextDetail\n v-if=\"!hideDetails\"\n :error-messages=\"combinedErrorMessages\"\n :hint=\"hint\"\n />\n </div>\n</template>\n"],"mappings":""}
|
|
1
|
+
{"version":3,"file":"RuiFileUpload.js","names":["_sfc_main"],"sources":["../../../../src/components/forms/file-upload/RuiFileUpload.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport type { RuiIcons } from '@/icons';\nimport RuiFormTextDetail from '@/components/helpers/RuiFormTextDetail.vue';\nimport RuiIcon from '@/components/icons/RuiIcon.vue';\nimport RuiProgress from '@/components/progress/RuiProgress.vue';\nimport { useRuiI8n } from '@/composables/use-rui-i18n';\nimport { RUI_I18N_KEYS } from '@/i18n/keys';\nimport { type FormDetails, useFormDetails, useFormTextDetail } from '@/utils/form-text-detail';\nimport { formatFileSize, getNonRootAttrs, getRootAttrs } from '@/utils/helpers';\n\nexport interface FileUploadProps {\n /** File type filter (HTML accept attribute syntax). e.g. '.csv', 'image/*', '.png,.jpg' */\n accept?: string;\n /** Allow multiple file selection. */\n multiple?: boolean;\n disabled?: boolean;\n /** Shows a loading overlay (consumer-driven, e.g. while uploading). */\n loading?: boolean;\n /** Upload progress 0-100. When provided alongside `loading`, the overlay shows a determinate progress bar with percentage. */\n progress?: number;\n /** Renders the success state border/icon. Consumer toggles back to false. */\n uploaded?: boolean;\n /** Optional max size per file in bytes; failures emit `error` and set internal error state. */\n maxSize?: number;\n /** External error messages (e.g. from Vuelidate). Shown via the standard form detail block. */\n errorMessages?: string | string[];\n /** Hint text below the dropzone. */\n hint?: string;\n /** Disable drag-and-drop; the dropzone becomes click-only (matches rotki.com use case). */\n noDrop?: boolean;\n /**\n * Replace the default \"Drag and drop or \\{button\\}\" copy. With `noDrop` the default is empty, so only the button shows.\n * The defaults of this and the other texts, error messages included, come from the `rui.file_upload.*` translation keys.\n */\n uploadText?: string;\n /** Label shown on the inline button when no file is selected. */\n clickToUploadText?: string;\n /** Label shown on the inline button when one or more files are already selected. */\n replaceText?: string;\n /** The line under the dropzone for its hint and errors: `reserve`, `auto` or `hidden`; falls back to the nearest `RuiFieldDefaults`, then the app default, then `reserve`. */\n details?: FormDetails;\n /** @deprecated Use `details=\"hidden\"`. Removed in 4.0. */\n hideDetails?: boolean;\n}\n\ndefineOptions({\n name: 'RuiFileUpload',\n inheritAttrs: false,\n});\n\nconst modelValue = defineModel<File | File[] | undefined>({ required: true });\n\nconst {\n accept = '*',\n multiple = false,\n disabled = false,\n loading = false,\n progress = undefined,\n uploaded = false,\n maxSize = undefined,\n errorMessages = [],\n hint = '',\n noDrop = false,\n uploadText = undefined,\n clickToUploadText = undefined,\n replaceText = undefined,\n details = undefined,\n hideDetails = false,\n} = defineProps<FileUploadProps>();\n\nconst emit = defineEmits<{\n error: [message: string];\n}>();\n\ndefineSlots<{\n preview?: (props: { files: File[]; remove: () => void }) => any;\n icon?: () => any;\n description?: () => any;\n}>();\n\nconst { t } = useRuiI8n();\n\nconst wrapper = useTemplateRef<HTMLDivElement>('wrapper');\nconst input = useTemplateRef<HTMLInputElement>('input');\nconst internalError = ref<string>('');\n\nconst files = computed<File[]>(() => {\n const value = get(modelValue);\n if (!value)\n return [];\n return Array.isArray(value) ? value : [value];\n});\n\nconst hasFiles = computed<boolean>(() => get(files).length > 0);\n\nconst progressLabel = computed<string | undefined>(() => {\n const value = progress;\n return value === undefined ? undefined : `${Math.round(value)}%`;\n});\n\nconst combinedErrorMessages = computed<string[]>(() => {\n const external = Array.isArray(errorMessages) ? errorMessages : [errorMessages];\n const internal = get(internalError);\n return [...external.filter(Boolean), ...(internal ? [internal] : [])];\n});\n\nconst { hasError } = useFormTextDetail(combinedErrorMessages, () => []);\nconst detailsMode = useFormDetails(() => details, () => hideDetails);\n\n// a click-only dropzone does not offer dragging\nconst resolvedUploadText = computed<string>(() => uploadText ?? (noDrop ? '' : t(RUI_I18N_KEYS.fileUpload.dropPrefix, 'Drag and drop or')));\nconst resolvedClickToUploadText = computed<string>(() => clickToUploadText ?? t(RUI_I18N_KEYS.fileUpload.clickToUpload, 'click to upload'));\nconst resolvedReplaceText = computed<string>(() => replaceText ?? t(RUI_I18N_KEYS.fileUpload.replace, 'replace file'));\n\ntype WellTone = 'primary' | 'error' | 'success';\n\n// the icon well follows the dropzone's state, like its border\nconst wellTone = computed<WellTone>(() => {\n if (get(hasError))\n return 'error';\n if (uploaded)\n return 'success';\n return 'primary';\n});\n\nconst wellIcon = computed<RuiIcons>(() => {\n const tone = get(wellTone);\n if (tone === 'error')\n return 'lu-circle-alert';\n if (tone === 'success')\n return 'lu-circle-check';\n return 'lu-file-up';\n});\n\n// the 10% tint vanishes on a dark page, so dark doubles it\nconst wellClasses: Record<WellTone, string> = {\n primary: 'bg-rui-primary-soft dark:bg-rui-primary/20',\n error: 'bg-rui-error-soft dark:bg-rui-error/20',\n success: 'bg-rui-success-soft dark:bg-rui-success/20',\n};\n\nfunction matchesAccept(file: File, acceptString: string): boolean {\n if (!acceptString || acceptString === '*' || acceptString === '*/*')\n return true;\n\n const fileName = file.name.toLowerCase();\n const fileExtension = fileName.includes('.') ? fileName.slice(fileName.lastIndexOf('.')) : '';\n const fileType = file.type.toLowerCase();\n const acceptTypes = acceptString.split(',').map(value => value.trim().toLowerCase()).filter(Boolean);\n\n return acceptTypes.some((type) => {\n if (type.startsWith('.'))\n return type === fileExtension;\n if (type.endsWith('/*'))\n return fileType.startsWith(type.slice(0, -1));\n return type === fileType;\n });\n}\n\nfunction reportError(message: string): void {\n set(internalError, message);\n emit('error', message);\n}\n\nfunction clearInternalError(): void {\n set(internalError, '');\n}\n\nfunction validate(candidates: File[]): File[] | undefined {\n if (candidates.length === 0)\n return undefined;\n\n if (!multiple && candidates.length > 1) {\n reportError(t(RUI_I18N_KEYS.fileUpload.tooMany, 'Only one file can be uploaded'));\n return undefined;\n }\n\n for (const file of candidates) {\n if (!matchesAccept(file, accept)) {\n reportError(t(RUI_I18N_KEYS.fileUpload.wrongType, { accept }, `Only ${accept} files are allowed`));\n return undefined;\n }\n if (maxSize !== undefined && file.size > maxSize) {\n const size = formatFileSize(maxSize);\n reportError(t(RUI_I18N_KEYS.fileUpload.tooLarge, { name: file.name, size }, `${file.name} exceeds the maximum size of ${size}`));\n return undefined;\n }\n }\n\n return candidates;\n}\n\nfunction setFiles(candidates: File[]): void {\n const validated = validate(candidates);\n if (!validated)\n return;\n clearInternalError();\n set(modelValue, multiple ? validated : validated[0]);\n}\n\nfunction removeFile(target?: File): void {\n clearInternalError();\n const inputEl = get(input);\n if (inputEl)\n inputEl.value = '';\n\n if (!target || !multiple) {\n set(modelValue, multiple ? [] : undefined);\n return;\n }\n\n const remaining = get(files).filter(file => file !== target);\n set(modelValue, remaining);\n}\n\nfunction onSelect(event: Event): void {\n const target = event.target;\n if (!(target instanceof HTMLInputElement) || !target.files)\n return;\n const picked = Array.from(target.files);\n // the model owns the selection; a cleared input fires `change` for the same file again\n target.value = '';\n setFiles(picked);\n}\n\nfunction onDrop(dropped: File[] | null): void {\n if (disabled || noDrop || !dropped || dropped.length === 0)\n return;\n setFiles(dropped);\n}\n\nconst { isOverDropZone } = useDropZone(wrapper, {\n onDrop,\n});\n\nfunction openPicker(): void {\n if (disabled)\n return;\n get(input)?.click();\n}\n\nwatch(() => errorMessages, () => clearInternalError());\n\ndefineExpose({\n removeFile: () => removeFile(),\n openPicker,\n});\n</script>\n\n<template>\n <div v-bind=\"getRootAttrs($attrs)\">\n <div\n ref=\"wrapper\"\n class=\"p-4 border border-rui-outline dark:border-rui-neutral-400 rounded-rui-panel w-full relative border-dashed transition\"\n :class=\"{\n 'border-rui-primary! bg-rui-primary-subtle': isOverDropZone && !disabled && !noDrop,\n 'border-rui-error! border-solid! bg-rui-error-subtle': hasError,\n 'border-rui-success! border-solid! bg-rui-success-subtle': uploaded,\n 'opacity-60 pointer-events-none': disabled,\n }\"\n :data-disabled=\"disabled || undefined\"\n :data-uploaded=\"uploaded || undefined\"\n :data-error=\"hasError ? '' : undefined\"\n :data-drop-active=\"isOverDropZone || undefined\"\n >\n <div\n class=\"flex flex-col items-center justify-center gap-3\"\n :class=\"{ 'opacity-0': loading }\"\n >\n <slot\n name=\"preview\"\n :files=\"files\"\n :remove=\"() => removeFile()\"\n >\n <div\n v-if=\"hasFiles\"\n class=\"flex flex-col gap-1 w-full\"\n data-id=\"file-list\"\n >\n <div\n v-for=\"file in files\"\n :key=\"`${file.name}-${file.lastModified}`\"\n class=\"flex items-center gap-2 bg-rui-primary-soft dark:bg-rui-primary/20 rounded-full pl-3 pr-1 py-1\"\n data-id=\"file-item\"\n >\n <RuiIcon\n name=\"lu-file\"\n color=\"primary\"\n size=\"20\"\n />\n <div class=\"flex-1 overflow-hidden min-w-0\">\n <div\n class=\"text-sm leading-5 truncate\"\n :title=\"file.name\"\n >\n {{ file.name }}\n </div>\n <div class=\"text-rui-text-secondary text-xs leading-3\">\n {{ formatFileSize(file.size) }}\n </div>\n </div>\n <button\n type=\"button\"\n class=\"p-1 rounded-full hover:bg-rui-hover transition focus-visible:focus-ring outline-hidden\"\n :disabled=\"disabled\"\n :aria-label=\"t(RUI_I18N_KEYS.fileUpload.remove, { name: file.name }, `Remove ${file.name}`)\"\n data-id=\"remove-file\"\n @click=\"removeFile(file)\"\n >\n <RuiIcon\n name=\"lu-x\"\n size=\"16\"\n />\n </button>\n </div>\n </div>\n <div\n v-else\n class=\"h-10 w-10 rounded-full flex items-center justify-center\"\n :class=\"wellClasses[wellTone]\"\n data-id=\"icon-well\"\n >\n <slot name=\"icon\">\n <RuiIcon\n :name=\"wellIcon\"\n :color=\"wellTone\"\n />\n </slot>\n </div>\n </slot>\n\n <div class=\"text-center text-sm\">\n <slot name=\"description\">\n <div class=\"flex items-center justify-center gap-1 flex-wrap\">\n <span v-if=\"resolvedUploadText\">{{ resolvedUploadText }}</span>\n <button\n type=\"button\"\n class=\"text-rui-primary underline disabled:no-underline disabled:text-rui-text-disabled\"\n :class=\"{ 'first-letter:uppercase': !resolvedUploadText }\"\n :disabled=\"disabled\"\n data-id=\"open-picker\"\n @click=\"openPicker()\"\n >\n {{ hasFiles ? resolvedReplaceText : resolvedClickToUploadText }}\n </button>\n </div>\n </slot>\n </div>\n\n <input\n ref=\"input\"\n type=\"file\"\n :accept=\"accept\"\n :multiple=\"multiple\"\n :disabled=\"disabled\"\n hidden\n data-id=\"file-input\"\n v-bind=\"getNonRootAttrs($attrs, ['class', 'onChange'])\"\n @change=\"onSelect($event)\"\n />\n </div>\n\n <div\n v-if=\"loading\"\n class=\"flex flex-col items-center justify-center absolute h-full w-full top-0 left-0 gap-2\"\n data-id=\"loading\"\n >\n <RuiProgress\n circular\n :variant=\"progress === undefined ? 'indeterminate' : 'determinate'\"\n :value=\"progress\"\n color=\"primary\"\n size=\"40\"\n />\n <div\n v-if=\"progressLabel\"\n class=\"text-xs text-rui-text-secondary\"\n data-id=\"progress-label\"\n v-text=\"progressLabel\"\n />\n </div>\n </div>\n <RuiFormTextDetail\n v-if=\"detailsMode !== 'hidden'\"\n :details=\"detailsMode\"\n :error-messages=\"combinedErrorMessages\"\n :hint=\"hint\"\n />\n </div>\n</template>\n"],"mappings":""}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { type FormDetails } from '../../../utils/form-text-detail.js';
|
|
1
2
|
export interface FileUploadProps {
|
|
2
3
|
/** File type filter (HTML accept attribute syntax). e.g. '.csv', 'image/*', '.png,.jpg' */
|
|
3
4
|
accept?: string;
|
|
@@ -27,7 +28,9 @@ export interface FileUploadProps {
|
|
|
27
28
|
clickToUploadText?: string;
|
|
28
29
|
/** Label shown on the inline button when one or more files are already selected. */
|
|
29
30
|
replaceText?: string;
|
|
30
|
-
/**
|
|
31
|
+
/** The line under the dropzone for its hint and errors: `reserve`, `auto` or `hidden`; falls back to the nearest `RuiFieldDefaults`, then the app default, then `reserve`. */
|
|
32
|
+
details?: FormDetails;
|
|
33
|
+
/** @deprecated Use `details="hidden"`. Removed in 4.0. */
|
|
31
34
|
hideDetails?: boolean;
|
|
32
35
|
}
|
|
33
36
|
type __VLS_Props = FileUploadProps;
|
package/dist/components/forms/file-upload/RuiFileUpload.vue_vue_type_script_setup_true_lang.js
CHANGED
|
@@ -3,7 +3,7 @@ import { useDropZone } from "../../../vendor/@vueuse/core/dist/index.js";
|
|
|
3
3
|
import { useRuiI8n } from "../../../composables/use-rui-i18n.js";
|
|
4
4
|
import RuiIcon_default from "../../icons/RuiIcon.js";
|
|
5
5
|
import RuiProgress_default from "../../progress/RuiProgress.js";
|
|
6
|
-
import { useFormTextDetail } from "../../../utils/form-text-detail.js";
|
|
6
|
+
import { useFormDetails, useFormTextDetail } from "../../../utils/form-text-detail.js";
|
|
7
7
|
import RuiFormTextDetail_default from "../../helpers/RuiFormTextDetail.js";
|
|
8
8
|
import { formatFileSize, getNonRootAttrs, getRootAttrs } from "../../../utils/helpers.js";
|
|
9
9
|
import { RUI_I18N_KEYS } from "../../../i18n/keys.js";
|
|
@@ -76,6 +76,7 @@ var RuiFileUpload_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*/ de
|
|
|
76
76
|
uploadText: { default: () => void 0 },
|
|
77
77
|
clickToUploadText: { default: () => void 0 },
|
|
78
78
|
replaceText: { default: () => void 0 },
|
|
79
|
+
details: { default: () => void 0 },
|
|
79
80
|
hideDetails: {
|
|
80
81
|
type: Boolean,
|
|
81
82
|
default: false
|
|
@@ -108,6 +109,7 @@ var RuiFileUpload_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*/ de
|
|
|
108
109
|
return [...external.filter(Boolean), ...internal ? [internal] : []];
|
|
109
110
|
});
|
|
110
111
|
const { hasError } = useFormTextDetail(combinedErrorMessages, () => []);
|
|
112
|
+
const detailsMode = useFormDetails(() => __props.details, () => __props.hideDetails);
|
|
111
113
|
const resolvedUploadText = computed(() => __props.uploadText ?? (__props.noDrop ? "" : t(RUI_I18N_KEYS.fileUpload.dropPrefix, "Drag and drop or")));
|
|
112
114
|
const resolvedClickToUploadText = computed(() => __props.clickToUploadText ?? t(RUI_I18N_KEYS.fileUpload.clickToUpload, "click to upload"));
|
|
113
115
|
const resolvedReplaceText = computed(() => __props.replaceText ?? t(RUI_I18N_KEYS.fileUpload.replace, "replace file"));
|
|
@@ -286,11 +288,16 @@ var RuiFileUpload_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*/ de
|
|
|
286
288
|
class: "text-xs text-rui-text-secondary",
|
|
287
289
|
"data-id": "progress-label",
|
|
288
290
|
textContent: toDisplayString(unref(progressLabel))
|
|
289
|
-
}, null, 8, _hoisted_13)) : createCommentVNode("", true)])) : createCommentVNode("", true)], 10, _hoisted_1),
|
|
291
|
+
}, null, 8, _hoisted_13)) : createCommentVNode("", true)])) : createCommentVNode("", true)], 10, _hoisted_1), unref(detailsMode) !== "hidden" ? (openBlock(), createBlock(RuiFormTextDetail_default, {
|
|
290
292
|
key: 0,
|
|
293
|
+
details: unref(detailsMode),
|
|
291
294
|
"error-messages": unref(combinedErrorMessages),
|
|
292
295
|
hint: __props.hint
|
|
293
|
-
}, null, 8, [
|
|
296
|
+
}, null, 8, [
|
|
297
|
+
"details",
|
|
298
|
+
"error-messages",
|
|
299
|
+
"hint"
|
|
300
|
+
])) : createCommentVNode("", true)], 16);
|
|
294
301
|
};
|
|
295
302
|
}
|
|
296
303
|
});
|
package/dist/components/forms/file-upload/RuiFileUpload.vue_vue_type_script_setup_true_lang.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"RuiFileUpload.vue_vue_type_script_setup_true_lang.js","names":["$attrs","_useModel","_openBlock","_createElementBlock","_normalizeProps","_guardReactiveProps","_unref","_createElementVNode","_normalizeClass","_renderSlot","_Fragment","_renderList","_createVNode","RuiIcon","_toDisplayString","_createCommentVNode","_mergeProps","RuiProgress","_createBlock","RuiFormTextDetail"],"sources":["../../../../src/components/forms/file-upload/RuiFileUpload.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport type { RuiIcons } from '@/icons';\nimport RuiFormTextDetail from '@/components/helpers/RuiFormTextDetail.vue';\nimport RuiIcon from '@/components/icons/RuiIcon.vue';\nimport RuiProgress from '@/components/progress/RuiProgress.vue';\nimport { useRuiI8n } from '@/composables/use-rui-i18n';\nimport { RUI_I18N_KEYS } from '@/i18n/keys';\nimport { useFormTextDetail } from '@/utils/form-text-detail';\nimport { formatFileSize, getNonRootAttrs, getRootAttrs } from '@/utils/helpers';\n\nexport interface FileUploadProps {\n /** File type filter (HTML accept attribute syntax). e.g. '.csv', 'image/*', '.png,.jpg' */\n accept?: string;\n /** Allow multiple file selection. */\n multiple?: boolean;\n disabled?: boolean;\n /** Shows a loading overlay (consumer-driven, e.g. while uploading). */\n loading?: boolean;\n /** Upload progress 0-100. When provided alongside `loading`, the overlay shows a determinate progress bar with percentage. */\n progress?: number;\n /** Renders the success state border/icon. Consumer toggles back to false. */\n uploaded?: boolean;\n /** Optional max size per file in bytes; failures emit `error` and set internal error state. */\n maxSize?: number;\n /** External error messages (e.g. from Vuelidate). Shown via the standard form detail block. */\n errorMessages?: string | string[];\n /** Hint text below the dropzone. */\n hint?: string;\n /** Disable drag-and-drop; the dropzone becomes click-only (matches rotki.com use case). */\n noDrop?: boolean;\n /**\n * Replace the default \"Drag and drop or \\{button\\}\" copy. With `noDrop` the default is empty, so only the button shows.\n * The defaults of this and the other texts, error messages included, come from the `rui.file_upload.*` translation keys.\n */\n uploadText?: string;\n /** Label shown on the inline button when no file is selected. */\n clickToUploadText?: string;\n /** Label shown on the inline button when one or more files are already selected. */\n replaceText?: string;\n /** Hide the bottom hint/error details strip. */\n hideDetails?: boolean;\n}\n\ndefineOptions({\n name: 'RuiFileUpload',\n inheritAttrs: false,\n});\n\nconst modelValue = defineModel<File | File[] | undefined>({ required: true });\n\nconst {\n accept = '*',\n multiple = false,\n disabled = false,\n loading = false,\n progress = undefined,\n uploaded = false,\n maxSize = undefined,\n errorMessages = [],\n hint = '',\n noDrop = false,\n uploadText = undefined,\n clickToUploadText = undefined,\n replaceText = undefined,\n hideDetails = false,\n} = defineProps<FileUploadProps>();\n\nconst emit = defineEmits<{\n error: [message: string];\n}>();\n\ndefineSlots<{\n preview?: (props: { files: File[]; remove: () => void }) => any;\n icon?: () => any;\n description?: () => any;\n}>();\n\nconst { t } = useRuiI8n();\n\nconst wrapper = useTemplateRef<HTMLDivElement>('wrapper');\nconst input = useTemplateRef<HTMLInputElement>('input');\nconst internalError = ref<string>('');\n\nconst files = computed<File[]>(() => {\n const value = get(modelValue);\n if (!value)\n return [];\n return Array.isArray(value) ? value : [value];\n});\n\nconst hasFiles = computed<boolean>(() => get(files).length > 0);\n\nconst progressLabel = computed<string | undefined>(() => {\n const value = progress;\n return value === undefined ? undefined : `${Math.round(value)}%`;\n});\n\nconst combinedErrorMessages = computed<string[]>(() => {\n const external = Array.isArray(errorMessages) ? errorMessages : [errorMessages];\n const internal = get(internalError);\n return [...external.filter(Boolean), ...(internal ? [internal] : [])];\n});\n\nconst { hasError } = useFormTextDetail(combinedErrorMessages, () => []);\n\n// a click-only dropzone does not offer dragging\nconst resolvedUploadText = computed<string>(() => uploadText ?? (noDrop ? '' : t(RUI_I18N_KEYS.fileUpload.dropPrefix, 'Drag and drop or')));\nconst resolvedClickToUploadText = computed<string>(() => clickToUploadText ?? t(RUI_I18N_KEYS.fileUpload.clickToUpload, 'click to upload'));\nconst resolvedReplaceText = computed<string>(() => replaceText ?? t(RUI_I18N_KEYS.fileUpload.replace, 'replace file'));\n\ntype WellTone = 'primary' | 'error' | 'success';\n\n// the icon well follows the dropzone's state, like its border\nconst wellTone = computed<WellTone>(() => {\n if (get(hasError))\n return 'error';\n if (uploaded)\n return 'success';\n return 'primary';\n});\n\nconst wellIcon = computed<RuiIcons>(() => {\n const tone = get(wellTone);\n if (tone === 'error')\n return 'lu-circle-alert';\n if (tone === 'success')\n return 'lu-circle-check';\n return 'lu-file-up';\n});\n\n// the 10% tint vanishes on a dark page, so dark doubles it\nconst wellClasses: Record<WellTone, string> = {\n primary: 'bg-rui-primary-soft dark:bg-rui-primary/20',\n error: 'bg-rui-error-soft dark:bg-rui-error/20',\n success: 'bg-rui-success-soft dark:bg-rui-success/20',\n};\n\nfunction matchesAccept(file: File, acceptString: string): boolean {\n if (!acceptString || acceptString === '*' || acceptString === '*/*')\n return true;\n\n const fileName = file.name.toLowerCase();\n const fileExtension = fileName.includes('.') ? fileName.slice(fileName.lastIndexOf('.')) : '';\n const fileType = file.type.toLowerCase();\n const acceptTypes = acceptString.split(',').map(value => value.trim().toLowerCase()).filter(Boolean);\n\n return acceptTypes.some((type) => {\n if (type.startsWith('.'))\n return type === fileExtension;\n if (type.endsWith('/*'))\n return fileType.startsWith(type.slice(0, -1));\n return type === fileType;\n });\n}\n\nfunction reportError(message: string): void {\n set(internalError, message);\n emit('error', message);\n}\n\nfunction clearInternalError(): void {\n set(internalError, '');\n}\n\nfunction validate(candidates: File[]): File[] | undefined {\n if (candidates.length === 0)\n return undefined;\n\n if (!multiple && candidates.length > 1) {\n reportError(t(RUI_I18N_KEYS.fileUpload.tooMany, 'Only one file can be uploaded'));\n return undefined;\n }\n\n for (const file of candidates) {\n if (!matchesAccept(file, accept)) {\n reportError(t(RUI_I18N_KEYS.fileUpload.wrongType, { accept }, `Only ${accept} files are allowed`));\n return undefined;\n }\n if (maxSize !== undefined && file.size > maxSize) {\n const size = formatFileSize(maxSize);\n reportError(t(RUI_I18N_KEYS.fileUpload.tooLarge, { name: file.name, size }, `${file.name} exceeds the maximum size of ${size}`));\n return undefined;\n }\n }\n\n return candidates;\n}\n\nfunction setFiles(candidates: File[]): void {\n const validated = validate(candidates);\n if (!validated)\n return;\n clearInternalError();\n set(modelValue, multiple ? validated : validated[0]);\n}\n\nfunction removeFile(target?: File): void {\n clearInternalError();\n const inputEl = get(input);\n if (inputEl)\n inputEl.value = '';\n\n if (!target || !multiple) {\n set(modelValue, multiple ? [] : undefined);\n return;\n }\n\n const remaining = get(files).filter(file => file !== target);\n set(modelValue, remaining);\n}\n\nfunction onSelect(event: Event): void {\n const target = event.target;\n if (!(target instanceof HTMLInputElement) || !target.files)\n return;\n const picked = Array.from(target.files);\n // the model owns the selection; a cleared input fires `change` for the same file again\n target.value = '';\n setFiles(picked);\n}\n\nfunction onDrop(dropped: File[] | null): void {\n if (disabled || noDrop || !dropped || dropped.length === 0)\n return;\n setFiles(dropped);\n}\n\nconst { isOverDropZone } = useDropZone(wrapper, {\n onDrop,\n});\n\nfunction openPicker(): void {\n if (disabled)\n return;\n get(input)?.click();\n}\n\nwatch(() => errorMessages, () => clearInternalError());\n\ndefineExpose({\n removeFile: () => removeFile(),\n openPicker,\n});\n</script>\n\n<template>\n <div v-bind=\"getRootAttrs($attrs)\">\n <div\n ref=\"wrapper\"\n class=\"p-4 border border-rui-outline dark:border-rui-neutral-400 rounded-rui-panel w-full relative border-dashed transition\"\n :class=\"{\n 'border-rui-primary! bg-rui-primary-subtle': isOverDropZone && !disabled && !noDrop,\n 'border-rui-error! border-solid! bg-rui-error-subtle': hasError,\n 'border-rui-success! border-solid! bg-rui-success-subtle': uploaded,\n 'opacity-60 pointer-events-none': disabled,\n }\"\n :data-disabled=\"disabled || undefined\"\n :data-uploaded=\"uploaded || undefined\"\n :data-error=\"hasError ? '' : undefined\"\n :data-drop-active=\"isOverDropZone || undefined\"\n >\n <div\n class=\"flex flex-col items-center justify-center gap-3\"\n :class=\"{ 'opacity-0': loading }\"\n >\n <slot\n name=\"preview\"\n :files=\"files\"\n :remove=\"() => removeFile()\"\n >\n <div\n v-if=\"hasFiles\"\n class=\"flex flex-col gap-1 w-full\"\n data-id=\"file-list\"\n >\n <div\n v-for=\"file in files\"\n :key=\"`${file.name}-${file.lastModified}`\"\n class=\"flex items-center gap-2 bg-rui-primary-soft dark:bg-rui-primary/20 rounded-full pl-3 pr-1 py-1\"\n data-id=\"file-item\"\n >\n <RuiIcon\n name=\"lu-file\"\n color=\"primary\"\n size=\"20\"\n />\n <div class=\"flex-1 overflow-hidden min-w-0\">\n <div\n class=\"text-sm leading-5 truncate\"\n :title=\"file.name\"\n >\n {{ file.name }}\n </div>\n <div class=\"text-rui-text-secondary text-xs leading-3\">\n {{ formatFileSize(file.size) }}\n </div>\n </div>\n <button\n type=\"button\"\n class=\"p-1 rounded-full hover:bg-rui-hover transition focus-visible:focus-ring outline-hidden\"\n :disabled=\"disabled\"\n :aria-label=\"t(RUI_I18N_KEYS.fileUpload.remove, { name: file.name }, `Remove ${file.name}`)\"\n data-id=\"remove-file\"\n @click=\"removeFile(file)\"\n >\n <RuiIcon\n name=\"lu-x\"\n size=\"16\"\n />\n </button>\n </div>\n </div>\n <div\n v-else\n class=\"h-10 w-10 rounded-full flex items-center justify-center\"\n :class=\"wellClasses[wellTone]\"\n data-id=\"icon-well\"\n >\n <slot name=\"icon\">\n <RuiIcon\n :name=\"wellIcon\"\n :color=\"wellTone\"\n />\n </slot>\n </div>\n </slot>\n\n <div class=\"text-center text-sm\">\n <slot name=\"description\">\n <div class=\"flex items-center justify-center gap-1 flex-wrap\">\n <span v-if=\"resolvedUploadText\">{{ resolvedUploadText }}</span>\n <button\n type=\"button\"\n class=\"text-rui-primary underline disabled:no-underline disabled:text-rui-text-disabled\"\n :class=\"{ 'first-letter:uppercase': !resolvedUploadText }\"\n :disabled=\"disabled\"\n data-id=\"open-picker\"\n @click=\"openPicker()\"\n >\n {{ hasFiles ? resolvedReplaceText : resolvedClickToUploadText }}\n </button>\n </div>\n </slot>\n </div>\n\n <input\n ref=\"input\"\n type=\"file\"\n :accept=\"accept\"\n :multiple=\"multiple\"\n :disabled=\"disabled\"\n hidden\n data-id=\"file-input\"\n v-bind=\"getNonRootAttrs($attrs, ['class', 'onChange'])\"\n @change=\"onSelect($event)\"\n />\n </div>\n\n <div\n v-if=\"loading\"\n class=\"flex flex-col items-center justify-center absolute h-full w-full top-0 left-0 gap-2\"\n data-id=\"loading\"\n >\n <RuiProgress\n circular\n :variant=\"progress === undefined ? 'indeterminate' : 'determinate'\"\n :value=\"progress\"\n color=\"primary\"\n size=\"40\"\n />\n <div\n v-if=\"progressLabel\"\n class=\"text-xs text-rui-text-secondary\"\n data-id=\"progress-label\"\n v-text=\"progressLabel\"\n />\n </div>\n </div>\n <RuiFormTextDetail\n v-if=\"!hideDetails\"\n :error-messages=\"combinedErrorMessages\"\n :hint=\"hint\"\n />\n </div>\n</template>\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"RuiFileUpload.vue_vue_type_script_setup_true_lang.js","names":["$attrs","_useModel","_openBlock","_createElementBlock","_normalizeProps","_guardReactiveProps","_unref","_createElementVNode","_normalizeClass","_renderSlot","_Fragment","_renderList","_createVNode","RuiIcon","_toDisplayString","_createCommentVNode","_mergeProps","RuiProgress","_createBlock","RuiFormTextDetail"],"sources":["../../../../src/components/forms/file-upload/RuiFileUpload.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport type { RuiIcons } from '@/icons';\nimport RuiFormTextDetail from '@/components/helpers/RuiFormTextDetail.vue';\nimport RuiIcon from '@/components/icons/RuiIcon.vue';\nimport RuiProgress from '@/components/progress/RuiProgress.vue';\nimport { useRuiI8n } from '@/composables/use-rui-i18n';\nimport { RUI_I18N_KEYS } from '@/i18n/keys';\nimport { type FormDetails, useFormDetails, useFormTextDetail } from '@/utils/form-text-detail';\nimport { formatFileSize, getNonRootAttrs, getRootAttrs } from '@/utils/helpers';\n\nexport interface FileUploadProps {\n /** File type filter (HTML accept attribute syntax). e.g. '.csv', 'image/*', '.png,.jpg' */\n accept?: string;\n /** Allow multiple file selection. */\n multiple?: boolean;\n disabled?: boolean;\n /** Shows a loading overlay (consumer-driven, e.g. while uploading). */\n loading?: boolean;\n /** Upload progress 0-100. When provided alongside `loading`, the overlay shows a determinate progress bar with percentage. */\n progress?: number;\n /** Renders the success state border/icon. Consumer toggles back to false. */\n uploaded?: boolean;\n /** Optional max size per file in bytes; failures emit `error` and set internal error state. */\n maxSize?: number;\n /** External error messages (e.g. from Vuelidate). Shown via the standard form detail block. */\n errorMessages?: string | string[];\n /** Hint text below the dropzone. */\n hint?: string;\n /** Disable drag-and-drop; the dropzone becomes click-only (matches rotki.com use case). */\n noDrop?: boolean;\n /**\n * Replace the default \"Drag and drop or \\{button\\}\" copy. With `noDrop` the default is empty, so only the button shows.\n * The defaults of this and the other texts, error messages included, come from the `rui.file_upload.*` translation keys.\n */\n uploadText?: string;\n /** Label shown on the inline button when no file is selected. */\n clickToUploadText?: string;\n /** Label shown on the inline button when one or more files are already selected. */\n replaceText?: string;\n /** The line under the dropzone for its hint and errors: `reserve`, `auto` or `hidden`; falls back to the nearest `RuiFieldDefaults`, then the app default, then `reserve`. */\n details?: FormDetails;\n /** @deprecated Use `details=\"hidden\"`. Removed in 4.0. */\n hideDetails?: boolean;\n}\n\ndefineOptions({\n name: 'RuiFileUpload',\n inheritAttrs: false,\n});\n\nconst modelValue = defineModel<File | File[] | undefined>({ required: true });\n\nconst {\n accept = '*',\n multiple = false,\n disabled = false,\n loading = false,\n progress = undefined,\n uploaded = false,\n maxSize = undefined,\n errorMessages = [],\n hint = '',\n noDrop = false,\n uploadText = undefined,\n clickToUploadText = undefined,\n replaceText = undefined,\n details = undefined,\n hideDetails = false,\n} = defineProps<FileUploadProps>();\n\nconst emit = defineEmits<{\n error: [message: string];\n}>();\n\ndefineSlots<{\n preview?: (props: { files: File[]; remove: () => void }) => any;\n icon?: () => any;\n description?: () => any;\n}>();\n\nconst { t } = useRuiI8n();\n\nconst wrapper = useTemplateRef<HTMLDivElement>('wrapper');\nconst input = useTemplateRef<HTMLInputElement>('input');\nconst internalError = ref<string>('');\n\nconst files = computed<File[]>(() => {\n const value = get(modelValue);\n if (!value)\n return [];\n return Array.isArray(value) ? value : [value];\n});\n\nconst hasFiles = computed<boolean>(() => get(files).length > 0);\n\nconst progressLabel = computed<string | undefined>(() => {\n const value = progress;\n return value === undefined ? undefined : `${Math.round(value)}%`;\n});\n\nconst combinedErrorMessages = computed<string[]>(() => {\n const external = Array.isArray(errorMessages) ? errorMessages : [errorMessages];\n const internal = get(internalError);\n return [...external.filter(Boolean), ...(internal ? [internal] : [])];\n});\n\nconst { hasError } = useFormTextDetail(combinedErrorMessages, () => []);\nconst detailsMode = useFormDetails(() => details, () => hideDetails);\n\n// a click-only dropzone does not offer dragging\nconst resolvedUploadText = computed<string>(() => uploadText ?? (noDrop ? '' : t(RUI_I18N_KEYS.fileUpload.dropPrefix, 'Drag and drop or')));\nconst resolvedClickToUploadText = computed<string>(() => clickToUploadText ?? t(RUI_I18N_KEYS.fileUpload.clickToUpload, 'click to upload'));\nconst resolvedReplaceText = computed<string>(() => replaceText ?? t(RUI_I18N_KEYS.fileUpload.replace, 'replace file'));\n\ntype WellTone = 'primary' | 'error' | 'success';\n\n// the icon well follows the dropzone's state, like its border\nconst wellTone = computed<WellTone>(() => {\n if (get(hasError))\n return 'error';\n if (uploaded)\n return 'success';\n return 'primary';\n});\n\nconst wellIcon = computed<RuiIcons>(() => {\n const tone = get(wellTone);\n if (tone === 'error')\n return 'lu-circle-alert';\n if (tone === 'success')\n return 'lu-circle-check';\n return 'lu-file-up';\n});\n\n// the 10% tint vanishes on a dark page, so dark doubles it\nconst wellClasses: Record<WellTone, string> = {\n primary: 'bg-rui-primary-soft dark:bg-rui-primary/20',\n error: 'bg-rui-error-soft dark:bg-rui-error/20',\n success: 'bg-rui-success-soft dark:bg-rui-success/20',\n};\n\nfunction matchesAccept(file: File, acceptString: string): boolean {\n if (!acceptString || acceptString === '*' || acceptString === '*/*')\n return true;\n\n const fileName = file.name.toLowerCase();\n const fileExtension = fileName.includes('.') ? fileName.slice(fileName.lastIndexOf('.')) : '';\n const fileType = file.type.toLowerCase();\n const acceptTypes = acceptString.split(',').map(value => value.trim().toLowerCase()).filter(Boolean);\n\n return acceptTypes.some((type) => {\n if (type.startsWith('.'))\n return type === fileExtension;\n if (type.endsWith('/*'))\n return fileType.startsWith(type.slice(0, -1));\n return type === fileType;\n });\n}\n\nfunction reportError(message: string): void {\n set(internalError, message);\n emit('error', message);\n}\n\nfunction clearInternalError(): void {\n set(internalError, '');\n}\n\nfunction validate(candidates: File[]): File[] | undefined {\n if (candidates.length === 0)\n return undefined;\n\n if (!multiple && candidates.length > 1) {\n reportError(t(RUI_I18N_KEYS.fileUpload.tooMany, 'Only one file can be uploaded'));\n return undefined;\n }\n\n for (const file of candidates) {\n if (!matchesAccept(file, accept)) {\n reportError(t(RUI_I18N_KEYS.fileUpload.wrongType, { accept }, `Only ${accept} files are allowed`));\n return undefined;\n }\n if (maxSize !== undefined && file.size > maxSize) {\n const size = formatFileSize(maxSize);\n reportError(t(RUI_I18N_KEYS.fileUpload.tooLarge, { name: file.name, size }, `${file.name} exceeds the maximum size of ${size}`));\n return undefined;\n }\n }\n\n return candidates;\n}\n\nfunction setFiles(candidates: File[]): void {\n const validated = validate(candidates);\n if (!validated)\n return;\n clearInternalError();\n set(modelValue, multiple ? validated : validated[0]);\n}\n\nfunction removeFile(target?: File): void {\n clearInternalError();\n const inputEl = get(input);\n if (inputEl)\n inputEl.value = '';\n\n if (!target || !multiple) {\n set(modelValue, multiple ? [] : undefined);\n return;\n }\n\n const remaining = get(files).filter(file => file !== target);\n set(modelValue, remaining);\n}\n\nfunction onSelect(event: Event): void {\n const target = event.target;\n if (!(target instanceof HTMLInputElement) || !target.files)\n return;\n const picked = Array.from(target.files);\n // the model owns the selection; a cleared input fires `change` for the same file again\n target.value = '';\n setFiles(picked);\n}\n\nfunction onDrop(dropped: File[] | null): void {\n if (disabled || noDrop || !dropped || dropped.length === 0)\n return;\n setFiles(dropped);\n}\n\nconst { isOverDropZone } = useDropZone(wrapper, {\n onDrop,\n});\n\nfunction openPicker(): void {\n if (disabled)\n return;\n get(input)?.click();\n}\n\nwatch(() => errorMessages, () => clearInternalError());\n\ndefineExpose({\n removeFile: () => removeFile(),\n openPicker,\n});\n</script>\n\n<template>\n <div v-bind=\"getRootAttrs($attrs)\">\n <div\n ref=\"wrapper\"\n class=\"p-4 border border-rui-outline dark:border-rui-neutral-400 rounded-rui-panel w-full relative border-dashed transition\"\n :class=\"{\n 'border-rui-primary! bg-rui-primary-subtle': isOverDropZone && !disabled && !noDrop,\n 'border-rui-error! border-solid! bg-rui-error-subtle': hasError,\n 'border-rui-success! border-solid! bg-rui-success-subtle': uploaded,\n 'opacity-60 pointer-events-none': disabled,\n }\"\n :data-disabled=\"disabled || undefined\"\n :data-uploaded=\"uploaded || undefined\"\n :data-error=\"hasError ? '' : undefined\"\n :data-drop-active=\"isOverDropZone || undefined\"\n >\n <div\n class=\"flex flex-col items-center justify-center gap-3\"\n :class=\"{ 'opacity-0': loading }\"\n >\n <slot\n name=\"preview\"\n :files=\"files\"\n :remove=\"() => removeFile()\"\n >\n <div\n v-if=\"hasFiles\"\n class=\"flex flex-col gap-1 w-full\"\n data-id=\"file-list\"\n >\n <div\n v-for=\"file in files\"\n :key=\"`${file.name}-${file.lastModified}`\"\n class=\"flex items-center gap-2 bg-rui-primary-soft dark:bg-rui-primary/20 rounded-full pl-3 pr-1 py-1\"\n data-id=\"file-item\"\n >\n <RuiIcon\n name=\"lu-file\"\n color=\"primary\"\n size=\"20\"\n />\n <div class=\"flex-1 overflow-hidden min-w-0\">\n <div\n class=\"text-sm leading-5 truncate\"\n :title=\"file.name\"\n >\n {{ file.name }}\n </div>\n <div class=\"text-rui-text-secondary text-xs leading-3\">\n {{ formatFileSize(file.size) }}\n </div>\n </div>\n <button\n type=\"button\"\n class=\"p-1 rounded-full hover:bg-rui-hover transition focus-visible:focus-ring outline-hidden\"\n :disabled=\"disabled\"\n :aria-label=\"t(RUI_I18N_KEYS.fileUpload.remove, { name: file.name }, `Remove ${file.name}`)\"\n data-id=\"remove-file\"\n @click=\"removeFile(file)\"\n >\n <RuiIcon\n name=\"lu-x\"\n size=\"16\"\n />\n </button>\n </div>\n </div>\n <div\n v-else\n class=\"h-10 w-10 rounded-full flex items-center justify-center\"\n :class=\"wellClasses[wellTone]\"\n data-id=\"icon-well\"\n >\n <slot name=\"icon\">\n <RuiIcon\n :name=\"wellIcon\"\n :color=\"wellTone\"\n />\n </slot>\n </div>\n </slot>\n\n <div class=\"text-center text-sm\">\n <slot name=\"description\">\n <div class=\"flex items-center justify-center gap-1 flex-wrap\">\n <span v-if=\"resolvedUploadText\">{{ resolvedUploadText }}</span>\n <button\n type=\"button\"\n class=\"text-rui-primary underline disabled:no-underline disabled:text-rui-text-disabled\"\n :class=\"{ 'first-letter:uppercase': !resolvedUploadText }\"\n :disabled=\"disabled\"\n data-id=\"open-picker\"\n @click=\"openPicker()\"\n >\n {{ hasFiles ? resolvedReplaceText : resolvedClickToUploadText }}\n </button>\n </div>\n </slot>\n </div>\n\n <input\n ref=\"input\"\n type=\"file\"\n :accept=\"accept\"\n :multiple=\"multiple\"\n :disabled=\"disabled\"\n hidden\n data-id=\"file-input\"\n v-bind=\"getNonRootAttrs($attrs, ['class', 'onChange'])\"\n @change=\"onSelect($event)\"\n />\n </div>\n\n <div\n v-if=\"loading\"\n class=\"flex flex-col items-center justify-center absolute h-full w-full top-0 left-0 gap-2\"\n data-id=\"loading\"\n >\n <RuiProgress\n circular\n :variant=\"progress === undefined ? 'indeterminate' : 'determinate'\"\n :value=\"progress\"\n color=\"primary\"\n size=\"40\"\n />\n <div\n v-if=\"progressLabel\"\n class=\"text-xs text-rui-text-secondary\"\n data-id=\"progress-label\"\n v-text=\"progressLabel\"\n />\n </div>\n </div>\n <RuiFormTextDetail\n v-if=\"detailsMode !== 'hidden'\"\n :details=\"detailsMode\"\n :error-messages=\"combinedErrorMessages\"\n :hint=\"hint\"\n />\n </div>\n</template>\n"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"RuiRadio.js","names":["_sfc_main"],"sources":["../../../../../src/components/forms/radio-button/radio/RuiRadio.vue"],"sourcesContent":["<script lang=\"ts\" setup generic=\"TValue\">\nimport type { ContextColorsType } from '@/consts/colors';\nimport { checkControlStyles } from '@/components/forms/check-control-styles';\nimport RuiFormTextDetail from '@/components/helpers/RuiFormTextDetail.vue';\nimport { useFormTextDetail } from '@/utils/form-text-detail';\nimport { getNonRootAttrs, getRootAttrs } from '@/utils/helpers';\n\nexport interface RadioProps<TValue> {\n value: TValue;\n disabled?: boolean;\n color?: ContextColorsType;\n size?: 'sm' | 'lg';\n label?: string;\n hint?: string;\n errorMessages?: string | string[];\n successMessages?: string | string[];\n hideDetails?: boolean;\n required?: boolean;\n}\n\ndefineOptions({\n name: 'RuiRadio',\n inheritAttrs: false,\n});\n\nconst modelValue = defineModel<TValue>({ required: false });\n\nconst {\n value,\n disabled = false,\n color,\n size,\n label = '',\n hint = '',\n errorMessages = [],\n successMessages = [],\n hideDetails = false,\n required = false,\n} = defineProps<RadioProps<TValue>>();\n\ndefineSlots<{\n default?: () => any;\n}>();\n\nconst { hasError, validation } = useFormTextDetail(\n () => errorMessages,\n () => successMessages,\n);\n\nconst selected = computed<boolean>(() => get(modelValue) === value);\n\nconst ui = computed<ReturnType<typeof checkControlStyles>>(() => checkControlStyles({\n size,\n shape: 'round',\n disabled,\n checked: get(selected),\n validation: get(validation),\n color: color ?? 'grey',\n}));\n\nfunction input(event: Event): void {\n const target = event.target;\n if (target instanceof HTMLInputElement && target.checked)\n set(modelValue, value);\n}\n</script>\n\n<template>\n <div v-bind=\"getRootAttrs($attrs)\">\n <label\n :class=\"ui.wrapper()\"\n :data-disabled=\"disabled || undefined\"\n :data-checked=\"selected || undefined\"\n :data-error=\"hasError ? '' : undefined\"\n >\n <input\n :checked=\"selected\"\n type=\"radio\"\n :class=\"ui.input()\"\n :disabled=\"disabled\"\n :aria-invalid=\"hasError\"\n v-bind=\"getNonRootAttrs($attrs)\"\n :value=\"value\"\n @input=\"input($event)\"\n />\n <div :class=\"ui.control()\">\n <span\n :class=\"ui.mark()\"\n data-mark\n :data-state=\"selected ? 'checked' : 'unchecked'\"\n >\n <span\n v-if=\"selected\"\n :class=\"ui.dot()\"\n />\n </span>\n </div>\n <div\n v-if=\"label || $slots.default\"\n :class=\"ui.label()\"\n >\n <slot>{{ label }}</slot>\n <span\n v-if=\"required\"\n class=\"text-rui-error\"\n >\n *\n </span>\n </div>\n </label>\n <RuiFormTextDetail\n v-if=\"
|
|
1
|
+
{"version":3,"file":"RuiRadio.js","names":["_sfc_main"],"sources":["../../../../../src/components/forms/radio-button/radio/RuiRadio.vue"],"sourcesContent":["<script lang=\"ts\" setup generic=\"TValue\">\nimport type { ContextColorsType } from '@/consts/colors';\nimport { checkControlStyles } from '@/components/forms/check-control-styles';\nimport RuiFormTextDetail from '@/components/helpers/RuiFormTextDetail.vue';\nimport { type FormDetails, useFormDetails, useFormTextDetail } from '@/utils/form-text-detail';\nimport { getNonRootAttrs, getRootAttrs } from '@/utils/helpers';\n\nexport interface RadioProps<TValue> {\n value: TValue;\n disabled?: boolean;\n color?: ContextColorsType;\n size?: 'sm' | 'lg';\n label?: string;\n hint?: string;\n errorMessages?: string | string[];\n successMessages?: string | string[];\n /** The line under the control for its hint and messages: `reserve`, `auto` or `hidden`; falls back to the nearest `RuiFieldDefaults`, then the app default, then `reserve`. */\n details?: FormDetails;\n /** @deprecated Use `details=\"hidden\"`. Removed in 4.0. */\n hideDetails?: boolean;\n /** Puts the hint or message under the label text instead of under the radio, so a list of radios keeps one text column. */\n detailsUnderLabel?: boolean;\n required?: boolean;\n}\n\ndefineOptions({\n name: 'RuiRadio',\n inheritAttrs: false,\n});\n\nconst modelValue = defineModel<TValue>({ required: false });\n\nconst {\n value,\n disabled = false,\n color,\n size,\n label = '',\n hint = '',\n errorMessages = [],\n successMessages = [],\n details = undefined,\n hideDetails = false,\n detailsUnderLabel = false,\n required = false,\n} = defineProps<RadioProps<TValue>>();\n\ndefineSlots<{\n default?: () => any;\n}>();\n\nconst { hasError, validation } = useFormTextDetail(\n () => errorMessages,\n () => successMessages,\n);\nconst detailsMode = useFormDetails(() => details, () => hideDetails);\n\nconst selected = computed<boolean>(() => get(modelValue) === value);\n\nconst ui = computed<ReturnType<typeof checkControlStyles>>(() => checkControlStyles({\n size,\n shape: 'round',\n disabled,\n checked: get(selected),\n validation: get(validation),\n color: color ?? 'grey',\n underLabel: detailsUnderLabel,\n}));\n\nfunction input(event: Event): void {\n const target = event.target;\n if (target instanceof HTMLInputElement && target.checked)\n set(modelValue, value);\n}\n</script>\n\n<template>\n <div v-bind=\"getRootAttrs($attrs)\">\n <label\n :class=\"ui.wrapper()\"\n :data-disabled=\"disabled || undefined\"\n :data-checked=\"selected || undefined\"\n :data-error=\"hasError ? '' : undefined\"\n >\n <input\n :checked=\"selected\"\n type=\"radio\"\n :class=\"ui.input()\"\n :disabled=\"disabled\"\n :aria-invalid=\"hasError\"\n v-bind=\"getNonRootAttrs($attrs)\"\n :value=\"value\"\n @input=\"input($event)\"\n />\n <div :class=\"ui.control()\">\n <span\n :class=\"ui.mark()\"\n data-mark\n :data-state=\"selected ? 'checked' : 'unchecked'\"\n >\n <span\n v-if=\"selected\"\n :class=\"ui.dot()\"\n />\n </span>\n </div>\n <div\n v-if=\"label || $slots.default\"\n :class=\"ui.label()\"\n >\n <slot>{{ label }}</slot>\n <span\n v-if=\"required\"\n class=\"text-rui-error\"\n >\n *\n </span>\n </div>\n </label>\n <RuiFormTextDetail\n v-if=\"detailsMode !== 'hidden'\"\n :class=\"ui.details()\"\n :details=\"detailsMode\"\n :error-messages=\"errorMessages\"\n :success-messages=\"successMessages\"\n :hint=\"hint\"\n />\n </div>\n</template>\n"],"mappings":""}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { ContextColorsType } from '../../../../consts/colors.js';
|
|
2
|
+
import { type FormDetails } from '../../../../utils/form-text-detail.js';
|
|
2
3
|
export interface RadioProps<TValue> {
|
|
3
4
|
value: TValue;
|
|
4
5
|
disabled?: boolean;
|
|
@@ -8,7 +9,12 @@ export interface RadioProps<TValue> {
|
|
|
8
9
|
hint?: string;
|
|
9
10
|
errorMessages?: string | string[];
|
|
10
11
|
successMessages?: string | string[];
|
|
12
|
+
/** The line under the control for its hint and messages: `reserve`, `auto` or `hidden`; falls back to the nearest `RuiFieldDefaults`, then the app default, then `reserve`. */
|
|
13
|
+
details?: FormDetails;
|
|
14
|
+
/** @deprecated Use `details="hidden"`. Removed in 4.0. */
|
|
11
15
|
hideDetails?: boolean;
|
|
16
|
+
/** Puts the hint or message under the label text instead of under the radio, so a list of radios keeps one text column. */
|
|
17
|
+
detailsUnderLabel?: boolean;
|
|
12
18
|
required?: boolean;
|
|
13
19
|
}
|
|
14
20
|
declare const __VLS_export: <TValue>(__VLS_props: NonNullable<Awaited<typeof __VLS_setup>>["props"], __VLS_ctx?: __VLS_PrettifyLocal<Pick<NonNullable<Awaited<typeof __VLS_setup>>, "attrs" | "emit" | "slots">>, __VLS_exposed?: NonNullable<Awaited<typeof __VLS_setup>>["expose"], __VLS_setup?: Promise<{
|