From 0d44c48bd1b22877f4c7604d3541dd7498b05c8c Mon Sep 17 00:00:00 2001 From: Stepan Date: Tue, 31 Jan 2023 17:33:10 +0300 Subject: [PATCH 01/22] WDS-125 add options storing in model for remote searching --- .../ui/src/components/Select/SDropdown.vue | 1 + packages/ui/src/components/Select/SSelect.vue | 1 + .../ui/src/components/Select/SSelectBase.vue | 12 ++- .../src/components/Select/SSelectDropdown.vue | 2 +- packages/ui/src/components/Select/api.ts | 1 + .../src/components/Select/use-model.spec.ts | 28 ++++- .../ui/src/components/Select/use-model.ts | 100 ++++++++++++++++-- packages/ui/stories/SSelect.stories.ts | 31 +++++- 8 files changed, 160 insertions(+), 16 deletions(-) diff --git a/packages/ui/src/components/Select/SDropdown.vue b/packages/ui/src/components/Select/SDropdown.vue index 7b67dec2..84eaf269 100644 --- a/packages/ui/src/components/Select/SDropdown.vue +++ b/packages/ui/src/components/Select/SDropdown.vue @@ -16,6 +16,7 @@ const props = defineProps<{ noAutoClose?: boolean loading?: boolean dropdownSearch?: boolean + remoteSearch?: boolean }>() const buttonType = computed(() => (props.inline ? SelectButtonType.Inline : SelectButtonType.Default)) diff --git a/packages/ui/src/components/Select/SSelect.vue b/packages/ui/src/components/Select/SSelect.vue index 6ae97cc2..c35a2964 100644 --- a/packages/ui/src/components/Select/SSelect.vue +++ b/packages/ui/src/components/Select/SSelect.vue @@ -15,6 +15,7 @@ const props = defineProps<{ noAutoClose?: boolean loading?: boolean dropdownSearch?: boolean + remoteSearch?: boolean }>() const defaultOptionType = computed(() => (props.multiple ? SelectOptionType.Checkbox : SelectOptionType.Radio)) diff --git a/packages/ui/src/components/Select/SSelectBase.vue b/packages/ui/src/components/Select/SSelectBase.vue index da8812d9..29d4768d 100644 --- a/packages/ui/src/components/Select/SSelectBase.vue +++ b/packages/ui/src/components/Select/SSelectBase.vue @@ -55,7 +55,12 @@ const props = withDefaults( /** * Adds search field to dropdown */ - dropdownSearch: boolean + dropdownSearch?: boolean + + /** + * Turns off options filtering by select itself. When false select filters by option's label. Adds chips + */ + remoteSearch?: boolean }>(), { size: SelectSize.Md, @@ -69,6 +74,7 @@ const props = withDefaults( loading: false, sameWidthPopper: false, dropdownSearch: false, + remoteSearch: false, }, ) @@ -78,12 +84,13 @@ const emit = defineEmits<{ }>() const model = useVModel(props, 'modelValue', emit) -const { multiple, disabled, loading, options, size, label, noAutoClose } = toRefs(props) +const { multiple, disabled, loading, options, size, label, noAutoClose, remoteSearch } = toRefs(props) const modeling = useSelectModel({ model, multiple, options, + storeSelectedOptions: remoteSearch, singleModeAutoClose: not(noAutoClose), onAutoClose: () => togglePopper(false), }) @@ -116,6 +123,7 @@ const api: SelectApi = reactive({ size, noAutoClose, searchQuery, + remoteSearch, updateSearchQuery, }) diff --git a/packages/ui/src/components/Select/SSelectDropdown.vue b/packages/ui/src/components/Select/SSelectDropdown.vue index 7612019d..835fd9b2 100644 --- a/packages/ui/src/components/Select/SSelectDropdown.vue +++ b/packages/ui/src/components/Select/SSelectDropdown.vue @@ -30,7 +30,7 @@ const optionGroups: ComputedRef = computed(() => { const isSearching = eagerComputed(() => props.search && api.searchQuery) const shownOptionGroups: ComputedRef = computed(() => { - if (!api.searchQuery) { + if (!api.searchQuery || api.remoteSearch) { return optionGroups.value } diff --git a/packages/ui/src/components/Select/api.ts b/packages/ui/src/components/Select/api.ts index 9321fbde..47fd2956 100644 --- a/packages/ui/src/components/Select/api.ts +++ b/packages/ui/src/components/Select/api.ts @@ -12,6 +12,7 @@ export interface SelectApi extends UnwrapRef> { readonly size: SelectSize readonly noAutoClose: boolean readonly searchQuery: string + readonly remoteSearch: boolean readonly isMenuOpened: boolean /** diff --git a/packages/ui/src/components/Select/use-model.spec.ts b/packages/ui/src/components/Select/use-model.spec.ts index a8b0c290..77aa2d37 100644 --- a/packages/ui/src/components/Select/use-model.spec.ts +++ b/packages/ui/src/components/Select/use-model.spec.ts @@ -9,10 +9,14 @@ const OPTIONS = [ ] describe('Auto-closing', () => { - function useFactory(props: Except, 'options' | 'multiple'>, mode: 'single' | 'multi') { + function useFactory( + props: Except, 'options' | 'multiple' | 'storeSelectedOptions'>, + mode: 'single' | 'multi', + ) { return useSelectModel({ options: ref(OPTIONS), multiple: ref(mode === 'multi' ? true : false), + storeSelectedOptions: ref(false), ...props, }) } @@ -85,3 +89,25 @@ describe('Auto-closing', () => { expect(onAutoClose).not.toBeCalled() }) }) + +describe('Storing options', () => { + test('when select option and then it removed from options list it is selected anyway', () => { + const changingOptions = ref(OPTIONS) + const modeling = useSelectModel({ + model: ref(null), + options: changingOptions, + multiple: ref(true), + storeSelectedOptions: ref(true), + singleModeAutoClose: ref(true), + onAutoClose: () => {}, + }) + + modeling.select('thin') + modeling.select('regular') + modeling.select('deep') + + changingOptions.value = [OPTIONS[1], OPTIONS[2]] + + expect(modeling.selectedOptions.value).toHaveLength(3) + }) +}) diff --git a/packages/ui/src/components/Select/use-model.ts b/packages/ui/src/components/Select/use-model.ts index 0b5064af..3fbd002b 100644 --- a/packages/ui/src/components/Select/use-model.ts +++ b/packages/ui/src/components/Select/use-model.ts @@ -31,7 +31,12 @@ export interface UseSelectModelParams { multiple: Ref model: Ref options: Ref[] | SelectOptionGroup[]> - + /** + * Enables remembering options for async search in multiple select when option list + * changes on search query changes. If no option creates option without label (e.g. for cases when model changes + * from outside and has values that have no options for them) + */ + storeSelectedOptions: Ref singleModeAutoClose: Ref /** * Should be used to actually perform menu closing @@ -45,6 +50,7 @@ export function useSelectModel({ options, singleModeAutoClose, onAutoClose, + storeSelectedOptions }: UseSelectModelParams): UseSelectModelReturn { const triggerAutoClose = () => singleModeAutoClose.value && onAutoClose() @@ -59,6 +65,56 @@ export function useSelectModel({ return new Set(modelAsArray.value) }) + const optionItems = computed(() => { + if (isSelectOptions(options.value)) { + return options.value + } + + return options.value.flatMap((x) => x.items) + }) + + const storedOptions = shallowReactive(new Map>()) + + let modelChangedManually = false + watch(modelAsArray, () => { + if (modelChangedManually) { + modelChangedManually = false + + return + } + + if (!storeSelectedOptions.value) { + return + } + + storedOptions.forEach((_, value) => { + if (!modelAsArray.value.includes(value)) { + storedOptions.delete(value) + } + }) + modelAsArray.value.forEach(rememberOption) + }, { immediate: true, flush: 'sync' }) + + function rememberOption(value: T) { + if (storedOptions.has(value)) { + return + } + + const option = optionItems.value.find(x => x.value === value) + + if (option) { + storedOptions.set(option.value, option) + + return; + } + + storedOptions.set(value, { label: '', value }) + } + + function forgetOption(value: T) { + storedOptions.delete(value) + } + function isValueSelected(value: T): boolean { return selectedSet.value.has(value) } @@ -69,8 +125,14 @@ export function useSelectModel({ function select(value: T): void { if (multiple.value) { + modelChangedManually = true model.value = [...new Set([...modelAsArray.value, value])] + + if (storeSelectedOptions.value) { + rememberOption(value) + } } else { + modelChangedManually = true model.value = value triggerAutoClose() } @@ -78,8 +140,14 @@ export function useSelectModel({ function unselect(value: T) { if (multiple.value) { + modelChangedManually = true model.value = modelAsArray.value.filter((x) => x !== value) + + if (storeSelectedOptions.value) { + forgetOption(value) + } } else { + modelChangedManually = true model.value = null triggerAutoClose() } @@ -90,26 +158,38 @@ export function useSelectModel({ } function toggleGroupSelection(optionGroup: SelectOptionGroup): void { + const optionGroupValues = optionGroup.items.map((x) => x.value) + if (isGroupSelected(optionGroup)) { - const optionGroupSet = new Set(optionGroup.items.map((x) => x.value)) - model.value = modelAsArray.value.filter((x) => !optionGroupSet.has(x)) + const optionGroupSet = new Set(optionGroupValues) + const newModel = modelAsArray.value.filter((x) => !optionGroupSet.has(x)) + modelChangedManually = true + model.value = newModel + + if (storeSelectedOptions.value) { + newModel.forEach(forgetOption) + } return } - model.value = [...new Set([...modelAsArray.value, ...optionGroup.items.map((x) => x.value)])] + const newModel = [...new Set([...modelAsArray.value, ...optionGroupValues])] + modelChangedManually = true + model.value = newModel + + if (storeSelectedOptions.value) { + newModel.forEach(rememberOption) + } } - const optionItems = computed(() => { - if (isSelectOptions(options.value)) { - return options.value + const selectedOptions = computed[]>(() => { + if (storeSelectedOptions.value) { + return [...storedOptions.values()] } - return options.value.flatMap((x) => x.items) + return optionItems.value.filter((x) => isValueSelected(x.value)) }) - const selectedOptions = computed(() => optionItems.value.filter((x) => isValueSelected(x.value))) - const isSomethingSelected = computed(() => !!selectedSet.value.size) const isModelAnArray = computed(() => Array.isArray(model.value)) diff --git a/packages/ui/stories/SSelect.stories.ts b/packages/ui/stories/SSelect.stories.ts index 777cd384..d93dc8ee 100644 --- a/packages/ui/stories/SSelect.stories.ts +++ b/packages/ui/stories/SSelect.stories.ts @@ -205,16 +205,30 @@ Dropdown.args = commonArgs export const WithSearch = defineStory((args) => ({ components: { SSelect, SDropdown }, setup() { + const asyncOptions = shallowRef([...OPTIONS]) + const isLoadingAsyncOptions = ref(false) + + async function handleSearch(value: string) { + isLoadingAsyncOptions.value = true + asyncOptions.value = await new Promise((resolve) => { + setTimeout(() => resolve(OPTIONS.filter((x) => new RegExp(value, 'i').test(x.label))), 1000) + }) + isLoadingAsyncOptions.value = false + } + return { OPTION_GROUPS, + asyncOptions, + isLoadingAsyncOptions, model: ref(['en', 'jp']), + handleSearch, args, } }, template: ` - ({ :option-type="args.optionType" dropdown-search /> + `, })) From 0c23dddfdac27b402c096c06f1f81e108fcb1b6d Mon Sep 17 00:00:00 2001 From: Stepan Date: Fri, 3 Feb 2023 09:16:39 +0300 Subject: [PATCH 02/22] WDS-125 add features to STextField to prepare for select with search --- .changeset/eleven-donuts-sin.md | 5 + .changeset/nine-vans-dress.md | 5 + packages/ui/etc/api/ui.api.md | 117 ++++++++++++------ .../src/components/TextField/STextField.vue | 99 ++++++++++++--- 4 files changed, 168 insertions(+), 58 deletions(-) create mode 100644 .changeset/eleven-donuts-sin.md create mode 100644 .changeset/nine-vans-dress.md diff --git a/.changeset/eleven-donuts-sin.md b/.changeset/eleven-donuts-sin.md new file mode 100644 index 00000000..a2de00b0 --- /dev/null +++ b/.changeset/eleven-donuts-sin.md @@ -0,0 +1,5 @@ +--- +'@soramitsu-ui/ui': minor +--- + +feat(`STextField`): add `passiveAppend` prop to make append part focus input on click diff --git a/.changeset/nine-vans-dress.md b/.changeset/nine-vans-dress.md new file mode 100644 index 00000000..eddbcfb1 --- /dev/null +++ b/.changeset/nine-vans-dress.md @@ -0,0 +1,5 @@ +--- +'@soramitsu-ui/ui': minor +--- + +feat(`STextField`): add `prefix` slot - inline elements before input and `filledState` prop to use filled state when prefix presents diff --git a/packages/ui/etc/api/ui.api.md b/packages/ui/etc/api/ui.api.md index 54e1f459..2b3b1ed8 100644 --- a/packages/ui/etc/api/ui.api.md +++ b/packages/ui/etc/api/ui.api.md @@ -367,7 +367,7 @@ showCloseBtn: boolean; // Warning: (ae-forgotten-export) The symbol "__VLS_TypePropsToRuntimeProps" needs to be exported by the entry point lib.d.ts // // @public (undocumented) -export const SBadge: DefineComponent<__VLS_WithDefaults_18<__VLS_TypePropsToRuntimeProps_21<{ +export const SBadge: DefineComponent<__VLS_WithDefaults_19<__VLS_TypePropsToRuntimeProps_22<{ type?: "info" | "warning" | "error" | "active" | "debug" | "pending" | undefined; colorBackground?: boolean | undefined; withBorder?: boolean | undefined; @@ -377,7 +377,7 @@ type: string; colorBackground: boolean; withBorder: boolean; onlyMarker: boolean; -}>, {}, unknown, {}, {}, ComponentOptionsMixin, ComponentOptionsMixin, Record, string, VNodeProps & AllowedComponentProps & ComponentCustomProps, Readonly, {}, unknown, {}, {}, ComponentOptionsMixin, ComponentOptionsMixin, Record, string, VNodeProps & AllowedComponentProps & ComponentCustomProps, Readonly; // Warning: (ae-forgotten-export) The symbol "Props" needs to be exported by the entry point lib.d.ts // // @public (undocumented) -export const SCheckboxSolo: DefineComponent<__VLS_WithDefaults_19<__VLS_TypePropsToRuntimeProps_22, { +export const SCheckboxSolo: DefineComponent<__VLS_WithDefaults_20<__VLS_TypePropsToRuntimeProps_23, { modelValue: boolean; type: string; size: string; -}>, {}, unknown, {}, {}, ComponentOptionsMixin, ComponentOptionsMixin, "update:modelValue"[], "update:modelValue", VNodeProps & AllowedComponentProps & ComponentCustomProps, Readonly, { +}>, {}, unknown, {}, {}, ComponentOptionsMixin, ComponentOptionsMixin, "update:modelValue"[], "update:modelValue", VNodeProps & AllowedComponentProps & ComponentCustomProps, Readonly, { modelValue: boolean; type: string; size: string; @@ -495,11 +495,11 @@ size: "md" | "lg" | "xl"; // Warning: (ae-forgotten-export) The symbol "__VLS_TypePropsToRuntimeProps" needs to be exported by the entry point lib.d.ts // // @public (undocumented) -export const SCollapseTransition: DefineComponent<__VLS_WithDefaults_20<__VLS_TypePropsToRuntimeProps_23<{ +export const SCollapseTransition: DefineComponent<__VLS_WithDefaults_21<__VLS_TypePropsToRuntimeProps_24<{ duration?: string | undefined; }>, { duration: string; -}>, {}, unknown, {}, {}, ComponentOptionsMixin, ComponentOptionsMixin, Record, string, VNodeProps & AllowedComponentProps & ComponentCustomProps, Readonly, {}, unknown, {}, {}, ComponentOptionsMixin, ComponentOptionsMixin, Record, string, VNodeProps & AllowedComponentProps & ComponentCustomProps, Readonly, { duration: string; @@ -512,12 +512,12 @@ duration: string; // Warning: (ae-forgotten-export) The symbol "Props" needs to be exported by the entry point lib.d.ts // // @public (undocumented) -export const SDatePicker: DefineComponent<__VLS_WithDefaults_14<__VLS_TypePropsToRuntimeProps_17, { +export const SDatePicker: DefineComponent<__VLS_WithDefaults_15<__VLS_TypePropsToRuntimeProps_18, { type: string; time: boolean; disabled: boolean; shortcuts: () => DatePickerOptions; -}>, {}, unknown, {}, {}, ComponentOptionsMixin, ComponentOptionsMixin, "update:modelValue"[], "update:modelValue", VNodeProps & AllowedComponentProps & ComponentCustomProps, Readonly, { +}>, {}, unknown, {}, {}, ComponentOptionsMixin, ComponentOptionsMixin, "update:modelValue"[], "update:modelValue", VNodeProps & AllowedComponentProps & ComponentCustomProps, Readonly, { type: string; time: boolean; disabled: boolean; @@ -546,6 +546,7 @@ inline?: boolean | undefined; noAutoClose?: boolean | undefined; loading?: boolean | undefined; dropdownSearch?: boolean | undefined; +remoteSearch?: boolean | undefined; }>, {}, unknown, {}, {}, ComponentOptionsMixin, ComponentOptionsMixin, Record, string, VNodeProps & AllowedComponentProps & ComponentCustomProps, Readonly[] | SelectOptionGroup[] | undefined; @@ -558,6 +559,7 @@ inline?: boolean | undefined; noAutoClose?: boolean | undefined; loading?: boolean | undefined; dropdownSearch?: boolean | undefined; +remoteSearch?: boolean | undefined; }>>>, {}>; // @public (undocumented) @@ -583,6 +585,8 @@ export interface SelectApi extends UnwrapRef> { // (undocumented) readonly options: UnwrapRef[] | SelectOptionGroup[]>; // (undocumented) + readonly remoteSearch: boolean; + // (undocumented) readonly searchQuery: string; // (undocumented) readonly size: SelectSize; @@ -770,7 +774,7 @@ close: boolean; // Warning: (ae-forgotten-export) The symbol "__VLS_TypePropsToRuntimeProps" needs to be exported by the entry point lib.d.ts // // @public (undocumented) -export const SNavigationMenu: DefineComponent<__VLS_WithDefaults_24<__VLS_TypePropsToRuntimeProps_27<{ +export const SNavigationMenu: DefineComponent<__VLS_WithDefaults_25<__VLS_TypePropsToRuntimeProps_28<{ modelValue?: string | undefined; collapsed?: boolean | undefined; }>, { @@ -778,7 +782,7 @@ modelValue: string; collapsed: boolean; }>, {}, unknown, {}, {}, ComponentOptionsMixin, ComponentOptionsMixin, { "update:modelValue": (value: string) => void; -}, string, VNodeProps & AllowedComponentProps & ComponentCustomProps, Readonly, { @@ -795,9 +799,9 @@ collapsed: boolean; // Warning: (ae-forgotten-export) The symbol "__VLS_TypePropsToRuntimeProps" needs to be exported by the entry point lib.d.ts // // @public (undocumented) -export const SNavigationMenuItem: DefineComponent<__VLS_WithDefaults_23<__VLS_TypePropsToRuntimeProps_26<{ +export const SNavigationMenuItem: DefineComponent<__VLS_WithDefaults_24<__VLS_TypePropsToRuntimeProps_27<{ value: string; -}>, {}>, {}, unknown, {}, {}, ComponentOptionsMixin, ComponentOptionsMixin, Record, string, VNodeProps & AllowedComponentProps & ComponentCustomProps, Readonly, {}>, {}, unknown, {}, {}, ComponentOptionsMixin, ComponentOptionsMixin, Record, string, VNodeProps & AllowedComponentProps & ComponentCustomProps, Readonly, {}>>>, {}>; @@ -846,7 +850,7 @@ export const SNotificationsProvider: FunctionalComponent<{ // Warning: (ae-forgotten-export) The symbol "__VLS_TypePropsToRuntimeProps" needs to be exported by the entry point lib.d.ts // // @public (undocumented) -export const SPagination: DefineComponent<__VLS_WithDefaults_26<__VLS_TypePropsToRuntimeProps_29<{ +export const SPagination: DefineComponent<__VLS_WithDefaults_27<__VLS_TypePropsToRuntimeProps_30<{ total?: number | undefined; pageSize?: number | null | undefined; currentPage?: number | undefined; @@ -866,7 +870,7 @@ sizesLabel: string; "update:currentPage": (value: number) => void; } & { "update:pageSize": (value: number) => void; -}, string, VNodeProps & AllowedComponentProps & ComponentCustomProps, Readonly; // Warning: (ae-forgotten-export) The symbol "__VLS_TypePropsToRuntimeProps" needs to be exported by the entry point lib.d.ts // // @public (undocumented) -export const SProgressBar: DefineComponent<__VLS_WithDefaults_15<__VLS_TypePropsToRuntimeProps_18<{ +export const SProgressBar: DefineComponent<__VLS_WithDefaults_16<__VLS_TypePropsToRuntimeProps_19<{ percent?: number | undefined; lineHeight?: number | undefined; }>, { percent: number; lineHeight: number; -}>, {}, unknown, {}, {}, ComponentOptionsMixin, ComponentOptionsMixin, Record, string, VNodeProps & AllowedComponentProps & ComponentCustomProps, Readonly, {}, unknown, {}, {}, ComponentOptionsMixin, ComponentOptionsMixin, Record, string, VNodeProps & AllowedComponentProps & ComponentCustomProps, Readonly, { @@ -1025,11 +1029,11 @@ percent: number; // Warning: (ae-forgotten-export) The symbol "Props" needs to be exported by the entry point lib.d.ts // // @public (undocumented) -export const SRadio: DefineComponent<__VLS_WithDefaults_21<__VLS_TypePropsToRuntimeProps_24, { +export const SRadio: DefineComponent<__VLS_WithDefaults_22<__VLS_TypePropsToRuntimeProps_25, { disabled: boolean; type: string; size: string; -}>, {}, unknown, {}, {}, ComponentOptionsMixin, ComponentOptionsMixin, Record, string, VNodeProps & AllowedComponentProps & ComponentCustomProps, Readonly, { +}>, {}, unknown, {}, {}, ComponentOptionsMixin, ComponentOptionsMixin, Record, string, VNodeProps & AllowedComponentProps & ComponentCustomProps, Readonly, { disabled: boolean; type: string; size: string; @@ -1049,12 +1053,12 @@ export const SRadioAtom: FunctionalComponent; // Warning: (ae-forgotten-export) The symbol "Props" needs to be exported by the entry point lib.d.ts // // @public (undocumented) -export const SRadioGroup: DefineComponent<__VLS_WithDefaults_22<__VLS_TypePropsToRuntimeProps_25, { +export const SRadioGroup: DefineComponent<__VLS_WithDefaults_23<__VLS_TypePropsToRuntimeProps_26, { modelValue: null; radioSelector: string; labelledBy: string; describedBy: string; -}>, {}, unknown, {}, {}, ComponentOptionsMixin, ComponentOptionsMixin, "update:modelValue"[], "update:modelValue", VNodeProps & AllowedComponentProps & ComponentCustomProps, Readonly, { +}>, {}, unknown, {}, {}, ComponentOptionsMixin, ComponentOptionsMixin, "update:modelValue"[], "update:modelValue", VNodeProps & AllowedComponentProps & ComponentCustomProps, Readonly, { modelValue: null; radioSelector: string; labelledBy: string; @@ -1081,7 +1085,9 @@ label?: string | undefined; size?: SelectSize | undefined; noAutoClose?: boolean | undefined; loading?: boolean | undefined; +triggerSearch?: boolean | undefined; dropdownSearch?: boolean | undefined; +remoteSearch?: boolean | undefined; }>, {}, unknown, {}, {}, ComponentOptionsMixin, ComponentOptionsMixin, Record, string, VNodeProps & AllowedComponentProps & ComponentCustomProps, Readonly[] | SelectOptionGroup[] | undefined; @@ -1092,7 +1098,9 @@ label?: string | undefined; size?: SelectSize | undefined; noAutoClose?: boolean | undefined; loading?: boolean | undefined; +triggerSearch?: boolean | undefined; dropdownSearch?: boolean | undefined; +remoteSearch?: boolean | undefined; }>>>, {}>; // Warning: (ae-forgotten-export) The symbol "__VLS_WithDefaults" needs to be exported by the entry point lib.d.ts @@ -1111,7 +1119,9 @@ syncMenuAndInputWidths?: boolean | undefined; noAutoClose?: boolean | undefined; loading?: boolean | undefined; sameWidthPopper?: boolean | undefined; -dropdownSearch: boolean; +triggerSearch?: boolean | undefined; +dropdownSearch?: boolean | undefined; +remoteSearch?: boolean | undefined; }>, { size: "md"; options: () => never[]; @@ -1123,7 +1133,9 @@ noAutoClose: boolean; label: null; loading: boolean; sameWidthPopper: boolean; +triggerSearch: boolean; dropdownSearch: boolean; +remoteSearch: boolean; }>, {}, unknown, {}, {}, ComponentOptionsMixin, ComponentOptionsMixin, { "update:modelValue": (value: any) => void; } & { @@ -1140,7 +1152,9 @@ syncMenuAndInputWidths?: boolean | undefined; noAutoClose?: boolean | undefined; loading?: boolean | undefined; sameWidthPopper?: boolean | undefined; -dropdownSearch: boolean; +triggerSearch?: boolean | undefined; +dropdownSearch?: boolean | undefined; +remoteSearch?: boolean | undefined; }>, { size: "md"; options: () => never[]; @@ -1152,7 +1166,9 @@ noAutoClose: boolean; label: null; loading: boolean; sameWidthPopper: boolean; +triggerSearch: boolean; dropdownSearch: boolean; +remoteSearch: boolean; }>>> & { "onUpdate:modelValue"?: ((value: any) => any) | undefined; onSearch?: ((value: string) => any) | undefined; @@ -1166,8 +1182,10 @@ options: SelectOption[] | SelectOptionGroup[]; label: string | null; noAutoClose: boolean; dropdownSearch: boolean; +remoteSearch: boolean; syncMenuAndInputWidths: boolean; sameWidthPopper: boolean; +triggerSearch: boolean; }>; // Warning: (ae-forgotten-export) The symbol "__VLS_WithDefaults" needs to be exported by the entry point lib.d.ts @@ -1191,18 +1209,31 @@ type: SelectButtonType; // @public (undocumented) export const SSelectChevron: FunctionalComponent; +// Warning: (ae-forgotten-export) The symbol "__VLS_WithDefaults" needs to be exported by the entry point lib.d.ts +// Warning: (ae-forgotten-export) The symbol "__VLS_TypePropsToRuntimeProps" needs to be exported by the entry point lib.d.ts +// // @public (undocumented) -export const SSelectInput: DefineComponent< {}, {}, {}, {}, {}, ComponentOptionsMixin, ComponentOptionsMixin, EmitsOptions, string, VNodeProps & AllowedComponentProps & ComponentCustomProps, Readonly>, {}>; +export const SSelectInput: DefineComponent<__VLS_WithDefaults_11<__VLS_TypePropsToRuntimeProps_13<{ +search?: boolean | undefined; +}>, { +search: boolean; +}>, {}, unknown, {}, {}, ComponentOptionsMixin, ComponentOptionsMixin, Record, string, VNodeProps & AllowedComponentProps & ComponentCustomProps, Readonly, { +search: boolean; +}>>>, { +search: boolean; +}>; // Warning: (ae-forgotten-export) The symbol "__VLS_TypePropsToRuntimeProps" needs to be exported by the entry point lib.d.ts // // @public (undocumented) -export const SSelectOption: DefineComponent<__VLS_TypePropsToRuntimeProps_13<{ +export const SSelectOption: DefineComponent<__VLS_TypePropsToRuntimeProps_14<{ type: SelectOptionType; selected?: boolean | undefined; }>, {}, unknown, {}, {}, ComponentOptionsMixin, ComponentOptionsMixin, { toggle: () => void; -}, string, VNodeProps & AllowedComponentProps & ComponentCustomProps, Readonly>> & { @@ -1214,10 +1245,10 @@ onToggle?: (() => any) | undefined; // Warning: (ae-forgotten-export) The symbol "Props" needs to be exported by the entry point lib.d.ts // // @public (undocumented) -export const SSpinner: DefineComponent<__VLS_WithDefaults_11<__VLS_TypePropsToRuntimeProps_14, { +export const SSpinner: DefineComponent<__VLS_WithDefaults_12<__VLS_TypePropsToRuntimeProps_15, { size: string; width: number; -}>, {}, unknown, {}, {}, ComponentOptionsMixin, ComponentOptionsMixin, Record, string, VNodeProps & AllowedComponentProps & ComponentCustomProps, Readonly, { +}>, {}, unknown, {}, {}, ComponentOptionsMixin, ComponentOptionsMixin, Record, string, VNodeProps & AllowedComponentProps & ComponentCustomProps, Readonly, { size: string; width: number; }>>>, { @@ -1230,12 +1261,12 @@ width: string | number; // Warning: (ae-forgotten-export) The symbol "Props" needs to be exported by the entry point lib.d.ts // // @public (undocumented) -export const SSwitch: DefineComponent<__VLS_WithDefaults_12<__VLS_TypePropsToRuntimeProps_15, { +export const SSwitch: DefineComponent<__VLS_WithDefaults_13<__VLS_TypePropsToRuntimeProps_16, { label: string; disabled: boolean; }>, {}, unknown, {}, {}, ComponentOptionsMixin, ComponentOptionsMixin, { "update:modelValue": (value: boolean) => void; -}, string, VNodeProps & AllowedComponentProps & ComponentCustomProps, Readonly, { +}, string, VNodeProps & AllowedComponentProps & ComponentCustomProps, Readonly, { label: string; disabled: boolean; }>>> & { @@ -1249,12 +1280,12 @@ label: string; // Warning: (ae-forgotten-export) The symbol "__VLS_TypePropsToRuntimeProps" needs to be exported by the entry point lib.d.ts // // @public (undocumented) -export const STab: DefineComponent<__VLS_WithDefaults_17<__VLS_TypePropsToRuntimeProps_20<{ +export const STab: DefineComponent<__VLS_WithDefaults_18<__VLS_TypePropsToRuntimeProps_21<{ disabled?: boolean | undefined; name: string; }>, { disabled: boolean; -}>, {}, unknown, {}, {}, ComponentOptionsMixin, ComponentOptionsMixin, Record, string, VNodeProps & AllowedComponentProps & ComponentCustomProps, Readonly, {}, unknown, {}, {}, ComponentOptionsMixin, ComponentOptionsMixin, Record, string, VNodeProps & AllowedComponentProps & ComponentCustomProps, Readonly, { @@ -1267,7 +1298,7 @@ disabled: boolean; // Warning: (ae-forgotten-export) The symbol "__VLS_TypePropsToRuntimeProps" needs to be exported by the entry point lib.d.ts // // @public (undocumented) -export const STable: DefineComponent<__VLS_WithDefaults_25<__VLS_TypePropsToRuntimeProps_28<{ +export const STable: DefineComponent<__VLS_WithDefaults_26<__VLS_TypePropsToRuntimeProps_29<{ data?: TableRow[] | undefined; defaultSort?: { prop: string; @@ -1361,7 +1392,7 @@ select: (payload_0: TableRow[], payload_1: TableRow) => void; "change:current": (payload_0: TableRow | null, payload_1: TableRow | null) => void; } & { "click:row-details": (value: TableRow) => void; -}, string, VNodeProps & AllowedComponentProps & ComponentCustomProps, Readonly; @@ -1624,13 +1655,13 @@ reserveSelection: boolean; // Warning: (ae-forgotten-export) The symbol "__VLS_TypePropsToRuntimeProps" needs to be exported by the entry point lib.d.ts // // @public (undocumented) -export const STabsPanel: DefineComponent<__VLS_WithDefaults_16<__VLS_TypePropsToRuntimeProps_19<{ +export const STabsPanel: DefineComponent<__VLS_WithDefaults_17<__VLS_TypePropsToRuntimeProps_20<{ modelValue: string; background?: "primary" | "secondary" | "none" | undefined; }>, { modelValue: string; background: string; -}>, {}, unknown, {}, {}, ComponentOptionsMixin, ComponentOptionsMixin, "update:modelValue"[], "update:modelValue", VNodeProps & AllowedComponentProps & ComponentCustomProps, Readonly, {}, unknown, {}, {}, ComponentOptionsMixin, ComponentOptionsMixin, "update:modelValue"[], "update:modelValue", VNodeProps & AllowedComponentProps & ComponentCustomProps, Readonly, { @@ -1669,22 +1700,26 @@ export type Status = typeof Status[keyof typeof Status]; // Warning: (ae-forgotten-export) The symbol "Props" needs to be exported by the entry point lib.d.ts // // @public (undocumented) -export const STextField: DefineComponent<__VLS_WithDefaults_13<__VLS_TypePropsToRuntimeProps_16, { +export const STextField: DefineComponent<__VLS_WithDefaults_14<__VLS_TypePropsToRuntimeProps_17, { multiline: boolean; password: boolean; disabled: boolean; counter: boolean; noEye: boolean; noModelValueStrictSync: boolean; +filledState: boolean; +passiveAppend: boolean; }>, {}, unknown, {}, {}, ComponentOptionsMixin, ComponentOptionsMixin, { "update:modelValue": (value: string) => void; -}, string, VNodeProps & AllowedComponentProps & ComponentCustomProps, Readonly, { +}, string, VNodeProps & AllowedComponentProps & ComponentCustomProps, Readonly, { multiline: boolean; password: boolean; disabled: boolean; counter: boolean; noEye: boolean; noModelValueStrictSync: boolean; +filledState: boolean; +passiveAppend: boolean; }>>> & { "onUpdate:modelValue"?: ((value: string) => any) | undefined; }, { @@ -1693,6 +1728,8 @@ noModelValueStrictSync: boolean; password: boolean; noEye: boolean; counter: string | number | boolean; +filledState: boolean; +passiveAppend: boolean; }>; // @public (undocumented) @@ -1765,7 +1802,7 @@ apiKey: ProvideKey | ProvideKey[]; // Warning: (ae-forgotten-export) The symbol "__VLS_TypePropsToRuntimeProps" needs to be exported by the entry point lib.d.ts // // @public (undocumented) -export const STooltip: DefineComponent<__VLS_WithDefaults_27<__VLS_TypePropsToRuntimeProps_30<{ +export const STooltip: DefineComponent<__VLS_WithDefaults_28<__VLS_TypePropsToRuntimeProps_31<{ wrapperTag?: string | object | undefined; content?: string | undefined; header?: string | undefined; @@ -1779,7 +1816,7 @@ header: string; placement: string; primaryButtonText: string; secondaryButtonText: string; -}>, {}, unknown, {}, {}, ComponentOptionsMixin, ComponentOptionsMixin, ("click:primary-button" | "click:secondary-button")[], "click:primary-button" | "click:secondary-button", VNodeProps & AllowedComponentProps & ComponentCustomProps, Readonly, {}, unknown, {}, {}, ComponentOptionsMixin, ComponentOptionsMixin, ("click:primary-button" | "click:secondary-button")[], "click:primary-button" | "click:secondary-button", VNodeProps & AllowedComponentProps & ComponentCustomProps, Readonly(), { @@ -113,6 +122,8 @@ const props = withDefaults(defineProps(), { counter: false, noEye: false, noModelValueStrictSync: false, + filledState: false, + passiveAppend: false, }) const emit = defineEmits<(event: 'update:modelValue', value: string) => void>() @@ -141,7 +152,42 @@ function onInput(e: Event) { const isValueEmpty = computed(() => !model.value) const isFocused = ref(false) -const labelTypographyClass = computed(() => (!isFocused.value && isValueEmpty.value ? 'sora-tpg-p3' : 'sora-tpg-p4')) +const labelTypographyClass = computed(() => + !(props.filledState || isFocused.value) && isValueEmpty.value ? 'sora-tpg-p3' : 'sora-tpg-p4', +) + +const inputRef = ref(null) + +function focusInput() { + if (!(inputRef.value instanceof HTMLInputElement)) { + return + } + + if (isFocused.value) { + return + } + + inputRef.value.focus() + isFocused.value = true +} + +function handleInputLineMouseDown() { + focusInput() +} + +function handleAppendMouseDown() { + if (!props.passiveAppend) { + return + } + + focusInput() +} + +function handleEyeButtonFocus(event: Event) { + if (event.target instanceof HTMLElement) { + event.target.blur() + } +} // MESSAGE @@ -211,7 +257,8 @@ const inputType = computed(() => :class="[ 's-text-field', { - 's-text-field_empty': isValueEmpty, + 's-text-field_empty': isValueEmpty && !filledState, + 's-text-field_filled-state': filledState, 's-text-field_disabled': disabled, }, rootClass(), @@ -227,21 +274,31 @@ const inputType = computed(() => {{ label }} - + + + +
data-testid="eye" type="button" @click="toggleForceReveal()" + @focus="handleEyeButtonFocus" > @@ -309,6 +367,7 @@ $theme-content-tertiary: theme.token-as-var('sys.color.content-tertiary'); } &:not(&_empty), + &_filled-state, &:focus-within { label { transform: translateY(#{$label-top-secondary}); @@ -322,7 +381,7 @@ $theme-content-tertiary: theme.token-as-var('sys.color.content-tertiary'); @apply relative flex; @apply transition-all; - height: $height; + min-height: $height; &:hover:not(:focus-within) { background: $theme-bg-hover; @@ -343,19 +402,23 @@ $theme-content-tertiary: theme.token-as-var('sys.color.content-tertiary'); transform: translateY(#{$label-top-primary}); } - input { - @apply h-full flex-1 w-full min-w-0; + #{$root}__input-line { + @apply flex flex-wrap cursor-text; padding: $input-padding; - background: transparent; - &:focus { - outline: none; + input { + @apply flex-1 w-full min-w-1/4; + + background: transparent; + &:focus { + outline: none; + } } } } &__append { - @apply h-full flex items-center space-x-4 pr-4; + @apply flex items-center space-x-4 pr-4; } &__counter { From d778e53ec353c744db1dd6f130b87bf3d0aaa789 Mon Sep 17 00:00:00 2001 From: Stepan Date: Fri, 3 Feb 2023 13:16:34 +0300 Subject: [PATCH 03/22] WDS-125 add options storing for single select --- .../ui/src/components/Select/use-model.ts | 59 +++++++++++++------ 1 file changed, 41 insertions(+), 18 deletions(-) diff --git a/packages/ui/src/components/Select/use-model.ts b/packages/ui/src/components/Select/use-model.ts index 3fbd002b..37885afe 100644 --- a/packages/ui/src/components/Select/use-model.ts +++ b/packages/ui/src/components/Select/use-model.ts @@ -50,7 +50,7 @@ export function useSelectModel({ options, singleModeAutoClose, onAutoClose, - storeSelectedOptions + storeSelectedOptions, }: UseSelectModelParams): UseSelectModelReturn { const triggerAutoClose = () => singleModeAutoClose.value && onAutoClose() @@ -76,36 +76,40 @@ export function useSelectModel({ const storedOptions = shallowReactive(new Map>()) let modelChangedManually = false - watch(modelAsArray, () => { - if (modelChangedManually) { - modelChangedManually = false + watch( + modelAsArray, + () => { + if (modelChangedManually) { + modelChangedManually = false - return - } - - if (!storeSelectedOptions.value) { - return - } + return + } - storedOptions.forEach((_, value) => { - if (!modelAsArray.value.includes(value)) { - storedOptions.delete(value) + if (!storeSelectedOptions.value) { + return } - }) - modelAsArray.value.forEach(rememberOption) - }, { immediate: true, flush: 'sync' }) + + storedOptions.forEach((_, value) => { + if (!modelAsArray.value.includes(value)) { + storedOptions.delete(value) + } + }) + modelAsArray.value.forEach(rememberOption) + }, + { immediate: true, flush: 'sync' }, + ) function rememberOption(value: T) { if (storedOptions.has(value)) { return } - const option = optionItems.value.find(x => x.value === value) + const option = optionItems.value.find((x) => x.value === value) if (option) { storedOptions.set(option.value, option) - return; + return } storedOptions.set(value, { label: '', value }) @@ -133,7 +137,21 @@ export function useSelectModel({ } } else { modelChangedManually = true + + if (storeSelectedOptions.value) { + let oldValue = Array.isArray(model.value) ? model.value[0] : model.value; + + if (oldValue) { + forgetOption(oldValue) + } + } + model.value = value + + if (storeSelectedOptions.value) { + rememberOption(value) + } + triggerAutoClose() } } @@ -149,6 +167,11 @@ export function useSelectModel({ } else { modelChangedManually = true model.value = null + + if (storeSelectedOptions.value) { + forgetOption(value) + } + triggerAutoClose() } } From 64119ccf3bc7e70d96508cd15ed7d7abd33ab5dd Mon Sep 17 00:00:00 2001 From: Stepan Date: Fri, 3 Feb 2023 13:19:14 +0300 Subject: [PATCH 04/22] WDS-125 refactor: move dropdown search prop to scoped slot from SSelectBase --- packages/ui/src/components/Select/SDropdown.vue | 4 ++-- packages/ui/src/components/Select/SSelect.vue | 4 ++-- packages/ui/src/components/Select/SSelectBase.vue | 5 ++++- 3 files changed, 8 insertions(+), 5 deletions(-) diff --git a/packages/ui/src/components/Select/SDropdown.vue b/packages/ui/src/components/Select/SDropdown.vue index 84eaf269..424e21f3 100644 --- a/packages/ui/src/components/Select/SDropdown.vue +++ b/packages/ui/src/components/Select/SDropdown.vue @@ -46,9 +46,9 @@ function isThereLabelSlot() { - - From b76e303e439a2ae06f07e2a394b91ff795e39369 Mon Sep 17 00:00:00 2001 From: Stepan Date: Fri, 3 Feb 2023 15:32:15 +0300 Subject: [PATCH 05/22] WDS-125 fix text field input font --- .changeset/sixty-pants-dance.md | 5 +++++ packages/ui/src/components/TextField/STextField.vue | 1 + 2 files changed, 6 insertions(+) create mode 100644 .changeset/sixty-pants-dance.md diff --git a/.changeset/sixty-pants-dance.md b/.changeset/sixty-pants-dance.md new file mode 100644 index 00000000..a4c7c141 --- /dev/null +++ b/.changeset/sixty-pants-dance.md @@ -0,0 +1,5 @@ +--- +'@soramitsu-ui/ui': patch +--- + +fix (`STextField`): fix input font diff --git a/packages/ui/src/components/TextField/STextField.vue b/packages/ui/src/components/TextField/STextField.vue index 91564852..7c0799c5 100644 --- a/packages/ui/src/components/TextField/STextField.vue +++ b/packages/ui/src/components/TextField/STextField.vue @@ -283,6 +283,7 @@ const inputType = computed(() => Date: Fri, 3 Feb 2023 15:57:25 +0300 Subject: [PATCH 06/22] WDS-125 fix prefix overflow in text field --- .../ui/src/components/TextField/STextField.vue | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/packages/ui/src/components/TextField/STextField.vue b/packages/ui/src/components/TextField/STextField.vue index 7c0799c5..4b795ebb 100644 --- a/packages/ui/src/components/TextField/STextField.vue +++ b/packages/ui/src/components/TextField/STextField.vue @@ -402,18 +402,18 @@ $theme-content-tertiary: theme.token-as-var('sys.color.content-tertiary'); // primary state by default transform: translateY(#{$label-top-primary}); } + } - #{$root}__input-line { - @apply flex flex-wrap cursor-text; - padding: $input-padding; + &__input-line { + @apply flex flex-wrap cursor-text min-w-0; + padding: $input-padding; - input { - @apply flex-1 w-full min-w-1/4; + input { + @apply flex-1 w-full min-w-1/4; - background: transparent; - &:focus { - outline: none; - } + background: transparent; + &:focus { + outline: none; } } } From 5474938e5f341c8837a0e3f994d3808151e66609 Mon Sep 17 00:00:00 2001 From: Stepan Date: Fri, 3 Feb 2023 15:58:06 +0300 Subject: [PATCH 07/22] WDS-125 fix text overflow and icon shrink in select option --- packages/ui/src/components/Select/SSelectOption.vue | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/packages/ui/src/components/Select/SSelectOption.vue b/packages/ui/src/components/Select/SSelectOption.vue index 33bcde80..c256570c 100644 --- a/packages/ui/src/components/Select/SSelectOption.vue +++ b/packages/ui/src/components/Select/SSelectOption.vue @@ -42,24 +42,27 @@ const CHECK_ICON_SIZE = { > -
+
diff --git a/packages/ui/src/components/Select/SSelect.vue b/packages/ui/src/components/Select/SSelect.vue index 3d064690..0e0a466d 100644 --- a/packages/ui/src/components/Select/SSelect.vue +++ b/packages/ui/src/components/Select/SSelect.vue @@ -43,7 +43,11 @@ const defaultOptionType = computed(() => (props.multiple ? SelectOptionType.Chec + > + + diff --git a/packages/ui/src/components/Select/SSelectDropdown.vue b/packages/ui/src/components/Select/SSelectDropdown.vue index 732d5031..54271fd9 100644 --- a/packages/ui/src/components/Select/SSelectDropdown.vue +++ b/packages/ui/src/components/Select/SSelectDropdown.vue @@ -37,6 +37,7 @@ const shownOptionGroups: ComputedRef = computed(() => { return optionGroups.value.map((x) => ({ ...x, items: x.items.filter((x) => escapedQuery.value.test(x.label)) })) }) +const isNothingToShow = eagerComputed(() => shownOptionGroups.value.every((x) => !x.items.length)) function isActionButtonShown(selectAllBtn: boolean) { return api.multiple && selectAllBtn @@ -105,6 +106,16 @@ const SEARCH_ICON_SIZE = {
+
+ + No data + +
+