diff --git a/package.json b/package.json index 7cac5a278..a0e61faee 100644 --- a/package.json +++ b/package.json @@ -54,6 +54,7 @@ "@oku-ui/separator": "workspace:^", "@oku-ui/slot": "workspace:^", "@oku-ui/switch": "workspace:^", + "@oku-ui/tabs": "workspace:^", "@oku-ui/toggle": "workspace:^", "@oku-ui/use-composable": "workspace:^", "@oku-ui/utils": "workspace:^", diff --git a/packages/components/tabs/README.md b/packages/components/tabs/README.md new file mode 100644 index 000000000..31237339f --- /dev/null +++ b/packages/components/tabs/README.md @@ -0,0 +1,10 @@ +# `@oku-ui/tabs` + +## Installation + +```sh +$ pnpm add @oku-ui/tabs +``` + +## Usage +... \ No newline at end of file diff --git a/packages/components/tabs/build.config.ts b/packages/components/tabs/build.config.ts new file mode 100644 index 000000000..b972b9a78 --- /dev/null +++ b/packages/components/tabs/build.config.ts @@ -0,0 +1,12 @@ +import { defineBuildConfig } from 'unbuild' + +export default defineBuildConfig({ + entries: [ + { + builder: 'mkdist', + input: './src/', + pattern: ['**/!(*.test|*.stories).ts'], + }, + ], + declaration: true, +}) diff --git a/packages/components/tabs/package.json b/packages/components/tabs/package.json new file mode 100644 index 000000000..561893bcf --- /dev/null +++ b/packages/components/tabs/package.json @@ -0,0 +1,47 @@ +{ + "name": "@oku-ui/tabs", + "type": "module", + "version": "0.1.0", + "license": "MIT", + "source": "src/index.ts", + "funding": "https://github.com/sponsors/productdevbook", + "homepage": "https://oku-ui.com/primitives", + "repository": { + "type": "git", + "url": "git+https://github.com/oku-ui/primitives.git", + "directory": "packages/components/tabs" + }, + "bugs": { + "url": "https://github.com/oku-ui/primitives/issues" + }, + "exports": { + ".": { + "types": "./dist/index.d.ts", + "import": "./dist/index.mjs" + } + }, + "module": "./dist/index.mjs", + "types": "./dist/index.d.ts", + "files": [ + "dist" + ], + "scripts": { + "build": "tsup", + "dev": "tsup --watch" + }, + "peerDependencies": { + "vue": "^3.3.4" + }, + "dependencies": { + "@oku-ui/direction": "latest", + "@oku-ui/presence": "latest", + "@oku-ui/primitive": "latest", + "@oku-ui/provide": "latest", + "@oku-ui/roving-focus": "latest", + "@oku-ui/use-composable": "latest", + "@oku-ui/utils": "latest" + }, + "devDependencies": { + "tsconfig": "workspace:^" + } +} diff --git a/packages/components/tabs/src/index.ts b/packages/components/tabs/src/index.ts new file mode 100644 index 000000000..517b59858 --- /dev/null +++ b/packages/components/tabs/src/index.ts @@ -0,0 +1,9 @@ +export { OkuTabs } from './tabs' +export { OkuTabList } from './tab-list' +export { OkuTabTrigger } from './tab-trigger' +export { OkuTabContent } from './tab-content' + +export type { TabsProps } from './tabs' +export { type TabListProps } from './tab-list' +export { type TabsTriggerProps } from './tab-trigger' +export { type TabsContentProps } from './tab-content' diff --git a/packages/components/tabs/src/stories/TabsDemo.vue b/packages/components/tabs/src/stories/TabsDemo.vue new file mode 100644 index 000000000..2bccf1c68 --- /dev/null +++ b/packages/components/tabs/src/stories/TabsDemo.vue @@ -0,0 +1,301 @@ + + + + + diff --git a/packages/components/tabs/src/stories/tabs.stories.ts b/packages/components/tabs/src/stories/tabs.stories.ts new file mode 100644 index 000000000..6aa681837 --- /dev/null +++ b/packages/components/tabs/src/stories/tabs.stories.ts @@ -0,0 +1,70 @@ +import type { Meta, StoryObj } from '@storybook/vue3' + +import type { ITabsProps } from './TabsDemo.vue' +import OkuTabsComponent from './TabsDemo.vue' + +interface StoryProps extends ITabsProps { } + +const meta = { + title: 'Components/Tabs', + component: OkuTabsComponent, + args: { + template: '#1', + }, + tags: ['autodocs'], +} satisfies Meta & { + args: StoryProps +} + +export default meta +type Story = StoryObj + +export const Styled: Story = { + args: { + template: '#1', + // allShow: true, + }, + render: (args: any) => ({ + components: { OkuTabsComponent }, + setup() { + return { args } + }, + template: ` + + `, + }), +} + +export const Indeterminate: Story = { + args: { + template: '#2', + // allShow: true, + }, + + render: (args: any) => ({ + components: { OkuTabsComponent }, + setup() { + return { args } + }, + template: ` + + `, + }), +} + +export const Form: Story = { + args: { + template: '#3', + // allShow: true, + }, + + render: (args: any) => ({ + components: { OkuTabsComponent }, + setup() { + return { args } + }, + template: ` + + `, + }), +} diff --git a/packages/components/tabs/src/tab-content.ts b/packages/components/tabs/src/tab-content.ts new file mode 100644 index 000000000..8cc02e84e --- /dev/null +++ b/packages/components/tabs/src/tab-content.ts @@ -0,0 +1,91 @@ +import { Primitive, PrimitiveProps } from '@oku-ui/primitive' +import type { ElementType, IPrimitiveProps, InstanceTypeRef, MergeProps } from '@oku-ui/primitive' +import { computed, defineComponent, h, ref, toRefs, watchEffect } from 'vue' +import type { ComputedRef, PropType } from 'vue' +import { OkuPresence } from '@oku-ui/presence' +import { useForwardRef } from '@oku-ui/use-composable' +import type { ScopedPropsInterface } from './tabs' +import { ScopedProps, useTabsInject } from './tabs' +import { makeContentId, makeTriggerId } from './utils' + +const TAB_CONTENT_NAME = 'OkuTabContent' as const + +type TabsContentElement = ElementType<'div'> +export type _TabsContentEl = HTMLDivElement + +interface TabsContentProps extends ScopedPropsInterface { + value: string + + /** + * Used to force mounting when more control is needed. Useful when + * controlling animation with React animation libraries. + */ + forceMount?: true +} + +const TabContent = defineComponent({ + name: TAB_CONTENT_NAME, + inheritAttrs: false, + props: { + value: { + type: String as PropType, + required: true, + }, + forceMount: { + type: Boolean as PropType, + default: false, + }, + ...PrimitiveProps, + ...ScopedProps, + }, + setup(props, { slots, attrs }) { + const { value } = toRefs(props) + const { ...ContentAttrs } = attrs + const injectTabs = useTabsInject(TAB_CONTENT_NAME, props.scopeTabs) + + const triggerId = makeTriggerId(injectTabs.baseId, value.value) + const contentId = makeContentId(injectTabs.baseId, value.value) + const isSelected = computed(() => value.value === injectTabs.value?.value) + + const forwardedRef = useForwardRef() + const isMountAnimationPreventedRef = ref(isSelected.value) + + watchEffect((onClean) => { + const rAF = requestAnimationFrame(() => (isMountAnimationPreventedRef.value = false)) + onClean(() => cancelAnimationFrame(rAF)) + }) + + return () => h(OkuPresence, { + present: isSelected.value || props.forceMount, + }, { + default: ({ isPresent }: { isPresent: ComputedRef }) => h(Primitive.div, { + 'data-state': isSelected.value ? 'active' : 'inactive', + 'data-orientation': injectTabs.orientation, + 'role': 'tabpanel', + 'aria-labelledby': triggerId, + 'hidden': !isPresent, + 'id': contentId, + 'tabindex': '0', + ...ContentAttrs, + 'ref': forwardedRef, + 'style': { + ...ContentAttrs.style ?? {} as any, + animationDuration: isMountAnimationPreventedRef.value ? '0s' : undefined, + }, + }, { + default: () => isPresent ? slots.default?.() : null, + }), + }) + }, +}) + +type _TabsProps = MergeProps + +type InstanceTabsContent = InstanceTypeRef + +const OkuTabContent = TabContent as typeof TabContent & +(new () => { $props: _TabsProps }) + +export { OkuTabContent } + +export type { TabsContentProps, InstanceTabsContent } diff --git a/packages/components/tabs/src/tab-list.ts b/packages/components/tabs/src/tab-list.ts new file mode 100644 index 000000000..94bd1e5f9 --- /dev/null +++ b/packages/components/tabs/src/tab-list.ts @@ -0,0 +1,79 @@ +import { Primitive, PrimitiveProps } from '@oku-ui/primitive' +import type { + ElementType, + IPrimitiveProps, + InstanceTypeRef, + MergeProps, +} from '@oku-ui/primitive' +import type { PropType } from 'vue' +import { defineComponent, h, toRefs } from 'vue' +import { useForwardRef } from '@oku-ui/use-composable' +import { OkuRovingFocusGroup, createRovingFocusGroupScope } from '@oku-ui/roving-focus' +import type { ScopedPropsInterface } from './tabs' +import { ScopedProps, useTabsInject } from './tabs' + +type TabListElement = ElementType<'div'> +export type _TabListEl = HTMLDivElement + +const TAB_LIST_NAME = 'OkuTabList' as const + +interface TabListProps extends ScopedPropsInterface { + loop?: boolean +} + +const useRovingFocusGroupScope = createRovingFocusGroupScope() + +const TabList = defineComponent({ + name: TAB_LIST_NAME, + inheritAttrs: false, + props: { + loop: { + type: Boolean as PropType, + default: true, + }, + ...PrimitiveProps, + ...ScopedProps, + }, + setup(props, { slots, attrs }) { + const { loop } = toRefs(props) + const { ...listAttrs } = attrs + + const injectTabs = useTabsInject(TAB_LIST_NAME, props.scopeTabs) + const forwardedRef = useForwardRef() + + const rovingFocusGroupScope = useRovingFocusGroupScope(props.scopeTabs) + + return () => + h(OkuRovingFocusGroup, { + asChild: true, + ...rovingFocusGroupScope.value, + dir: injectTabs.dir, + loop: loop.value, + orientation: injectTabs.orientation, + }, { + default: () => h( + Primitive.div, + { + 'role': 'tablist', + 'aria-orientation': injectTabs.orientation, + 'asChild': props.asChild, + ...listAttrs, + 'ref': forwardedRef, + }, + { + default: () => slots.default?.(), + }, + ), + }) + }, +}) + +type _TabListProps = MergeProps + +export type InstanceTabListType = InstanceTypeRef + +const OkuTabList = TabList as typeof TabList & (new () => { $props: _TabListProps }) + +export { OkuTabList } + +export type { TabListProps } diff --git a/packages/components/tabs/src/tab-trigger.ts b/packages/components/tabs/src/tab-trigger.ts new file mode 100644 index 000000000..fb0ec57e0 --- /dev/null +++ b/packages/components/tabs/src/tab-trigger.ts @@ -0,0 +1,112 @@ +import { Primitive, PrimitiveProps } from '@oku-ui/primitive' +import type { ElementType, IPrimitiveProps, InstanceTypeRef, MergeProps } from '@oku-ui/primitive' +import { type PropType, computed, defineComponent, h, toRefs } from 'vue' +import { useForwardRef } from '@oku-ui/use-composable' +import { OkuRovingFocusGroupItem } from '@oku-ui/roving-focus' +import { composeEventHandlers } from '@oku-ui/utils' +import type { ScopedPropsInterface } from './tabs' +import { ScopedProps, useRovingFocusGroupScope, useTabsInject } from './tabs' +import { makeContentId, makeTriggerId } from './utils' + +type TabsTriggerElement = ElementType<'button'> +export type _TabsTriggerEl = HTMLButtonElement + +const TAB_TRIGGER_NAME = 'OkuTabTrigger' as const + +interface TabsTriggerProps extends ScopedPropsInterface { + value: string + disabled?: boolean + onMousedown?: (event: MouseEvent) => void + onKeydown?: (event: KeyboardEvent) => void + onFocus?: (event: FocusEvent) => void +} + +const TabTrigger = defineComponent({ + name: TAB_TRIGGER_NAME, + inheritAttrs: false, + props: { + value: { + type: String as PropType, + required: true, + }, + disabled: { + type: Boolean as PropType, + default: false, + }, + onMousedown: Function as PropType<(e: MouseEvent) => void>, + onKeydown: Function as PropType<(e: KeyboardEvent) => void>, + onFocus: Function as PropType<(e: FocusEvent) => void>, + ...ScopedProps, + ...PrimitiveProps, + }, + setup(props, { slots, attrs }) { + const { scopeTabs, value, disabled } = toRefs(props) + const { ...triggerAttrs } = attrs + const injectedValue = useTabsInject(TAB_TRIGGER_NAME, scopeTabs.value) + + const forwardedRef = useForwardRef() + + const rovingFocusGroupScope = useRovingFocusGroupScope(scopeTabs.value) + const triggerId = makeTriggerId(injectedValue.baseId, value.value) + const contentId = makeContentId(injectedValue.baseId, value.value) + const isSelected = computed(() => (value.value === injectedValue.value?.value)) + return () => + h(OkuRovingFocusGroupItem, { + asChild: true, + ...rovingFocusGroupScope, + active: isSelected.value, + focusable: !disabled.value, + }, { + default: () => h( + Primitive.button, + { + 'type': 'button', + 'role': 'tab', + 'aria-selected': isSelected.value, + 'aria-controls': contentId, + 'data-state': isSelected.value ? 'active' : 'inactive', + 'data-disabled': disabled.value ? '' : undefined, + 'disabled': disabled.value, + 'id': triggerId, + ...triggerAttrs, + 'ref': forwardedRef, + 'onMousedown': composeEventHandlers(props.onMousedown, (event: MouseEvent) => { + // only call handler if it's the left button (mousedown gets triggered by all mouse buttons) + // but not when the control key is pressed (avoiding MacOS right click) + if (!disabled.value && event.button === 0 && event.ctrlKey === false) { + injectedValue.onValueChange(value.value) + } + else { + // prevent focus to avoid accidental activation + event.preventDefault() + } + }), + 'onKeydown': composeEventHandlers(props.onKeydown, (event: KeyboardEvent) => { + if ([' ', 'Enter'].includes(event.key)) + injectedValue.onValueChange(value.value) + }), + 'onFocus': composeEventHandlers(props.onFocus, () => { + // handle "automatic" activation if necessary + // ie. activate tab following focus + const isAutomaticActivation = injectedValue.activationMode !== 'manual' + if (!isSelected.value && !disabled.value && isAutomaticActivation) + injectedValue.onValueChange(value.value) + }), + }, + { + default: () => slots.default?.(), + }, + ), + }) + }, +}) + +type _TabsProps = MergeProps + +export type InstanceTabsTriggerType = InstanceTypeRef + +const OkuTabTrigger = TabTrigger as typeof TabTrigger & (new () => { $props: _TabsProps }) + +export { OkuTabTrigger } + +export type { TabsTriggerProps } diff --git a/packages/components/tabs/src/tabs.ts b/packages/components/tabs/src/tabs.ts new file mode 100644 index 000000000..b0f00078e --- /dev/null +++ b/packages/components/tabs/src/tabs.ts @@ -0,0 +1,199 @@ +import type { ElementType, IPrimitiveProps, InstanceTypeRef, MergeProps, RefElement } from '@oku-ui/primitive' +import { Primitive } from '@oku-ui/primitive' +import { computed, defineComponent, h, toRefs, useModel } from 'vue' +import type { ComputedRef, PropType } from 'vue' +import type { Scope } from '@oku-ui/provide' +import { createProvideScope } from '@oku-ui/provide' +import { createRovingFocusGroupScope } from '@oku-ui/roving-focus' +import { useControllable, useForwardRef, useId } from '@oku-ui/use-composable' +import { useDirection } from '@oku-ui/direction' + +const TAB_NAME = 'OkuTab' as const + +type TabsElement = ElementType<'div'> +export type _TabsEl = HTMLDivElement + +export type ScopedPropsInterface

