From abd15239522c1d9d6f9284ba9abe862c5ea2b7f0 Mon Sep 17 00:00:00 2001 From: John Campion Jr Date: Tue, 3 Oct 2023 13:21:55 -0400 Subject: [PATCH] feat: use Floating UI for dropdown (#94) * feat: add floating UI to dropdowns Allows for positioning elements wherever you want * feat: added floating-ui to buttondropdown * feat(BaseDropdown): add floating-ui placement property, deprecate orientation --------- Co-authored-by: Sacha STAFYNIAK --- components/base/BaseDropdown.vue | 150 ++++++---- components/form/BaseAutocomplete.vue | 361 +++++++++++++----------- components/form/BaseListbox.vue | 399 ++++++++++++++------------- package.json | 1 + pnpm-lock.yaml | 101 ++++--- 5 files changed, 568 insertions(+), 444 deletions(-) diff --git a/components/base/BaseDropdown.vue b/components/base/BaseDropdown.vue index 92a70eaa..132ce056 100644 --- a/components/base/BaseDropdown.vue +++ b/components/base/BaseDropdown.vue @@ -1,5 +1,6 @@ + + diff --git a/components/form/BaseAutocomplete.vue b/components/form/BaseAutocomplete.vue index 8bb605b5..16c800bc 100644 --- a/components/form/BaseAutocomplete.vue +++ b/components/form/BaseAutocomplete.vue @@ -6,8 +6,8 @@ import { ComboboxLabel, ComboboxOption, ComboboxOptions, - TransitionRoot, } from '@headlessui/vue' +import { Float, FloatReference, FloatContent } from '@headlessui-float/vue' const props = withDefaults( defineProps<{ @@ -295,7 +295,7 @@ watch(debounced, async (value) => { }) function clear() { - value.value = props.clearValue + value.value = props.clearValue ?? [] } const iconResolved = computed(() => { @@ -359,192 +359,217 @@ function removeItem(item: any) { ]" as="div" > - - - {{ label }} - - - -
-
    -
  • -
    - {{ props.displayValue(item) }} - -
    -
  • -
-
- -
- - {{ props.label }} + {{ label }} -
- -
- - - - - -
- -
-
- - {{ props.error }} - - - - - -
+
    - - - {{ props.i18n.pending }} - - -
-
- - - {{ props.i18n.empty }} - - -
- -
-
+ + + + + + diff --git a/components/form/BaseListbox.vue b/components/form/BaseListbox.vue index 67b9ce01..72ea7a3a 100644 --- a/components/form/BaseListbox.vue +++ b/components/form/BaseListbox.vue @@ -7,6 +7,8 @@ import { ListboxOptions, } from '@headlessui/vue' +import { Float, FloatReference, FloatContent } from '@headlessui-float/vue' + const props = withDefaults( defineProps<{ /** @@ -224,212 +226,235 @@ const value = computed(() => { :multiple="props.multiple" :disabled="props.disabled" > - - {{ props.label }} - - -
- - -
- - - - - - - - - - - - - - -
- -
-
-
-
- - - - {{ props.label }} + + +
+ + -
  • - - + +
    - + -
    - +
    + {{ placeholder }} +
    +
    {{ - props.properties.label - ? item[props.properties.label] - : props.properties.value - ? item[props.properties.value] - : item + typeof props.multipleLabel === 'function' + ? props.multipleLabel(value, props.properties.label) + : props.multipleLabel }} - - + + + + + + + - -
  • - - - - - - {{ props.label }} - - - {{ props.error }} - -
    +
    + +
    +
    + + + + + + + +
  • + + + + + + +
    + + {{ + props.properties.label + ? item[props.properties.label] + : props.properties.value + ? item[props.properties.value] + : item + }} + + + {{ item[props.properties.sublabel] }} + +
    + + + +
    +
  • +
    +
    +
    + + + {{ props.label }} + + + + {{ props.error }} + + + + + diff --git a/package.json b/package.json index a78f8199..dc7abf9e 100644 --- a/package.json +++ b/package.json @@ -47,6 +47,7 @@ }, "dependencies": { "@headlessui/vue": "^1.7.16", + "@headlessui-float/vue": "^0.11.4", "@iconify/vue": "^4.1.1", "@nuxtjs/color-mode": "^3.3.0", "@nuxtjs/tailwindcss": "^6.8.0", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 1a109bce..f512418a 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -5,6 +5,9 @@ settings: excludeLinksFromLockfile: false dependencies: + '@headlessui-float/vue': + specifier: ^0.11.4 + version: 0.11.4 '@headlessui/vue': specifier: ^1.7.16 version: 1.7.16 @@ -466,7 +469,7 @@ packages: lodash.merge: 4.6.2 lodash.uniq: 4.5.0 resolve-from: 5.0.0 - ts-node: 10.9.1(@types/node@20.4.7)(typescript@5.2.2) + ts-node: 10.9.1(@types/node@18.15.11)(typescript@5.2.2) typescript: 5.2.2 transitivePeerDependencies: - '@swc/core' @@ -965,6 +968,53 @@ packages: resolution: {integrity: sha512-1S8uAY/MTJqVx0SC4epBq+N2yhuwtNwLbJYNZyhL2pO1ZVKn5HFXav5T41Ryzy9K9V7ZId2JB2oy/W4aCd9/2w==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + /@floating-ui/core@1.5.0: + resolution: {integrity: sha512-kK1h4m36DQ0UHGj5Ah4db7R0rHemTqqO0QLvUqi1/mUUp3LuAWbWxdxSIf/XsnH9VS6rRVPLJCncjRzUvyCLXg==} + dependencies: + '@floating-ui/utils': 0.1.4 + dev: false + + /@floating-ui/dom@1.5.3: + resolution: {integrity: sha512-ClAbQnEqJAKCJOEbbLo5IUlZHkNszqhuxS4fHAVxRPXPya6Ysf2G8KypnYcOTpx6I8xcgF9bbHb6g/2KpbV8qA==} + dependencies: + '@floating-ui/core': 1.5.0 + '@floating-ui/utils': 0.1.4 + dev: false + + /@floating-ui/utils@0.1.4: + resolution: {integrity: sha512-qprfWkn82Iw821mcKofJ5Pk9wgioHicxcQMxx+5zt5GSKoqdWvgG5AxVmpmUUjzTLPVSH5auBrhI93Deayn/DA==} + dev: false + + /@floating-ui/vue@0.2.1: + resolution: {integrity: sha512-HE+EIeakID7wI6vUwF0yMpaW48bNaPj8QtnQaRMkaQFhQReVBA4bY6fmJ3J7X+dqVgDbMhyfCG0fBJfdQMdWxQ==} + peerDependencies: + '@vue/composition-api': ^1.0.0-rc.1 + vue: ^2.0.0 || >=3.0.0 + peerDependenciesMeta: + '@vue/composition-api': + optional: true + vue: + optional: true + dependencies: + '@floating-ui/dom': 1.5.3 + vue-demi: 0.13.11 + dev: false + + /@headlessui-float/vue@0.11.4: + resolution: {integrity: sha512-hNGQTT3trknSB78ZI3usvnJACLyEUmacvk5Q8JQizJ8k+8GYLvhKklGIhJVO1E3litEzW6yyjPgfg6aEJ+1p6g==} + peerDependencies: + vue: ^3.0.0 + peerDependenciesMeta: + vue: + optional: true + dependencies: + '@floating-ui/core': 1.5.0 + '@floating-ui/dom': 1.5.3 + '@floating-ui/vue': 0.2.1 + transitivePeerDependencies: + - '@vue/composition-api' + dev: false + /@headlessui/vue@1.7.16: resolution: {integrity: sha512-nKT+nf/q6x198SsyK54mSszaQl/z+QxtASmgMEJtpxSX2Q0OPJX0upS/9daDyiECpeAsvjkoOrm2O/6PyBQ+Qg==} engines: {node: '>=10'} @@ -3085,7 +3135,7 @@ packages: dependencies: '@types/node': 20.4.7 cosmiconfig: 8.3.5(typescript@5.2.2) - ts-node: 10.9.1(@types/node@20.4.7)(typescript@5.2.2) + ts-node: 10.9.1(@types/node@18.15.11)(typescript@5.2.2) typescript: 5.2.2 dev: true @@ -7958,38 +8008,6 @@ packages: typescript: 5.2.2 v8-compile-cache-lib: 3.0.1 yn: 3.1.1 - dev: false - - /ts-node@10.9.1(@types/node@20.4.7)(typescript@5.2.2): - resolution: {integrity: sha512-NtVysVPkxxrwFGUUxGYhfux8k78pQB3JqYBXlLRZgdGUqTO5wU/UyHop5p70iEbGhB7q5KmiZiU0Y3KlJrScEw==} - hasBin: true - peerDependencies: - '@swc/core': '>=1.2.50' - '@swc/wasm': '>=1.2.50' - '@types/node': '*' - typescript: '>=2.7' - peerDependenciesMeta: - '@swc/core': - optional: true - '@swc/wasm': - optional: true - dependencies: - '@cspotcode/source-map-support': 0.8.1 - '@tsconfig/node10': 1.0.9 - '@tsconfig/node12': 1.0.11 - '@tsconfig/node14': 1.0.3 - '@tsconfig/node16': 1.0.4 - '@types/node': 20.4.7 - acorn: 8.10.0 - acorn-walk: 8.2.0 - arg: 4.1.3 - create-require: 1.1.1 - diff: 4.0.2 - make-error: 1.3.6 - typescript: 5.2.2 - v8-compile-cache-lib: 3.0.1 - yn: 3.1.1 - dev: true /tsconfig-paths@3.14.2: resolution: {integrity: sha512-o/9iXgCYc5L/JxCHPe3Hvh8Q/2xm5Z+p18PESBU6Ff33695QnCHBEjcytY2q19ua7Mbl/DavtBOLq+oG0RCL+g==} @@ -8496,6 +8514,21 @@ packages: dependencies: ufo: 1.3.0 + /vue-demi@0.13.11: + resolution: {integrity: sha512-IR8HoEEGM65YY3ZJYAjMlKygDQn25D5ajNFNoKh9RSDMQtlzCxtfQjdQgv9jjK+m3377SsJXY8ysq8kLCZL25A==} + engines: {node: '>=12'} + hasBin: true + requiresBuild: true + peerDependencies: + '@vue/composition-api': ^1.0.0-rc.1 + vue: ^3.0.0-0 || ^2.6.0 + peerDependenciesMeta: + '@vue/composition-api': + optional: true + vue: + optional: true + dev: false + /vue-demi@0.14.6: resolution: {integrity: sha512-8QA7wrYSHKaYgUxDA5ZC24w+eHm3sYCbp0EzcDwKqN3p6HqtTCGR/GVsPyZW92unff4UlcSh++lmqDWN3ZIq4w==} engines: {node: '>=12'}