Compare commits

..

59 Commits

Author SHA1 Message Date
Benjamin Canac
c5e9a1ef46 chore(release): 0.1.31 2023-01-17 15:35:38 +01:00
Benjamin Canac
afe69a570d chore(deps): bump 2023-01-17 15:35:16 +01:00
Benjamin Canac
e6ed834cea chore(release): 0.1.30 2023-01-17 15:00:54 +01:00
Benjamin Canac
30c5412a6b chore(CommandPalette): input close icon position 2023-01-17 15:00:21 +01:00
Benjamin Canac
01f56d9553 chore(Button): handle compact 2023-01-17 15:00:21 +01:00
Benjamin Canac
91f273c117 chore(release): 0.1.29 2023-01-17 15:00:19 +01:00
Benjamin Canac
cda8ce32a3 chore(release): 0.1.28 2023-01-13 18:33:18 +01:00
Benjamin Canac
2bc0eb05d1 chore(Slideover): emit event 2023-01-13 18:32:45 +01:00
Benjamin Canac
cfc4bdfbfe chore(release): 0.1.27 2023-01-12 15:33:35 +01:00
Sylvain Marroufin
370d05921d chore(lighthouse): improve CommandPalette 2023-01-12 14:28:29 +01:00
Sylvain Marroufin
b6455a151d chore(lighthouse): improve components accessibility (#127) 2023-01-12 12:32:42 +01:00
Benjamin Canac
8c0e0ec823 chore(release): 0.1.26 2023-01-09 18:43:45 +01:00
Sylvain Marroufin
4f56921096 fix(CommandPalette): select first item on search changes (#126) 2023-01-09 18:43:12 +01:00
Benjamin Canac
6a5ee32e05 chore(release): 0.1.25 2023-01-09 18:42:53 +01:00
Benjamin Canac
4ea07e1077 chore(release): 0.1.24 2023-01-04 12:17:13 +01:00
Benjamin Canac
5fd65d0917 chore(deps): bump 2023-01-04 11:39:24 +01:00
Benjamin Canac
2ec0cee1d9 chore(release): 0.1.23 2022-12-20 16:27:30 +01:00
Benjamin Canac
758e6f1400 Revert "chore: put back stop propagation on mode hover (#121)"
This reverts commit c015148f29.
2022-12-20 16:26:55 +01:00
Benjamin Canac
275fa1831d chore(release): 0.1.22 2022-12-19 18:27:36 +01:00
Benjamin Canac
8b5e08f6f2 chore(Tooltip): add prevent prop 2022-12-19 18:27:19 +01:00
Benjamin Canac
1635f57de6 chore(release): 0.1.21 2022-12-19 16:22:41 +01:00
Sylvain Marroufin
b3e0122001 chore(SelectCustom): add emit on open/close (#125) 2022-12-19 16:22:15 +01:00
Benjamin Canac
4f9d20e603 chore(release): 0.1.20 2022-12-19 11:37:46 +01:00
Benjamin Canac
f7add47cf2 chore(deps): pin @headlessui/vue version to 1.7.4 2022-12-19 11:37:28 +01:00
renovate[bot]
99c1c683eb Update all non-major dependencies (#122)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2022-12-17 16:57:12 +01:00
Anthony Fu
ff9f6c251d fix: avoid referring to complex types in props (#123) 2022-12-17 16:56:49 +01:00
Benjamin Canac
e0c703ca6c chore(release): 0.1.19 2022-12-16 10:47:39 +01:00
Benjamin Canac
2210faa160 chore(deps): bump @headlessui/vue 2022-12-16 10:47:20 +01:00
Benjamin Canac
19589b5e05 chore(release): 0.1.18 2022-12-15 14:45:48 +01:00
Benjamin Canac
7051fa39a7 chore: add missing w-full on headlessui buttons 2022-12-15 14:45:14 +01:00
renovate[bot]
911278e95e chore(deps): update all non-major dependencies (#120)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2022-12-13 13:55:28 +01:00
Sylvain Marroufin
c015148f29 chore: put back stop propagation on mode hover (#121) 2022-12-13 13:54:23 +01:00
Benjamin Canac
cb1fd55801 chore(release): 0.1.17 2022-12-06 16:56:31 +01:00
Benjamin Canac
16fd1c0ca3 fix: remove stop propagation on mode hover 2022-12-06 16:56:16 +01:00
Benjamin Canac
84ac92ed7a chore(release): 0.1.16 2022-12-06 16:25:01 +01:00
Benjamin Canac
0ade69de26 fix(Popover): preset from tooltip 2022-12-06 16:24:33 +01:00
Benjamin Canac
d9193abf55 chore(release): 0.1.15 2022-12-02 17:16:52 +01:00
Sylvain Marroufin
44a78d7f67 fix(Dropdown): better handle item click to preventDefault (#119) 2022-12-02 17:16:32 +01:00
Benjamin Canac
3ce600f89a chore(release): 0.1.14 2022-12-02 17:16:00 +01:00
Benjamin Canac
662dd9ee65 chore(release): 0.1.13 2022-12-01 14:08:26 +01:00
renovate[bot]
3be3751bfc chore(deps): update all non-major dependencies (#117)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2022-12-01 13:02:31 +01:00
Sylvain Marroufin
5042b5806b chore(components): update props with optional fields (#118) 2022-12-01 13:01:45 +01:00
Benjamin Canac
2535301bcd chore(release): 0.1.12 2022-11-29 13:56:30 +01:00
Benjamin Canac
629bb72424 fix(Checkbox): types 2022-11-29 13:54:35 +01:00
Benjamin Canac
543b4e025f chore(release): 0.1.11 2022-11-29 12:56:04 +01:00
Benjamin Canac
7f18c6bdc7 fix(Checkbox): revert type fix as it breaks checkboxes 2022-11-29 12:55:44 +01:00
Benjamin Canac
0ea924ca0d chore(release): 0.1.10 2022-11-26 23:44:03 +01:00
renovate[bot]
22f3d42470 chore(deps): update dependency @nuxtjs/color-mode to ^3.2.0 (#63)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2022-11-26 23:43:10 +01:00
Benjamin Canac
1ad96065fd fix: default popper options 2022-11-26 23:42:36 +01:00
Anthony Fu
952786ed79 fix: default props for command palette (#116) 2022-11-26 23:41:25 +01:00
Benjamin Canac
9cf92e34ab chore(release): 0.1.9 2022-11-25 10:00:40 +01:00
Benjamin Canac
6321d3ed8d fix(Icon): couldn't load anymore 2022-11-25 10:00:30 +01:00
Benjamin Canac
bc0c168c0b fix(Icon): eslint ignore 2022-11-23 17:00:08 +01:00
Benjamin Canac
9975305f2a chore(deps): bump 2022-11-23 11:32:08 +01:00
Anthony Fu
edc1bd677b chore: improve types (#115)
* wip: improve types

* feat: improve types

* Apply suggestions from code review

Co-authored-by: Sylvain Marroufin <marroufin.sylvain@gmail.com>

* chore: update

* chore: enable ci typecheck

* chore: fix

Co-authored-by: Sylvain Marroufin <marroufin.sylvain@gmail.com>
2022-11-23 11:30:18 +01:00
Benjamin Canac
32d1f21299 chore(release): 0.1.8 2022-11-16 18:01:05 +01:00
Benjamin Canac
f901e417e1 chore(deps): bump nuxt to 3.0.0 2022-11-16 16:09:50 +01:00
Benjamin Canac
23921e1ae2 chore(release): 0.1.7 2022-11-16 10:57:06 +01:00
Benjamin Canac
8a5f52dbc8 chore(deps): bump 2022-11-16 10:56:50 +01:00
34 changed files with 1681 additions and 1049 deletions

View File

@@ -41,6 +41,9 @@ jobs:
- name: Lint
run: yarn lint
- name: Typecheck
run: yarn typecheck
- name: Build
run: yarn build

View File

@@ -41,6 +41,9 @@ jobs:
- name: Lint
run: yarn lint
- name: Typecheck
run: yarn typecheck
- name: Build
run: yarn build

View File

@@ -2,6 +2,103 @@
All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines.
### [0.1.31](https://github.com/nuxtlabs/ui/compare/v0.1.30...v0.1.31) (2023-01-17)
### [0.1.30](https://github.com/nuxtlabs/ui/compare/v0.1.29...v0.1.30) (2023-01-17)
### [0.1.29](https://github.com/nuxtlabs/ui/compare/v0.1.28...v0.1.29) (2023-01-17)
### [0.1.28](https://github.com/nuxtlabs/ui/compare/v0.1.27...v0.1.28) (2023-01-13)
### [0.1.27](https://github.com/nuxtlabs/ui/compare/v0.1.26...v0.1.27) (2023-01-12)
### [0.1.26](https://github.com/nuxtlabs/ui/compare/v0.1.25...v0.1.26) (2023-01-09)
### Bug Fixes
* **CommandPalette:** select first item on search changes ([#126](https://github.com/nuxtlabs/ui/issues/126)) ([4f56921](https://github.com/nuxtlabs/ui/commit/4f56921096f5885cdab8b7cb5c5aa01304188e11))
### [0.1.25](https://github.com/nuxtlabs/ui/compare/v0.1.24...v0.1.25) (2023-01-09)
### [0.1.24](https://github.com/nuxtlabs/ui/compare/v0.1.23...v0.1.24) (2023-01-04)
### [0.1.23](https://github.com/nuxtlabs/ui/compare/v0.1.22...v0.1.23) (2022-12-20)
### [0.1.22](https://github.com/nuxtlabs/ui/compare/v0.1.21...v0.1.22) (2022-12-19)
### [0.1.21](https://github.com/nuxtlabs/ui/compare/v0.1.20...v0.1.21) (2022-12-19)
### [0.1.20](https://github.com/nuxtlabs/ui/compare/v0.1.19...v0.1.20) (2022-12-19)
### Bug Fixes
* avoid referring to complex types in props ([#123](https://github.com/nuxtlabs/ui/issues/123)) ([ff9f6c2](https://github.com/nuxtlabs/ui/commit/ff9f6c251df59641862d82587e5d963c8e6ea298))
### [0.1.19](https://github.com/nuxtlabs/ui/compare/v0.1.18...v0.1.19) (2022-12-16)
### [0.1.18](https://github.com/nuxtlabs/ui/compare/v0.1.17...v0.1.18) (2022-12-15)
### [0.1.17](https://github.com/nuxtlabs/ui/compare/v0.1.16...v0.1.17) (2022-12-06)
### Bug Fixes
* remove stop propagation on mode hover ([16fd1c0](https://github.com/nuxtlabs/ui/commit/16fd1c0ca38f1438e791c0d44399f590d9f20d02))
### [0.1.16](https://github.com/nuxtlabs/ui/compare/v0.1.15...v0.1.16) (2022-12-06)
### Bug Fixes
* **Popover:** preset from tooltip ([0ade69d](https://github.com/nuxtlabs/ui/commit/0ade69de2689b094b11a2dead8f71e3d2dccd552))
### [0.1.15](https://github.com/nuxtlabs/ui/compare/v0.1.14...v0.1.15) (2022-12-02)
### Bug Fixes
* **Dropdown:** better handle item click to preventDefault ([#119](https://github.com/nuxtlabs/ui/issues/119)) ([44a78d7](https://github.com/nuxtlabs/ui/commit/44a78d7f679812c59d4410d0bbc01f09abaa78dd))
### [0.1.14](https://github.com/nuxtlabs/ui/compare/v0.1.13...v0.1.14) (2022-12-02)
### [0.1.13](https://github.com/nuxtlabs/ui/compare/v0.1.12...v0.1.13) (2022-12-01)
### [0.1.12](https://github.com/nuxtlabs/ui/compare/v0.1.11...v0.1.12) (2022-11-29)
### Bug Fixes
* **Checkbox:** types ([629bb72](https://github.com/nuxtlabs/ui/commit/629bb724249cfe1fdd999cf52f8e72ca444bd94d))
### [0.1.11](https://github.com/nuxtlabs/ui/compare/v0.1.10...v0.1.11) (2022-11-29)
### Bug Fixes
* **Checkbox:** revert type fix as it breaks checkboxes ([7f18c6b](https://github.com/nuxtlabs/ui/commit/7f18c6bdc7c0054b2e5d4f9bf4e362847a3ba3a3))
### [0.1.10](https://github.com/nuxtlabs/ui/compare/v0.1.9...v0.1.10) (2022-11-26)
### Bug Fixes
* default popper options ([1ad9606](https://github.com/nuxtlabs/ui/commit/1ad96065fd706d828b906db3a5d578226ff08c36))
* default props for command palette ([#116](https://github.com/nuxtlabs/ui/issues/116)) ([952786e](https://github.com/nuxtlabs/ui/commit/952786ed79cd9cdf523f6eac5958f68790bacbea))
### [0.1.9](https://github.com/nuxtlabs/ui/compare/v0.1.8...v0.1.9) (2022-11-25)
### Bug Fixes
* **Icon:** couldn't load anymore ([6321d3e](https://github.com/nuxtlabs/ui/commit/6321d3ed8d5c9478cb1dafc1da94b21d0c7edb88))
* **Icon:** eslint ignore ([bc0c168](https://github.com/nuxtlabs/ui/commit/bc0c168c0b0feae96d6a1848c3a356d846e2cbb5))
### [0.1.8](https://github.com/nuxtlabs/ui/compare/v0.1.7...v0.1.8) (2022-11-16)
### [0.1.7](https://github.com/nuxtlabs/ui/compare/v0.1.6...v0.1.7) (2022-11-16)
### [0.1.6](https://github.com/nuxtlabs/ui/compare/v0.1.5...v0.1.6) (2022-11-15)

12
build.config.ts Normal file
View File

@@ -0,0 +1,12 @@
import fs from 'node:fs/promises'
import { join, resolve } from 'node:path'
import { defineBuildConfig } from 'unbuild'
export default defineBuildConfig({
hooks: {
'rollup:done': async (ctx) => {
// copy env.d.ts to dist
await fs.copyFile(resolve('src/env.d.ts'), join(ctx.options.outDir, 'env.d.ts'))
}
}
})

View File

@@ -59,7 +59,14 @@
</template>
<script setup>
useMeta({
useHead({
title: '@nuxthq/ui',
meta: [
{ name: 'viewport', content: 'width=device-width, initial-scale=1, maximum-scale=1' }
],
link: [
{ rel: 'stylesheet', href: 'https://rsms.me/inter/inter.css' }
],
bodyAttrs: {
class: 'antialiased font-sans text-gray-700 bg-gray-50 dark:bg-gray-900 dark:text-gray-200 bg-white dark:bg-black'
}

View File

@@ -3,15 +3,6 @@ import nuxtUI from '../src/module'
// https://v3.nuxtjs.org/docs/directory-structure/nuxt.config
export default defineNuxtConfig({
meta: {
title: '@nuxthq/ui',
meta: [
{ name: 'viewport', content: 'width=device-width, initial-scale=1, maximum-scale=1' }
],
link: [
{ rel: 'stylesheet', href: 'https://rsms.me/inter/inter.css' }
]
},
modules: [
nuxtUI
],

View File

@@ -1,6 +1,6 @@
{
"name": "@nuxthq/ui",
"version": "0.1.6",
"version": "0.1.31",
"repository": "https://github.com/nuxtlabs/ui",
"license": "MIT",
"exports": {
@@ -20,35 +20,37 @@
"dev": "nuxi dev docs",
"build:docs": "nuxi generate docs",
"lint": "eslint --ext .ts,.js,.vue .",
"typecheck": "nuxi typecheck",
"prepare": "nuxi prepare docs",
"release": "yarn lint && standard-version && git push --follow-tags"
},
"dependencies": {
"@headlessui/vue": "^1.7.4",
"@iconify/vue": "^4.0.0",
"@nuxt/kit": "^3.0.0-rc.13",
"@nuxtjs/color-mode": "^3.1.8",
"@nuxtjs/tailwindcss": "^6.1.3",
"@headlessui/vue": "1.7.4",
"@iconify/vue": "^4.0.2",
"@nuxt/kit": "^3.0.0",
"@nuxtjs/color-mode": "^3.2.0",
"@nuxtjs/tailwindcss": "^6.2.0",
"@popperjs/core": "^2.11.6",
"@tailwindcss/aspect-ratio": "^0.4.2",
"@tailwindcss/forms": "^0.5.3",
"@tailwindcss/line-clamp": "^0.4.2",
"@tailwindcss/typography": "^0.5.8",
"@vueuse/core": "^9.4.0",
"@vueuse/integrations": "^9.4.0",
"defu": "^6.1.0",
"@tailwindcss/typography": "^0.5.9",
"@vueuse/core": "^9.11.0",
"@vueuse/integrations": "^9.11.0",
"defu": "^6.1.1",
"fuse.js": "^6.6.2",
"lodash-es": "^4.17.21",
"tailwindcss": "^3.2.2"
"tailwindcss": "^3.2.4"
},
"devDependencies": {
"@nuxt/module-builder": "^0.2.0",
"@nuxtjs/eslint-config-typescript": "^11.0.0",
"@nuxt/module-builder": "^0.2.1",
"@nuxtjs/eslint-config-typescript": "^12.0.0",
"@types/lodash-es": "^4.17.6",
"eslint": "^8.27.0",
"nuxt": "^3.0.0-rc.9",
"eslint": "^8.32.0",
"nuxt": "^3.0.0",
"standard-version": "^9.5.0",
"vue-tsc": "^1.0.9"
"unbuild": "^1.1.1",
"vue-tsc": "^1.0.24"
},
"build": {
"externals": [

6
src/env.d.ts vendored Normal file
View File

@@ -0,0 +1,6 @@
import type { DefaultPreset } from './runtime/presets/default'
declare module '#build/ui' {
declare const preset: DefaultPreset
export default preset
}

View File

@@ -6,9 +6,14 @@
:aria-label="ariaLabel"
v-bind="buttonProps"
>
<Icon v-if="isLeading" :name="leadingIconName" :class="leadingIconClass" aria-hidden="true" />
<slot><span :class="truncate ? 'text-left break-all line-clamp-1' : ''">{{ label }}</span></slot>
<Icon v-if="isTrailing" :name="trailingIconName" :class="trailingIconClass" aria-hidden="true" />
<Icon v-if="isLeading && leadingIconName" :name="leadingIconName" :class="leadingIconClass" aria-hidden="true" />
<slot>
<span :class="[truncate ? 'text-left break-all line-clamp-1' : '', compact ? 'hidden sm:block' : '']">
<span :class="[labelCompact && 'hidden sm:block']">{{ label }}</span>
<span v-if="labelCompact" class="sm:hidden">{{ labelCompact }}</span>
</span>
</slot>
<Icon v-if="isTrailing && trailingIconName" :name="trailingIconName" :class="trailingIconClass" aria-hidden="true" />
</component>
</template>
@@ -34,6 +39,10 @@ const props = defineProps({
type: String,
default: null
},
labelCompact: {
type: String,
default: null
},
loading: {
type: Boolean,
default: false
@@ -127,6 +136,10 @@ const props = defineProps({
truncate: {
type: Boolean,
default: false
},
compact: {
type: Boolean,
default: false
}
})
@@ -164,7 +177,7 @@ const buttonClass = computed(() => {
return classNames(
props.baseClass,
$ui.button.size[props.size],
$ui.button[isSquare.value ? 'square' : 'spacing'][props.size],
$ui.button[isSquare.value ? 'square' : (props.compact ? 'compact' : 'spacing')][props.size],
$ui.button.variant[props.variant],
props.block ? 'w-full flex justify-center items-center' : 'inline-flex items-center',
props.rounded ? 'rounded-full' : props.roundedClass,
@@ -192,7 +205,7 @@ const leadingIconClass = computed(() => {
return classNames(
props.iconBaseClass,
$ui.button.icon.size[props.size],
(!!slots.default || !!props.label?.length) && $ui.button.icon.leading.spacing[props.size],
(!!slots.default || !!props.label?.length) && $ui.button.icon.leading[props.compact ? 'compactSpacing' : 'spacing'][props.size],
props.leadingIconClass,
props.loading && 'animate-spin'
)
@@ -202,7 +215,7 @@ const trailingIconClass = computed(() => {
return classNames(
props.iconBaseClass,
$ui.button.icon.size[props.size],
(!!slots.default || !!props.label?.length) && $ui.button.icon.trailing.spacing[props.size],
(!!slots.default || !!props.label?.length) && $ui.button.icon.trailing[props.compact ? 'compactSpacing' : 'spacing'][props.size],
props.trailingIconClass,
props.loading && !isLeading.value && 'animate-spin'
)

View File

@@ -1,6 +1,12 @@
<template>
<Menu v-slot="{ open }" as="div" :class="wrapperClass" @mouseleave="onMouseLeave">
<MenuButton ref="trigger" as="div" class="inline-flex" @mouseover="onMouseOver">
<MenuButton
ref="trigger"
as="div"
class="inline-flex w-full"
role="button"
@mouseover="onMouseOver"
>
<slot :open="open">
<button>Open</button>
</slot>
@@ -10,8 +16,13 @@
<transition appear v-bind="transitionClass">
<MenuItems :class="baseClass" static>
<div v-for="(subItems, index) of items" :key="index" :class="groupClass">
<MenuItem v-for="(item, subIndex) of subItems" :key="subIndex" v-slot="{ active, disabled }" :disabled="item.disabled" @click="e => item.click?.(e)">
<Component v-bind="item" :is="(item.to && NuxtLink) || (item.click && 'button') || 'div'" :class="resolveItemClass({ active, disabled })">
<MenuItem v-for="(item, subIndex) of subItems" :key="subIndex" v-slot="{ active, disabled }" :disabled="item.disabled">
<Component
v-bind="omit(item, ['click'])"
:is="(item.to && NuxtLink) || (item.click && 'button') || 'div'"
:class="resolveItemClass({ active, disabled })"
@click="item.click"
>
<slot :name="item.slot" :item="item">
<Icon v-if="item.icon" :name="item.icon" :class="[itemIconClass, item.iconClass]" />
<Avatar v-if="item.avatar" v-bind="{ size: 'xxs', ...item.avatar }" :class="itemAvatarClass" />
@@ -38,14 +49,14 @@ import {
MenuItems,
MenuItem
} from '@headlessui/vue'
import type { Ref, PropType } from 'vue'
import type { PropType } from 'vue'
import type { RouteLocationNormalized } from 'vue-router'
import { ref, computed, onMounted } from 'vue'
import { defu } from 'defu'
import NuxtLink from '#app/components/nuxt-link'
import Icon from '../elements/Icon.vue'
import Avatar from '../elements/Avatar.vue'
import { classNames } from '../../utils'
import { classNames, omit } from '../../utils'
import { usePopper } from '../../composables/usePopper'
import type { Avatar as AvatarType } from '../../types/avatar'
import type { PopperOptions } from '../../types'
@@ -54,8 +65,8 @@ import $ui from '#build/ui'
const props = defineProps({
items: {
type: Array as PropType<{
to: RouteLocationNormalized
exact: boolean
to?: RouteLocationNormalized
exact?: boolean
label: string
disabled?: boolean
slot?: string
@@ -128,7 +139,7 @@ const props = defineProps({
},
popperOptions: {
type: Object as PropType<PopperOptions>,
default: () => {}
default: () => ({})
},
openDelay: {
type: Number,
@@ -153,25 +164,20 @@ function resolveItemClass ({ active, disabled }: { active: boolean, disabled: bo
}
// https://github.com/tailwindlabs/headlessui/blob/f66f4926c489fc15289d528294c23a3dc2aee7b1/packages/%40headlessui-vue/src/components/menu/menu.ts#L131
const menuApi: Ref<any> = ref(null)
const menuApi = ref<any>(null)
let openTimeout: NodeJS.Timeout | null = null
let closeTimeout: NodeJS.Timeout | null = null
onMounted(() => {
setTimeout(() => {
// @ts-expect-error internals
const menuProvides = trigger.value?.$.provides
if (!menuProvides) {
return
}
const menuProvidesSymbols = Object.getOwnPropertySymbols(menuProvides)
menuApi.value = menuProvidesSymbols.length && menuProvides[menuProvidesSymbols[0]]
// stop trigger click propagation on hover
menuApi.value?.buttonRef?.addEventListener('click', (e: Event) => {
if (props.mode === 'hover') {
e.stopPropagation()
}
}, true)
}, 200)
})

View File

@@ -18,10 +18,15 @@ const props = defineProps({
}
})
const nuxtApp = useNuxtApp()
const state = useState('u-icons', () => ({}))
const state = useState<Record<string, Required<IconifyIcon> | Promise<void> | null>>('u-icons', () => ({}))
const isFetching = computed(() => !!state.value?.[props.name]?.then)
const icon = computed<IconifyIcon | null>(() => !state.value?.[props.name] || state.value[props.name].then ? null : state.value[props.name])
// eslint-disable-next-line @typescript-eslint/no-non-null-asserted-optional-chain
const isFetching = computed(() => state.value?.[props.name] && ('then' in state.value?.[props.name]!))
const icon = computed<Required<IconifyIcon> | null>(() =>
!state.value?.[props.name] || 'then' in state.value[props.name]!
? null
: state.value[props.name] as Required<IconifyIcon>
)
const component = computed(() => nuxtApp.vueApp.component(props.name))
const loadIconComponent = (name: string) => {

View File

@@ -8,7 +8,7 @@
<router-link
v-else
v-slot="{ href, navigate, isActive, isExactActive }"
v-bind="$props"
v-bind="$props as RouterLinkProps"
custom
>
<a
@@ -25,10 +25,11 @@
<script setup lang="ts">
import { computed } from 'vue'
import type { PropType } from 'vue'
import type { RouteLocationNormalized } from 'vue-router'
import type { RouteLocationNormalized, RouterLinkProps } from 'vue-router'
import { RouterLink } from 'vue-router'
const props = defineProps({
// @ts-expect-error internal props
...RouterLink.props,
to: {
type: [String, Object] as PropType<string | RouteLocationNormalized>,

View File

@@ -2,7 +2,7 @@
<div class="rounded-md p-4" :class="variantClass">
<div class="flex">
<div class="flex-shrink-0">
<Icon :name="iconName" :class="iconClass" class="h-5 w-5" />
<Icon v-if="iconName" :name="iconName" :class="iconClass" class="h-5 w-5" />
</div>
<div class="ml-3 flex-1 md:flex md:justify-between">
<p v-if="title" class="text-sm leading-5" :class="titleClass">

View File

@@ -37,7 +37,7 @@ const props = defineProps({
default: null
},
modelValue: {
type: [String, Number, Boolean, Array],
type: [Boolean, Array],
default: null
},
name: {

View File

@@ -13,22 +13,21 @@
:autocomplete="autocomplete"
:spellcheck="spellcheck"
:class="inputClass"
@input="onInput($event.target.value)"
@input="onInput(($event.target as any).value)"
@focus="$emit('focus', $event)"
@blur="$emit('blur', $event)"
>
<slot />
<div v-if="isLeading" :class="iconLeadingWrapperClass">
<Icon :name="iconName" :class="iconClass" />
<Icon v-if="iconName" :name="iconName" :class="iconClass" />
</div>
<div v-if="isTrailing" :class="iconTrailingWrapperClass">
<Icon :name="iconName" :class="iconClass" />
<Icon v-if="iconName" :name="iconName" :class="iconClass" />
</div>
</div>
</template>
<script setup lang="ts">
import type { Ref } from 'vue'
import { ref, computed, onMounted } from 'vue'
import Icon from '../elements/Icon.vue'
import { classNames } from '../../utils'
@@ -129,11 +128,11 @@ const props = defineProps({
const emit = defineEmits(['update:modelValue', 'focus', 'blur'])
const input: Ref<HTMLInputElement> = ref(null)
const input = ref<HTMLInputElement | null>(null)
const autoFocus = () => {
if (props.autofocus) {
input.value.focus()
input.value?.focus()
}
}

View File

@@ -7,7 +7,7 @@
:required="required"
:disabled="disabled"
:class="selectClass"
@input="onInput($event.target.value)"
@input="onInput(($event.target as any).value)"
>
<template v-for="(option, index) in normalizedOptionsWithPlaceholder">
<optgroup

View File

@@ -10,18 +10,18 @@
:class="wrapperClass"
@update:model-value="onUpdate"
>
<input :value="modelValue" :required="required" class="absolute inset-0 w-px opacity-0 cursor-default" tabindex="-1">
<input :value="modelValue" :required="required" class="absolute inset-0 w-px opacity-0 cursor-default" tabindex="-1" aria-hidden="true">
<ComboboxButton ref="trigger" v-slot="{ disabled: buttonDisabled }" as="div" class="inline-flex w-full">
<ComboboxButton ref="trigger" v-slot="{ disabled: buttonDisabled }" as="div" role="button" class="inline-flex w-full">
<slot :open="open" :disabled="buttonDisabled">
<button :class="selectCustomClass" :disabled="disabled" type="button">
<slot name="label">
<span v-if="modelValue" class="block truncate">{{ modelValue[textAttribute] }}</span>
<span v-if="modelValue" class="block truncate">{{ (modelValue as any)[textAttribute] }}</span>
<span v-else class="block truncate u-text-gray-400">{{ placeholder }}</span>
</slot>
<slot name="icon">
<span :class="iconWrapperClass">
<Icon :name="icon" :class="iconClass" aria-hidden="true" />
<Icon v-if="icon" :name="icon" :class="iconClass" aria-hidden="true" />
</span>
</slot>
</button>
@@ -58,7 +58,7 @@
</div>
<span v-if="selected" :class="resolveOptionIconClass({ active })">
<Icon :name="listOptionIcon" :class="listOptionIconSizeClass" aria-hidden="true" />
<Icon v-if="listOptionIcon" :name="listOptionIcon" :class="listOptionIconSizeClass" aria-hidden="true" />
</span>
</li>
</ComboboxOption>
@@ -84,7 +84,7 @@
</template>
<script setup lang="ts">
import { ref, computed } from 'vue'
import { ref, computed, watch } from 'vue'
import type { PropType, ComponentPublicInstance } from 'vue'
import { defu } from 'defu'
import {
@@ -257,11 +257,11 @@ const props = defineProps({
},
popperOptions: {
type: Object as PropType<PopperOptions>,
default: () => {}
default: () => ({})
}
})
const emit = defineEmits(['update:modelValue'])
const emit = defineEmits(['update:modelValue', 'open', 'close'])
const popperOptions = computed<PopperOptions>(() => defu({}, props.popperOptions, $ui.selectCustom.popperOptions))
@@ -307,6 +307,14 @@ const iconWrapperClass = classNames(
$ui.selectCustom.icon.trailing.wrapper
)
watch(container, (value) => {
if (value) {
emit('open')
} else {
emit('close')
}
})
function resolveOptionClass ({ active, selected, disabled }: { active: boolean, selected: boolean, disabled?: boolean }) {
return classNames(
props.listOptionBaseClass,
@@ -323,7 +331,7 @@ function resolveOptionIconClass ({ active }: { active: boolean }) {
)
}
function onUpdate (event) {
function onUpdate (event: any) {
if (query.value && searchInput.value?.$el) {
query.value = ''
// explicitly set input text because `ComboboxInput` `displayValue` is not reactive

View File

@@ -11,7 +11,7 @@
:placeholder="placeholder"
:autocomplete="autocomplete"
:class="textareaClass"
@input="onInput($event.target.value)"
@input="onInput(($event.target as any).value)"
@focus="$emit('focus', $event)"
@blur="$emit('blur', $event)"
/>
@@ -19,7 +19,6 @@
</template>
<script setup lang="ts">
import type { Ref } from 'vue'
import { ref, computed, watch, onMounted, nextTick } from 'vue'
import { classNames } from '../../utils'
import $ui from '#build/ui'
@@ -95,11 +94,11 @@ const props = defineProps({
const emit = defineEmits(['update:modelValue', 'focus', 'blur'])
const textarea: Ref<HTMLTextAreaElement> = ref(null)
const textarea = ref<HTMLTextAreaElement | null>(null)
const autoFocus = () => {
if (props.autofocus) {
textarea.value.focus()
textarea.value?.focus()
}
}

View File

@@ -19,7 +19,14 @@
@change="query = $event.target.value"
/>
<Button v-if="closeIcon" :icon="closeIcon" variant="transparent" class="absolute right-3" @click="onClear" />
<Button
v-if="closeIcon"
:icon="closeIcon"
variant="transparent"
class="absolute right-2"
aria-label="close"
@click="onClear"
/>
</div>
<ComboboxOptions v-if="groups.length" static hold class="relative flex-1 overflow-y-auto divide-y divide-gray-100 dark:divide-gray-800 scroll-py-2">
@@ -41,7 +48,7 @@
</template>
<script setup lang="ts">
import { ref, computed, onMounted } from 'vue'
import { ref, computed, watch, onMounted } from 'vue'
import { Combobox, ComboboxInput, ComboboxOptions } from '@headlessui/vue'
import type { ComputedRef, PropType, ComponentPublicInstance } from 'vue'
import { useFuse } from '@vueuse/integrations/useFuse'
@@ -104,7 +111,7 @@ const props = defineProps({
},
options: {
type: Object as PropType<Partial<UseFuseOptions<Command>>>,
default: () => {}
default: () => ({})
},
autoselect: {
type: Boolean,
@@ -134,6 +141,7 @@ onMounted(() => {
onMounted(() => {
setTimeout(() => {
// @ts-expect-error internals
const popoverProvides = comboboxInput.value?.$.provides
if (!popoverProvides) {
return
@@ -173,11 +181,19 @@ const groups = computed(() => map(groupBy(results.value, command => command.item
} as Group
}))
watch(groups, () => {
// Select first item on search changes
setTimeout(() => {
// https://github.com/tailwindlabs/headlessui/blob/6fa6074cd5d3a96f78a2d965392aa44101f5eede/packages/%40headlessui-vue/src/components/combobox/combobox.ts#L804
comboboxInput.value?.$el.dispatchEvent(new KeyboardEvent('keydown', { key: 'PageUp' }))
}, 0)
})
// Methods
function activateFirstOption () {
// hack combobox by using keyboard event
// https://github.com/tailwindlabs/headlessui/blob/main/packages/%40headlessui-vue/src/components/combobox/combobox.ts#L692
// https://github.com/tailwindlabs/headlessui/blob/6fa6074cd5d3a96f78a2d965392aa44101f5eede/packages/%40headlessui-vue/src/components/combobox/combobox.ts#L769
setTimeout(() => {
comboboxInput.value?.$el.dispatchEvent(new KeyboardEvent('keydown', { key: 'ArrowDown' }))
}, 0)
@@ -203,7 +219,7 @@ function onClear () {
}
defineExpose({
updateQuery: (q) => {
updateQuery: (q: string) => {
query.value = q
},
comboboxApi,

View File

@@ -1,5 +1,5 @@
<template>
<li class="p-2">
<li class="p-2" role="option">
<h2 v-if="group[groupAttribute]" class="px-3 my-2 text-xs font-semibold u-text-gray-900">
{{ group[groupAttribute] }}
</h2>
@@ -20,6 +20,7 @@
v-else-if="command.avatar"
v-bind="{ size: 'xxxs', ...command.avatar }"
class="flex-shrink-0"
aria-hidden="true"
/>
<span v-else-if="command.chip" class="flex-shrink-0 w-2 h-2 mx-1 rounded-full" :style="{ background: `#${command.chip}` }" />
@@ -72,7 +73,7 @@ defineProps({
}
})
function highlight ({ indices, value }, i = 1) {
function highlight ({ indices, value }: { indices: number[][], value:string }, i = 1): string {
const pair = indices[indices.length - i]
if (!pair) {
return value

View File

@@ -5,7 +5,7 @@
v-slot="{ isActive }"
:key="index"
v-bind="link"
:class="[baseClass, spacingClass]"
:class="[baseClass, spacingClass].join(' ')"
:active-class="activeClass"
:inactive-class="inactiveClass"
@click="link.click && link.click()"
@@ -39,6 +39,7 @@
<script setup lang="ts">
import type { PropType } from 'vue'
import type { RouteLocationNormalized } from 'vue-router'
import Icon from '../elements/Icon.vue'
import Link from '../elements/Link.vue'
import Avatar from '../elements/Avatar.vue'
@@ -48,6 +49,8 @@ import $ui from '#build/ui'
defineProps({
links: {
type: Array as PropType<{
to?: RouteLocationNormalized
exact?: boolean
label: string
icon?: string
iconClass?: string

View File

@@ -9,9 +9,10 @@
</template>
<script setup lang="ts">
import type { PropType } from 'vue'
import type { PropType, Ref } from 'vue'
import { computed, toRef } from 'vue'
import { defu } from 'defu'
import type { VirtualElement } from '@popperjs/core'
import { usePopper } from '../../composables/usePopper'
import type { PopperOptions } from '../../types'
import $ui from '#build/ui'
@@ -47,7 +48,7 @@ const props = defineProps({
},
popperOptions: {
type: Object as PropType<PopperOptions>,
default: () => {}
default: () => ({})
}
})
@@ -62,7 +63,7 @@ const isOpen = computed({
}
})
const virtualElement = toRef(props, 'virtualElement')
const virtualElement = toRef(props, 'virtualElement') as Ref<VirtualElement>
const popperOptions = computed<PopperOptions>(() => defu({}, props.popperOptions, $ui.contextMenu.popperOptions))

View File

@@ -56,6 +56,7 @@ import type { PropType } from 'vue'
import Icon from '../elements/Icon.vue'
import { useTimer } from '../../composables/useTimer'
import { classNames } from '../../utils'
import type { ToastNotificationAction } from '../../types'
import $ui from '#build/ui'
const props = defineProps({
@@ -172,7 +173,7 @@ function onClose () {
emit('close')
}
function onAction (action) {
function onAction (action: ToastNotificationAction) {
if (timer) {
timer.stop()
}

View File

@@ -17,11 +17,12 @@
</template>
<script setup lang="ts">
import type { ToastNotification } from '../../types'
import Notification from './Notification.vue'
import { useNuxtApp, useState } from '#imports'
const { $toast } = useNuxtApp()
const notifications = useState('notifications', () => [])
const notifications = useState<ToastNotification[]>('notifications', () => [])
</script>
<script lang="ts">

View File

@@ -1,6 +1,6 @@
<template>
<Popover v-slot="{ open, close }" :class="wrapperClass" @mouseleave="onMouseLeave">
<PopoverButton ref="trigger" as="div" class="inline-flex" @mouseover="onMouseOver">
<PopoverButton ref="trigger" as="div" class="inline-flex w-full" role="button" @mouseover="onMouseOver">
<slot :open="open" :close="close">
<button>Open</button>
</slot>
@@ -17,8 +17,8 @@
</template>
<script setup lang="ts">
import type { Ref, PropType } from 'vue'
import { computed, ref, onMounted } from 'vue'
import type { PropType } from 'vue'
import { defu } from 'defu'
import { Popover, PopoverButton, PopoverPanel } from '@headlessui/vue'
import { usePopper } from '../../composables/usePopper'
@@ -43,7 +43,7 @@ const props = defineProps({
},
widthClass: {
type: String,
default: () => $ui.tooltip.width
default: () => $ui.popover.width
},
baseClass: {
type: String,
@@ -55,7 +55,7 @@ const props = defineProps({
},
popperOptions: {
type: Object as PropType<PopperOptions>,
default: () => {}
default: () => ({})
},
openDelay: {
type: Number,
@@ -72,25 +72,20 @@ const popperOptions = computed<PopperOptions>(() => defu({}, props.popperOptions
const [trigger, container] = usePopper(popperOptions.value)
// https://github.com/tailwindlabs/headlessui/blob/f66f4926c489fc15289d528294c23a3dc2aee7b1/packages/%40headlessui-vue/src/components/popover/popover.ts#L151
const popoverApi: Ref<any> = ref(null)
const popoverApi = ref<any>(null)
let openTimeout: NodeJS.Timeout | null = null
let closeTimeout: NodeJS.Timeout | null = null
onMounted(() => {
setTimeout(() => {
// @ts-expect-error internals
const popoverProvides = trigger.value?.$.provides
if (!popoverProvides) {
return
}
const popoverProvidesSymbols = Object.getOwnPropertySymbols(popoverProvides)
popoverApi.value = popoverProvidesSymbols.length && popoverProvides[popoverProvidesSymbols[0]]
// stop trigger click propagation on hover
popoverApi.value?.button?.addEventListener('click', (e: Event) => {
if (props.mode === 'hover') {
e.stopPropagation()
}
}, true)
}, 200)
})

View File

@@ -1,6 +1,6 @@
<template>
<TransitionRoot as="template" :appear="appear" :show="isOpen">
<Dialog :class="[wrapperClass, { 'justify-end': side === 'right' }]" @close="isOpen = false">
<Dialog :class="[wrapperClass, { 'justify-end': side === 'right' }]" @close="close">
<TransitionChild
v-if="overlay"
as="template"
@@ -89,7 +89,7 @@ const props = defineProps({
}
})
const emit = defineEmits(['update:modelValue'])
const emit = defineEmits(['update:modelValue', 'close'])
const isOpen: WritableComputedRef<boolean> = computed({
get () {
@@ -129,6 +129,11 @@ const slideoverTransition = computed(() => {
...props.transitionClass
}
})
function close (value: boolean) {
isOpen.value = value
emit('close')
}
</script>
<script lang="ts">

View File

@@ -4,7 +4,7 @@
Hover me
</slot>
<div v-if="open" ref="container" :class="[containerClass, widthClass]">
<div v-if="open && !prevent" ref="container" :class="[containerClass, widthClass]">
<transition appear v-bind="transitionClass">
<div :class="[baseClass, backgroundClass, roundedClass, shadowClass, ringClass]">
<slot name="text">
@@ -36,6 +36,10 @@ const props = defineProps({
type: String,
default: null
},
prevent: {
type: Boolean,
default: false
},
shortcuts: {
type: Array as PropType<string[]>,
default: () => []
@@ -78,7 +82,7 @@ const props = defineProps({
},
popperOptions: {
type: Object as PropType<PopperOptions>,
default: () => {}
default: () => ({})
},
openDelay: {
type: Number,

View File

@@ -1,6 +1,6 @@
import { ref, onMounted, watchEffect } from 'vue'
import type { Ref } from 'vue'
import { popperGenerator, defaultModifiers } from '@popperjs/core/lib/popper-lite'
import { popperGenerator, defaultModifiers, VirtualElement } from '@popperjs/core/lib/popper-lite'
import type { Instance } from '@popperjs/core'
import { omitBy, isUndefined } from 'lodash-es'
import flip from '@popperjs/core/lib/modifiers/flip'
@@ -8,6 +8,7 @@ import offset from '@popperjs/core/lib/modifiers/offset'
import preventOverflow from '@popperjs/core/lib/modifiers/preventOverflow'
import computeStyles from '@popperjs/core/lib/modifiers/computeStyles'
import eventListeners from '@popperjs/core/lib/modifiers/eventListeners'
import { MaybeElement, unrefElement } from '@vueuse/core'
import type { PopperOptions } from '../types'
export const createPopper = popperGenerator({
@@ -25,21 +26,22 @@ export function usePopper ({
resize = true,
placement,
strategy
}: PopperOptions, virtualReference: Ref<Object> = null) {
const reference: Ref<HTMLElement> = ref(null)
const popper: Ref<HTMLElement> = ref(null)
const instance: Ref<Instance> = ref(null)
}: PopperOptions, virtualReference?: Ref<Element | VirtualElement>) {
const reference = ref<MaybeElement>(null)
const popper = ref<MaybeElement>(null)
const instance = ref<Instance | null>(null)
onMounted(() => {
watchEffect((onInvalidate) => {
if (!popper.value) { return }
if (!reference.value && !virtualReference.value) { return }
if (!reference.value && !virtualReference?.value) { return }
const popperEl = popper.value.$el || popper.value
const referenceEl = virtualReference?.value || reference.value.$el || reference.value
const popperEl = unrefElement(popper)
const referenceEl = virtualReference?.value || unrefElement(reference)
// if (!(referenceEl instanceof HTMLElement)) { return }
if (!(popperEl instanceof HTMLElement)) { return }
if (!referenceEl) { return }
instance.value = createPopper(referenceEl, popperEl, omitBy({
placement,
@@ -76,5 +78,5 @@ export function usePopper ({
})
})
return [reference, popper, instance]
return [reference, popper, instance] as const
}

View File

@@ -1,10 +1,10 @@
import { Ref, ref, computed } from 'vue-demi'
import { ref, computed } from 'vue-demi'
import { useTimestamp } from '@vueuse/core'
export function useTimer (cb: (...args: unknown[]) => any, interval: number) {
let timer: number | null = null
const timestamp = useTimestamp({ controls: true })
const startTime: Ref<number | null> = ref(null)
const startTime = ref<number | null>(null)
const remaining = computed(() => {
if (!startTime.value) {
@@ -46,7 +46,7 @@ export function useTimer (cb: (...args: unknown[]) => any, interval: number) {
}
function resume () {
startTime.value += (Date.now() - timestamp.timestamp.value)
startTime.value = (startTime.value || 0) + (Date.now() - timestamp.timestamp.value)
timestamp.resume()
set()
}

View File

@@ -1,9 +1,8 @@
import type { Ref } from 'vue'
import { defineNuxtPlugin, useState } from '#app'
import type { ToastNotification } from '../types'
export default defineNuxtPlugin(() => {
const notifications: Ref<ToastNotification[]> = useState('notifications', () => [])
const notifications = useState<ToastNotification[]>('notifications', () => [])
function addNotification (notification: Partial<ToastNotification>) {
const body = {

View File

@@ -1,4 +1,4 @@
export default (variantColors: string[]) => {
export default function defaultPreset (variantColors: string[]) {
const button = {
base: 'font-medium focus:outline-none disabled:cursor-not-allowed disabled:opacity-75 focus:ring-offset-white dark:focus:ring-offset-black',
rounded: 'rounded-md',
@@ -26,6 +26,14 @@ export default (variantColors: string[]) => {
lg: 'p-2',
xl: 'p-3'
},
compact: {
xxs: 'p-1 sm:px-2',
xs: 'p-1.5 sm:px-2.5',
sm: 'p-2 sm:px-3',
md: 'p-2 sm:px-4',
lg: 'p-2 sm:px-4',
xl: 'p-3 sm:px-6'
},
variant: {
...variantColors.reduce((acc: any, color: string) => {
acc[color] = `shadow-sm border border-transparent text-white bg-${color}-600 hover:bg-${color}-700 disabled:bg-${color}-600 focus:ring-2 focus:ring-offset-2 focus:ring-${color}-500`
@@ -57,6 +65,14 @@ export default (variantColors: string[]) => {
md: '-ml-1 mr-2',
lg: '-ml-1 mr-3',
xl: '-ml-1 mr-3'
},
compactSpacing: {
xxs: 'sm:-ml-0.5 sm:mr-1',
xs: 'sm:-ml-0.5 sm:mr-1.5',
sm: 'sm:-ml-0.5 sm:mr-2',
md: 'sm:-ml-1 sm:mr-2',
lg: 'sm:-ml-1 sm:mr-3',
xl: 'sm:-ml-1 sm:mr-3'
}
},
trailing: {
@@ -67,6 +83,14 @@ export default (variantColors: string[]) => {
md: 'ml-2 -mr-1',
lg: 'ml-3 -mr-1',
xl: 'ml-3 -mr-1'
},
compactSpacing: {
xxs: 'sm:ml-1 sm:-mr-0.5',
xs: 'sm:ml-1.5 sm:-mr-0.5',
sm: 'sm:ml-2 sm:-mr-0.5',
md: 'sm:ml-2 sm:-mr-1',
lg: 'sm:ml-3 sm:-mr-1',
xl: 'sm:ml-3 sm:-mr-1'
}
}
}
@@ -570,3 +594,5 @@ export default (variantColors: string[]) => {
contextMenu
}
}
export type DefaultPreset = ReturnType<typeof defaultPreset>

View File

@@ -1,3 +1,8 @@
export interface ToastNotificationAction {
label: string,
click: Function
}
export interface ToastNotification {
id: string
title: string
@@ -5,10 +10,8 @@ export interface ToastNotification {
type: string
icon?: string
timeout: number
actions?: {
label: string,
click: Function
}[]
actions?: ToastNotificationAction[]
click?: Function
callback?: Function
}

View File

@@ -1,3 +1,14 @@
{
"extends": "./docs/.nuxt/tsconfig.json"
"extends": "./docs/.nuxt/tsconfig.json",
"compilerOptions": {
"module": "esnext"
},
"include": [
"./**/*",
"./docs/.nuxt/nuxt.d.ts"
],
"exclude": [
"node_modules",
"dist"
]
}

2266
yarn.lock

File diff suppressed because it is too large Load Diff