diff --git a/package.json b/package.json index cba0fe840..4cc5a4d19 100644 --- a/package.json +++ b/package.json @@ -48,6 +48,7 @@ "@oku-ui/provide": "workspace:^", "@oku-ui/separator": "workspace:^", "@oku-ui/switch": "workspace:^", + "@oku-ui/visually-hidden": "workspace:^", "@oku-ui/toggle": "workspace:^", "@oku-ui/use-composable": "workspace:^", "@oku-ui/utils": "workspace:^", @@ -102,7 +103,7 @@ }, "lint-staged": { "*.{[jt]s?(x),vue}": [ - "npx pnpm lint:fix" + "pnpm lint:fix" ] } } diff --git a/packages/components/visually-hidden/README.md b/packages/components/visually-hidden/README.md new file mode 100644 index 000000000..249571778 --- /dev/null +++ b/packages/components/visually-hidden/README.md @@ -0,0 +1,10 @@ +# `@oku-ui/visually-hidden` + +## Installation + +```sh +$ pnpm add @oku-ui/visually-hidden +``` + +## Usage +... \ No newline at end of file diff --git a/packages/components/visually-hidden/build.config.ts b/packages/components/visually-hidden/build.config.ts new file mode 100644 index 000000000..b972b9a78 --- /dev/null +++ b/packages/components/visually-hidden/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/visually-hidden/package.json b/packages/components/visually-hidden/package.json new file mode 100644 index 000000000..7487e02b3 --- /dev/null +++ b/packages/components/visually-hidden/package.json @@ -0,0 +1,48 @@ +{ + "name": "@oku-ui/visually-hidden", + "type": "module", + "version": "0.0.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/visually-hidden" + }, + "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" + ], + "engines": { + "node": ">=18" + }, + "scripts": { + "build": "tsup", + "dev": "tsup --watch" + }, + "peerDependencies": { + "vue": "^3.3.0" + }, + "dependencies": { + "@oku-ui/primitive": "latest", + "@oku-ui/use-composable": "latest" + }, + "devDependencies": { + "tsconfig": "workspace:^" + }, + "publishConfig": { + "access": "public" + } +} diff --git a/packages/components/visually-hidden/src/VisuallyHidden.ts b/packages/components/visually-hidden/src/VisuallyHidden.ts new file mode 100644 index 000000000..f6d3b3ac6 --- /dev/null +++ b/packages/components/visually-hidden/src/VisuallyHidden.ts @@ -0,0 +1,68 @@ +import { Primitive } from '@oku-ui/primitive' +import type { + ElementType, + MergeProps, + PrimitiveProps, +} from '@oku-ui/primitive' +import { useRef } from '@oku-ui/use-composable' +import type { CSSProperties } from 'vue' +import { defineComponent, h } from 'vue' + +const NAME = 'OkuVisuallyHidden' + +type VisuallyHiddenElement = ElementType<'button'> + +interface VisuallyHiddenProps extends PrimitiveProps {} + +const VisuallyHidden = defineComponent({ + name: NAME, + inheritAttrs: false, + props: { + asChild: { + type: Boolean, + default: undefined, + }, + }, + setup(props, { attrs, expose }) { + const { ...visuallyHiddenAttrs } = attrs as VisuallyHiddenElement + + const { $el, newRef } = useRef() + + expose({ + innerRef: $el, + }) + + const originalReturn = () => + h(Primitive.span, { + ref: newRef, + asChild: props.asChild, + ...visuallyHiddenAttrs, + style: { + position: 'absolute', + border: 0, + width: 1, + height: 1, + padding: 0, + margin: -1, + overflow: 'hidden', + clip: 'rect(0, 0, 0, 0)', + whiteSpace: 'nowrap', + wordWrap: 'normal', + ...(visuallyHiddenAttrs.style as CSSProperties), + }, + }) + + return originalReturn as unknown as { + innerRef: VisuallyHiddenElement + } + }, +}) + +type _VisuallyHidden = MergeProps + +const OkuVisuallyHidden = VisuallyHidden as typeof VisuallyHidden & +(new () => { $props: _VisuallyHidden }) + +export { OkuVisuallyHidden } + +export type { VisuallyHiddenProps } diff --git a/packages/components/visually-hidden/src/index.ts b/packages/components/visually-hidden/src/index.ts new file mode 100644 index 000000000..06f81174b --- /dev/null +++ b/packages/components/visually-hidden/src/index.ts @@ -0,0 +1 @@ +export * from './VisuallyHidden' diff --git a/packages/components/visually-hidden/src/stories/VisuallyHidden.stories.ts b/packages/components/visually-hidden/src/stories/VisuallyHidden.stories.ts new file mode 100644 index 000000000..d1e1b8cb1 --- /dev/null +++ b/packages/components/visually-hidden/src/stories/VisuallyHidden.stories.ts @@ -0,0 +1,43 @@ +import type { Meta, StoryObj } from '@storybook/vue3' +import type { IVisuallyHiddenProps } from './VisuallyHiddenDemo.vue' +import OkuVisuallyHiddenComponent from './VisuallyHiddenDemo.vue' + +interface StoryProps extends IVisuallyHiddenProps {} + +const meta = { + title: 'Utilities/VisuallyHidden', + component: OkuVisuallyHiddenComponent, + args: { + template: '#1', + }, + argTypes: { + template: { + control: 'text', + }, + }, + tags: ['autodocs'], +} satisfies Meta & { + args: StoryProps +} + +export default meta +type Story = StoryObj & { + args: StoryProps +} + +export const Styled: Story = { + args: { + template: '#1', + // allShow: true, + }, + + render: (args: any) => ({ + components: { OkuVisuallyHiddenComponent }, + setup() { + return { args } + }, + template: ` + + `, + }), +} diff --git a/packages/components/visually-hidden/src/stories/VisuallyHiddenDemo.vue b/packages/components/visually-hidden/src/stories/VisuallyHiddenDemo.vue new file mode 100644 index 000000000..6c18e1aa2 --- /dev/null +++ b/packages/components/visually-hidden/src/stories/VisuallyHiddenDemo.vue @@ -0,0 +1,18 @@ + + + diff --git a/packages/components/visually-hidden/src/visually-hidden.test.ts b/packages/components/visually-hidden/src/visually-hidden.test.ts new file mode 100644 index 000000000..8db27ed5c --- /dev/null +++ b/packages/components/visually-hidden/src/visually-hidden.test.ts @@ -0,0 +1,51 @@ +import { mount } from '@vue/test-utils' +import { describe, expect, it } from 'vitest' +import { OkuVisuallyHidden } from '.' + +describe('OkuVisuallyHidden', () => { + it('renders correctly with default props', () => { + const wrapper = mount(OkuVisuallyHidden) + expect(wrapper.exists()).toBe(true) + + expect(wrapper.element.tagName.toLowerCase()).toBe('span') + + const style = wrapper.element.getAttribute('style') + expect(style).toBe( + 'position: absolute; border: 0px; padding: 0px; overflow: hidden; clip: rect(0px, 0px, 0px, 0px); white-space: nowrap; word-wrap: normal;', + ) + + // The component should have a ref named 'innerRef' + expect(wrapper.vm.innerRef).toBeDefined() + // @ts-expect-error "working" + expect(wrapper.vm.innerRef.tagName.toLowerCase()).toBe('span') + }) + + it('applies ref correctly', () => { + const wrapper = mount(OkuVisuallyHidden) + expect(wrapper.vm.innerRef).toBeDefined() + // @ts-expect-error "working" + expect(wrapper.vm.innerRef.tagName.toLowerCase()).toBe('span') + }) + + it('renders correctly with custom style', () => { + const style = { + background: 'red', + color: 'white', + } + + const wrapper = mount(OkuVisuallyHidden, { + attrs: { + style, + }, + }) + + const inlineStyle = wrapper.element.getAttribute('style') + expect(inlineStyle).toContain('position: absolute;') + expect(inlineStyle).toContain('border: 0px;') + expect(inlineStyle).toContain('overflow: hidden;') + expect(inlineStyle).toContain('white-space: nowrap;') + expect(inlineStyle).toContain('word-wrap: normal;') + expect(inlineStyle).toContain('background: red;') + expect(inlineStyle).toContain('color: white;') + }) +}) diff --git a/packages/components/visually-hidden/tsconfig.json b/packages/components/visually-hidden/tsconfig.json new file mode 100644 index 000000000..b8dfa9041 --- /dev/null +++ b/packages/components/visually-hidden/tsconfig.json @@ -0,0 +1,10 @@ +{ + "extends": "tsconfig/node16.json", + "compilerOptions": { + "rootDir": "src", + "outDir": "dist" + }, + "include": [ + "src" + ] +} diff --git a/packages/components/visually-hidden/tsup.config.ts b/packages/components/visually-hidden/tsup.config.ts new file mode 100644 index 000000000..a2f7a0d8b --- /dev/null +++ b/packages/components/visually-hidden/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/playground/nuxt3/package.json b/playground/nuxt3/package.json index a0000020c..8c545704b 100644 --- a/playground/nuxt3/package.json +++ b/playground/nuxt3/package.json @@ -18,7 +18,8 @@ "@oku-ui/label": "workspace:^", "@oku-ui/progress": "workspace:^", "@oku-ui/separator": "workspace:^", - "@oku-ui/switch": "workspace:^" + "@oku-ui/switch": "workspace:^", + "@oku-ui/visually-hidden": "workspace:^" }, "devDependencies": { "@nuxtjs/tailwindcss": "^6.8.0", diff --git a/playground/nuxt3/pages/visually-hidden.vue b/playground/nuxt3/pages/visually-hidden.vue new file mode 100644 index 000000000..4a1ed4497 --- /dev/null +++ b/playground/nuxt3/pages/visually-hidden.vue @@ -0,0 +1,5 @@ + + + diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index b126c98b4..042bc8009 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -67,6 +67,9 @@ importers: '@oku-ui/utils': specifier: workspace:^ version: link:packages/core/utils + '@oku-ui/visually-hidden': + specifier: workspace:^ + version: link:packages/components/visually-hidden '@storybook/addon-essentials': specifier: ^7.0.27 version: 7.0.27(react-dom@18.2.0)(react@18.2.0) @@ -168,7 +171,7 @@ importers: version: 1.2.1 vite: specifier: 4.3.5 - version: 4.3.5 + version: 4.3.5(@types/node@18.16.19) vite-plugin-dts: specifier: ^3.3.0 version: 3.3.0(rollup@3.21.0)(typescript@5.1.6)(vite@4.3.5) @@ -428,6 +431,22 @@ importers: specifier: workspace:^ version: link:../../tsconfig + packages/components/visually-hidden: + dependencies: + '@oku-ui/primitive': + specifier: latest + version: link:../../core/primitive + '@oku-ui/use-composable': + specifier: latest + version: link:../../core/use-composable + vue: + specifier: ^3.3.0 + version: 3.3.4 + devDependencies: + tsconfig: + specifier: workspace:^ + version: link:../../tsconfig + packages/core/primitive: dependencies: vue: @@ -517,6 +536,9 @@ importers: '@oku-ui/switch': specifier: workspace:^ version: link:../../packages/components/switch + '@oku-ui/visually-hidden': + specifier: workspace:^ + version: link:../../packages/components/visually-hidden devDependencies: '@nuxtjs/tailwindcss': specifier: ^6.8.0 @@ -572,7 +594,7 @@ importers: version: 0.25.1(rollup@3.21.0)(vue@3.3.4) vite: specifier: 4.3.5 - version: 4.3.5 + version: 4.3.5(@types/node@18.16.19) vue-tsc: specifier: ^1.8.5 version: 1.8.5(typescript@5.1.6) @@ -13931,7 +13953,7 @@ packages: dependencies: '@types/node': 18.16.18 esbuild: 0.17.19 - postcss: 8.4.26 + postcss: 8.4.25 rollup: 3.22.0 optionalDependencies: fsevents: 2.3.2 @@ -13964,7 +13986,7 @@ packages: dependencies: '@types/node': 18.16.19 esbuild: 0.17.19 - postcss: 8.4.26 + postcss: 8.4.25 rollup: 3.22.0 optionalDependencies: fsevents: 2.3.2 diff --git a/tailwind.config.js b/tailwind.config.js index 875f125b8..64ce20013 100644 --- a/tailwind.config.js +++ b/tailwind.config.js @@ -19,7 +19,4 @@ export default { collections: getIconCollections(['ph']), }), ], - corePlugins: { - preflight: false, - }, }