From b3e107e61ec8b6f241821e555b347d65efe9da59 Mon Sep 17 00:00:00 2001 From: Anton Malofeev Date: Fri, 19 Feb 2021 15:40:55 +0300 Subject: [PATCH] build v0.7.0 --- CHANGELOG.md | 43 +++++++++++ README.md | 52 ++++++++++++-- package.json | 2 +- src/abstract/DropdownFieldController.ts | 94 +++++++++++++++---------- src/components/DropdownMenuItem.tsx | 2 +- src/components/MutliDropdownButton.tsx | 83 +++++++++++++++++----- src/example/App.tsx | 5 +- 7 files changed, 217 insertions(+), 64 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5980fc5..2142996 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,46 @@ +## 0.7.0 + +### feat: MutliDropdownButton + +Usage: + +Create controller in setup or anywehere and +give generic type to use + +```typescript +const IndexedText { + id: string + text: string +} + + +const multiDropdownController = new MutliDropdownFieldController( + { keyofValue: 'id' } +) +``` + +Then use MultiDropdownButton with DropdownMenuItem in items +to make it work + +```typescript +MultiDropdownButton({ + controller: multiDropdownController, + items: dropdownItems.map((el) => + DropdownMenuItem({ + child: Text({ + text: ref(el.text), + }), + value: el, + key: el.id, + title: el.text, + }) + ), +}), +``` + +To get or change selected values use: +`controller.value` + ## 0.6.0 ### feat: Provider diff --git a/README.md b/README.md index ee28e45..7c27a54 100644 --- a/README.md +++ b/README.md @@ -200,9 +200,51 @@ ElevatedButton({ To close, just use `navigationController.pop()` - [x] Navigation & NavigationController + - [x] Popup (with background) functionality - [x] Fullscreen functionality +- [x] MultiDropdown Button + +Usage: + +Create controller in setup or anywehere and +give generic type to use + +```typescript +const IndexedText { + id: string + text: string +} + + +const multiDropdownController = new MutliDropdownFieldController( + { keyofValue: 'id' } +) +``` + +Then use MultiDropdownButton with DropdownMenuItem in items +to make it work + +```typescript +MultiDropdownButton({ + controller: multiDropdownController, + items: dropdownItems.map((el) => + DropdownMenuItem({ + child: Text({ + text: ref(el.text), + }), + value: el, + key: el.id, + title: el.text, + }) + ), +}), +``` + +To get or change selected values use: +`controller.value` + ### Usage: Add controller into MultiPorvider and Navigation widget below: @@ -216,11 +258,9 @@ MultiProvider.create({ }) ``` -## WIP - -- [] DropdownButton, DropdownButtonItem - [x] functionality - [] decoration +- [x] DropdownButton, DropdownButtonItem + [x] functionality + [] decoration - [] Visibility [x] functionality @@ -236,6 +276,8 @@ MultiProvider.create({ [x] Basic [] Style +## WIP + - [] GestureDetecture [x] click [] tap diff --git a/package.json b/package.json index 2640aed..09176de 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "@xsoulspace/vue_flutter_tailwind", "description": "Vue3 styled like Flutter with Tailwind CSS", - "version": "0.6.16", + "version": "0.7.0", "private": false, "author": { "name": "Anton Malofeev", diff --git a/src/abstract/DropdownFieldController.ts b/src/abstract/DropdownFieldController.ts index 7ad4476..d8b6029 100644 --- a/src/abstract/DropdownFieldController.ts +++ b/src/abstract/DropdownFieldController.ts @@ -16,7 +16,7 @@ interface DropdownButtonAbstractI { export interface MultiDropdownButtonI< TValue, - TKeyValue extends MutliDropdownSelectedItem + TKeyValue extends MutliDropdownSelectedItemI > extends DropdownButtonAbstractI { controller: MutliDropdownFieldController onCreateNew?: Maybe< @@ -44,40 +44,29 @@ export interface DropdownFieldControllerI value?: Maybe } -export interface MutliDropdownFieldControllerI< - TValue, - TKeyValue extends MutliDropdownSelectedItem -> extends DropdownFieldControllerAbstractI { - value?: Maybe[] -} - -interface MutliDropdownSelectedItemI { - key: string - value: TValue +export interface MutliDropdownFieldControllerI + extends DropdownFieldControllerAbstractI { + value?: Maybe[] + keyofValue: keyof TValue } /** * This class provides a way to create selected items from * outside for`{MutliDropdownFieldController.value}` */ -export class MutliDropdownSelectedItem< - TValue extends - | string - | number - | boolean - | { [prop: string]: any } - | { [prop: number]: any } -> { - key: string +export type MutliDropdownSelectedItemI = { + key: TValue[keyof TValue] | string value: TValue - - constructor({ key, value }: MutliDropdownSelectedItemI) { - this.key = key - this.value = value - } - - static use(arg: MutliDropdownSelectedItemI) { - return new MutliDropdownSelectedItem(arg) - } +} +/** + * This is typeguard for MutliDropdownSelectedItemI interface + */ +export const isMutliDropdownSelectedItem = < + TValue, + TKeyValue extends MutliDropdownSelectedItemI +>( + arg: Record +): arg is TKeyValue => { + return 'key' in arg && 'value' in arg } /** * This class provides a way to create selected items from @@ -148,42 +137,73 @@ type TKeyValueIndex = number // TODO: add properties export class MutliDropdownFieldController< TValue, - TKeyValue extends MutliDropdownSelectedItem = MutliDropdownSelectedItem + TKeyValue extends MutliDropdownSelectedItemI = MutliDropdownSelectedItemI > extends DropdownFieldControllerAbstract { private _reactVal: { - val: Maybe>[] + val: Maybe[] } = reactive({ val: [], }) + keyofValue: keyof TValue constructor({ value, readOnly, maxLines, maxLength, - }: MutliDropdownFieldControllerI) { + keyofValue, + }: MutliDropdownFieldControllerI) { super({ maxLength, maxLines, readOnly, }) - this.value = value ?? [] + this.keyofValue = keyofValue + const keyValues = this.toKeyValues({ values: value ?? [] }) + this.keyValue = keyValues + } + toRawValues({ values }: { values: Maybe[] }): Maybe[] { + return values.map((el) => el?.value) + } + toKeyValues({ values }: { values: Maybe[] }): Maybe[] { + return values.filter(isNotNull).map((value) => { + const key = value[this.keyofValue] + const keyValue: MutliDropdownSelectedItemI = { + key, + value, + } + if (isMutliDropdownSelectedItem(keyValue)) { + return keyValue + } + return + }) } + get valueIndexesByKeyMap() { - const map = new Map() + const map = new Map() - const filteredValues = this.value.filter(isNotNull) + const filteredValues = this.keyValue.filter(isNotNull) for (let i = 0; i < filteredValues.length; i++) { const val = filteredValues[i] map.set(val.key, i) } return map } - set value(val: Maybe>[]) { + set keyValue(val: Maybe[]) { this._reactVal.val = val } - get value() { + get keyValue() { return this._reactVal.val } + set value(val: Maybe[]) { + this.keyValue = this.toKeyValues({ + values: val, + }) + } + get value() { + return this.toRawValues({ + values: this.keyValue, + }) + } get reactive() { return this._reactVal } diff --git a/src/components/DropdownMenuItem.tsx b/src/components/DropdownMenuItem.tsx index 22811c7..97ef2f8 100644 --- a/src/components/DropdownMenuItem.tsx +++ b/src/components/DropdownMenuItem.tsx @@ -10,7 +10,7 @@ interface DropdownMenuItemI { child: Component key: string onTap?: Maybe - value?: Maybe + value: Maybe title: string } diff --git a/src/components/MutliDropdownButton.tsx b/src/components/MutliDropdownButton.tsx index dc09949..30291cc 100644 --- a/src/components/MutliDropdownButton.tsx +++ b/src/components/MutliDropdownButton.tsx @@ -4,8 +4,9 @@ import { BoxDecoration } from '../abstract/BoxDecoration' import { BoxShadow } from '../abstract/BoxShadow' import { Colors } from '../abstract/Colors' import { + isMutliDropdownSelectedItem, MultiDropdownButtonI, - MutliDropdownSelectedItem, + MutliDropdownSelectedItemI, MutliDropdownSelectedValueI, } from '../abstract/DropdownFieldController' import { DropdownMenuItemConstructor } from '../abstract/DropdownMenuItem' @@ -32,6 +33,47 @@ import { TextField } from './TextField' import { Visibility } from './Visibility' import { Wrap } from './Wrap' +/** + * Usage: + * + * Create controller in setup or anywehere and + * give generic type to use + * + * ```typescript + * const IndexedText { + * id: string + * text: string + * } + * + * + * const multiDropdownController = new MutliDropdownFieldController( + * { keyofValue: 'id' } + * ) + * ``` + * + * Then use MultiDropdownButton with DropdownMenuItem in items + * to make it work + * + * ```typescript + * MultiDropdownButton({ + * controller: multiDropdownController, + * items: dropdownItems.map((el) => + * DropdownMenuItem({ + * child: Text({ + * text: ref(el.text), + * }), + * value: el, + * key: el.id, + * title: el.text, + * }) + * ), + * }), + * ``` + * + * To get or change selected values use: + * `controller.value` + * + */ export const MultiDropdownButton = < TValue extends | string @@ -39,7 +81,7 @@ export const MultiDropdownButton = < | boolean | { [prop: string]: any } | { [prop: number]: any }, - TKeyValue extends MutliDropdownSelectedItem + TKeyValue extends MutliDropdownSelectedItemI >({ items, elevation, @@ -59,18 +101,25 @@ export const MultiDropdownButton = < items.filter((el) => controller.valueIndexesByKeyMap.has(el.key)) ) const selectedItemsMap = computed(() => - createKeyedMap>({ + createKeyedMap< + TKeyValue['key'] | string, + DropdownMenuItemConstructor + >({ arr: selectedItems.value, key: 'key', unifyValues: false, }) ) const isMenuOpened = ref(false) - const deleteSeletedItem = async ({ key }: { key: TKeyValue['key'] }) => { + const deleteSeletedItem = async ({ + key, + }: { + key: TKeyValue['key'] | string + }) => { const index = controller.valueIndexesByKeyMap.get(key) if (index != null) { - const val = controller.value[index] - controller.value.splice(index, 1) + const val = controller.keyValue[index] + controller.keyValue.splice(index, 1) if (onChanged) await onChanged(null, val?.value) return } @@ -81,24 +130,26 @@ export const MultiDropdownButton = < key, }: { item: MutliDropdownSelectedValueI> - key: TKeyValue['key'] + key: TKeyValue['key'] | string }) => { const val = item.value.value if (val != null) { - const selectedItem = MutliDropdownSelectedItem.use({ + const selectedItem: MutliDropdownSelectedItemI = { key, value: val, - }) + } + const selectedItemIndex = controller.valueIndexesByKeyMap.get(key) - if (selectedItemIndex != null && selectedItemIndex >= 0) { - controller.value.splice(selectedItemIndex, 1, selectedItem) - } else { - controller.value.unshift(selectedItem) + if (isMutliDropdownSelectedItem(selectedItem)) { + if (selectedItemIndex != null && selectedItemIndex >= 0) { + controller.keyValue.splice(selectedItemIndex, 1, selectedItem) + } else { + controller.keyValue.unshift(selectedItem) + } + if (onChanged) await onChanged(val, null) + return } - if (onChanged) await onChanged(val, null) - return } - if (onChanged) await onChanged(null, null) } /** * `1. User click on textField -> open dropdown diff --git a/src/example/App.tsx b/src/example/App.tsx index c213fd9..4e31457 100644 --- a/src/example/App.tsx +++ b/src/example/App.tsx @@ -182,7 +182,7 @@ export const WrapperApp = () => { ), }) const multiDropdownController = new MutliDropdownFieldController( - {} + { keyofValue: 'id' } ) watch( multiDropdownController.reactive, @@ -226,9 +226,6 @@ export const WrapperApp = () => { title: el.text, }) ), - onChanged: (newValue, oldValue) => { - // console.log({ newValue, oldValue }) - }, }), ElevatedButton({