= P & { scopeTabs?: Scope } +export const ScopedProps = { + scopeTabs: { + type: Object as PropType, + required: false, + }, +} + +type Orientation = 'horizontal' | 'vertical' +type Direction = 'ltr' | 'rtl' +/** + * Whether a tab is activated automatically or manually. + * @defaultValue automatic + * */ +type ActivationMode = 'automatic' | 'manual' +interface TabsProps extends ScopedPropsInterface { + /** The value for the selected tab, if controlled */ + value?: string + /** + * The default value of the tab. + * @default 'tab1' + * @type string + * @example + * ```vue + * + // ... + * + * ``` + * @see link-to-oku-docs/tab + */ + defaultValue?: string + /** + * The callback function that is called when the tab value changes. + * @default () => {} + * @type (value: string) => void + * @example + * ```vue + * console.log(value)}> + // ... + * + * */ + onValueChange?: (value: string) => void + /** + * The orientation of the tabs. + * @default 'horizontal' + * @type 'horizontal' | 'vertical' + * @example + * ```vue + * + // ... + * + * ``` + * @see link-to-oku-docs/tab + * */ + orientation?: Orientation + /** + * The direction of navigation between toolbar items. + */ + dir?: Direction + /** + * Whether a tab is activated automatically or manually. + * @defaultValue automatic + * */ + activationMode?: ActivationMode +} + +interface TabsProvideValue { + baseId: string + value?: ComputedRef + onValueChange: (value: string) => void + orientation?: TabsProps['orientation'] + dir?: TabsProps['dir'] + activationMode?: TabsProps['activationMode'] +} + +export const [createTabsProvider, _createTabsScope] = createProvideScope(TAB_NAME, [ + createRovingFocusGroupScope, +]) + +export const [TabsProvider, useTabsInject] + = createTabsProvider(TAB_NAME) + +export const useRovingFocusGroupScope = createRovingFocusGroupScope() + +const Tabs = defineComponent({ + name: TAB_NAME, + inheritAttrs: false, + props: { + value: { + type: String as PropType, + required: false, + }, + defaultValue: { + type: String as PropType, + default: undefined, + }, + orientation: { + type: String as PropType, + default: 'horizontal', + }, + dir: { + type: String as PropType, + default: 'ltr', + required: false, + }, + activationMode: { + type: String as PropType, + default: 'automatic', + required: false, + }, + modelValue: { + type: String as PropType, + required: false, + }, + onValueChange: { + type: Function as PropType<(value: string) => void>, + required: false, + }, + asChild: { + type: Boolean as PropType, + default: false, + }, + ...ScopedProps, + }, + emits: ['update:modelValue'], + setup(props, { slots, emit, attrs }) { + const { + value: valueProp, + onValueChange, + defaultValue, + orientation, + dir, + activationMode, + } = toRefs(props) + + const direction = useDirection(dir.value) + + const forwardedRef = useForwardRef() + + const modelValue = useModel(props, 'modelValue') + + const { state, updateValue } = useControllable({ + prop: computed(() => modelValue.value ?? valueProp.value), + defaultProp: computed(() => defaultValue.value), + onChange: (result: any) => { + emit('update:modelValue', result) + onValueChange.value?.(result) + }, + }) + + TabsProvider({ + onValueChange: updateValue, + orientation: orientation.value, + dir: direction, + value: state, + activationMode: activationMode.value, + baseId: useId(), + scope: props.scopeTabs, + }) + + return () => + h( + Primitive.div, + { + 'dir': direction, + 'data-orientation': props.orientation, + 'ref': forwardedRef, + ...attrs, + 'asChild': props.asChild, + }, slots, + ) + }, +}) + +type _TabsProps = MergeProps +export type IstanceTabsType = InstanceTypeRef + +type TabsRef = RefElement + +const OkuTabs = Tabs as typeof Tabs & (new () => { $props: _TabsProps }) + +export { OkuTabs } + +export type { TabsProps, TabsRef } diff --git a/packages/components/tabs/src/utils.ts b/packages/components/tabs/src/utils.ts new file mode 100644 index 000000000..a865868d7 --- /dev/null +++ b/packages/components/tabs/src/utils.ts @@ -0,0 +1,7 @@ +export function makeTriggerId(baseId: string, value: string) { + return `${baseId}-trigger-${value}` +} + +export function makeContentId(baseId: string, value: string) { + return `${baseId}-content-${value}` +} diff --git a/packages/components/tabs/tsconfig.json b/packages/components/tabs/tsconfig.json new file mode 100644 index 000000000..b8dfa9041 --- /dev/null +++ b/packages/components/tabs/tsconfig.json @@ -0,0 +1,10 @@ +{ + "extends": "tsconfig/node16.json", + "compilerOptions": { + "rootDir": "src", + "outDir": "dist" + }, + "include": [ + "src" + ] +} diff --git a/packages/components/tabs/tsup.config.ts b/packages/components/tabs/tsup.config.ts new file mode 100644 index 000000000..a2f7a0d8b --- /dev/null +++ b/packages/components/tabs/tsup.config.ts @@ -0,0 +1,22 @@ +import { defineConfig } from 'tsup' +import pkg from './package.json' + +const external = [ + ...Object.keys(pkg.dependencies || {}), + ...Object.keys(pkg.peerDependencies || {}), +] + +export default defineConfig((options) => { + return [ + { + ...options, + entryPoints: ['src/index.ts'], + external, + dts: true, + clean: true, + target: 'node16', + format: ['esm'], + outExtension: () => ({ js: '.mjs' }), + }, + ] +}) diff --git a/packages/core/use-composable/src/index.ts b/packages/core/use-composable/src/index.ts index bf791cd93..3a9b04522 100644 --- a/packages/core/use-composable/src/index.ts +++ b/packages/core/use-composable/src/index.ts @@ -10,5 +10,4 @@ export { useComposedRefs } from './useComposedRefs' export { useForwardRef } from './useForwardRef' export { useEscapeKeydown } from './useEscapeKeydown' export type { MaybeComputedElementRef } from './unrefElement' -export { useRect } from './use-rect' export { computedEager, syncRef, computedAsync } diff --git a/playground/nuxt3/package.json b/playground/nuxt3/package.json index 3a5cc9eb8..f120bdc14 100644 --- a/playground/nuxt3/package.json +++ b/playground/nuxt3/package.json @@ -22,6 +22,7 @@ "@oku-ui/separator": "workspace:^", "@oku-ui/slot": "workspace:^", "@oku-ui/switch": "workspace:^", + "@oku-ui/tabs": "workspace:^", "@oku-ui/visually-hidden": "workspace:^" }, "devDependencies": { diff --git a/playground/nuxt3/pages/index.vue b/playground/nuxt3/pages/index.vue index fb7139b86..540ad87e7 100644 --- a/playground/nuxt3/pages/index.vue +++ b/playground/nuxt3/pages/index.vue @@ -56,6 +56,10 @@ const pages: Page[] = [ name: 'roving-focus', path: '/roving-focus', }, + { + name: 'OkuTabs', + path: '/tabs', + }, ] diff --git a/playground/nuxt3/pages/tabs.vue b/playground/nuxt3/pages/tabs.vue new file mode 100644 index 000000000..17f56e5f8 --- /dev/null +++ b/playground/nuxt3/pages/tabs.vue @@ -0,0 +1,5 @@ + diff --git a/playground/vue3/package.json b/playground/vue3/package.json index 627aa713c..1c01e3663 100644 --- a/playground/vue3/package.json +++ b/playground/vue3/package.json @@ -20,6 +20,7 @@ "@oku-ui/separator": "workspace:^", "@oku-ui/slot": "workspace:^", "@oku-ui/switch": "workspace:^", + "@oku-ui/tabs": "workspace:^", "vite-plugin-pages": "^0.31.0", "vue": "^3.3.4", "vue-router": "^4.2.4" diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index b4371c9a2..5c10aa737 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -70,6 +70,9 @@ importers: '@oku-ui/switch': specifier: workspace:^ version: link:packages/components/switch + '@oku-ui/tabs': + specifier: workspace:^ + version: link:packages/components/tabs '@oku-ui/toggle': specifier: workspace:^ version: link:packages/components/toggle @@ -515,6 +518,37 @@ importers: specifier: workspace:^ version: link:../../tsconfig + packages/components/tabs: + dependencies: + '@oku-ui/direction': + specifier: latest + version: link:../direction + '@oku-ui/presence': + specifier: latest + version: link:../presence + '@oku-ui/primitive': + specifier: latest + version: link:../../core/primitive + '@oku-ui/provide': + specifier: latest + version: link:../../core/provide + '@oku-ui/roving-focus': + specifier: latest + version: link:../roving-focus + '@oku-ui/use-composable': + specifier: latest + version: link:../../core/use-composable + '@oku-ui/utils': + specifier: latest + version: link:../../core/utils + vue: + specifier: ^3.3.4 + version: 3.3.4 + devDependencies: + tsconfig: + specifier: workspace:^ + version: link:../../tsconfig + packages/components/toggle: dependencies: '@oku-ui/primitive': @@ -657,6 +691,9 @@ importers: '@oku-ui/switch': specifier: workspace:^ version: link:../../packages/components/switch + '@oku-ui/tabs': + specifier: workspace:^ + version: link:../../packages/components/tabs '@oku-ui/visually-hidden': specifier: workspace:^ version: link:../../packages/components/visually-hidden @@ -700,6 +737,9 @@ importers: '@oku-ui/switch': specifier: workspace:^ version: link:../../packages/components/switch + '@oku-ui/tabs': + specifier: workspace:^ + version: link:../../packages/components/tabs vite-plugin-pages: specifier: ^0.31.0 version: 0.31.0(vite@4.3.5)