-
-
Notifications
You must be signed in to change notification settings - Fork 35
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(collection): new component collection (#262)
* feat: collection * fix: lint * refactor: collection * fix: dom * fix: package version * fix: slot
- Loading branch information
1 parent
472a50c
commit 90c6115
Showing
18 changed files
with
433 additions
and
9 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
# `collection` | ||
|
||
<span><a href="https://www.npmjs.com/package/@oku-ui/collection "><img src="https://img.shields.io/npm/v/@oku-ui/collection?style=flat&colorA=18181B&colorB=28CF8D" alt="Version"></a> </span> | <span> <a href="https://www.npmjs.com/package/@oku-ui/collection"> <img src="https://img.shields.io/npm/dm/@oku-ui/collection?style=flat&colorA=18181B&colorB=28CF8D" alt="Downloads"> </a> </span> | <span> <a href="https://oku-ui.com/primitives/components/collection"><img src="https://img.shields.io/badge/Open%20Documentation-18181B" alt="Website"></a> </span> | ||
|
||
## Installation | ||
|
||
```sh | ||
$ pnpm add @oku-ui/collection | ||
``` | ||
|
||
## Usage | ||
|
||
soon docs |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
import { defineBuildConfig } from 'unbuild' | ||
|
||
export default defineBuildConfig({ | ||
entries: [ | ||
{ | ||
builder: 'mkdist', | ||
input: './src/', | ||
pattern: ['**/!(*.test|*.stories).ts'], | ||
}, | ||
], | ||
declaration: true, | ||
}) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,51 @@ | ||
{ | ||
"name": "@oku-ui/collection", | ||
"type": "module", | ||
"version": "0.2.3", | ||
"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/collection" | ||
}, | ||
"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/provide": "latest", | ||
"@oku-ui/slot": "latest", | ||
"@oku-ui/use-composable": "latest", | ||
"@oku-ui/utils": "latest" | ||
}, | ||
"devDependencies": { | ||
"tsconfig": "workspace:^" | ||
}, | ||
"publishConfig": { | ||
"access": "public" | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,142 @@ | ||
import type { FunctionalComponent, Ref, ReservedProps } from 'vue' | ||
import { computed, defineComponent, h, ref, watchEffect } from 'vue' | ||
import { useComposedRefs, useForwardRef } from '@oku-ui/use-composable' | ||
import { createProvideScope } from '@oku-ui/provide' | ||
import { OkuSlot } from '@oku-ui/slot' | ||
|
||
const CollectionProps = { | ||
scope: { type: null as any, required: true }, | ||
} | ||
|
||
type CollectionElement = HTMLElement | ||
|
||
// We have resorted to returning slots directly rather than exposing primitives that can then | ||
// be slotted like `<CollectionItem as={Slot}>…</CollectionItem>`. | ||
// This is because we encountered issues with generic types that cannot be statically analysed | ||
// due to creating them dynamically via createCollection. | ||
|
||
function createCollection<ItemElement extends HTMLElement, ItemData>(name: string) { | ||
// const ItemData = { | ||
// scope: { type: Object, required: true }, | ||
// } | ||
/* ----------------------------------------------------------------------------------------------- | ||
* CollectionProvider | ||
* --------------------------------------------------------------------------------------------- */ | ||
|
||
const PROVIDER_NAME = `${name}CollectionProvider` | ||
const [createCollectionProvide, createCollectionScope] = createProvideScope(PROVIDER_NAME) | ||
|
||
type ContextValue = { | ||
collectionRef: Ref<CollectionElement | undefined> | ||
itemMap: Map<Ref<ItemElement | null | undefined>, { ref: Ref<ItemElement> } & ItemData> | ||
} | ||
|
||
const [CollectionProviderImpl, useCollectionInject] = createCollectionProvide<ContextValue>( | ||
PROVIDER_NAME, | ||
{ collectionRef: ref(undefined), itemMap: new Map() }, | ||
) | ||
|
||
const CollectionProvider = defineComponent({ | ||
name: PROVIDER_NAME, | ||
inheritAttrs: false, | ||
props: { | ||
...CollectionProps, | ||
}, | ||
setup(props, { slots }) { | ||
const collectionRef = ref<CollectionElement>() | ||
const itemMap = new Map<Ref<ItemElement>, { ref: Ref<ItemElement> } & ItemData>() | ||
CollectionProviderImpl({ | ||
collectionRef, | ||
itemMap, | ||
scope: props.scope, | ||
}) | ||
|
||
return () => slots.default?.() | ||
}, | ||
}) | ||
/* ----------------------------------------------------------------------------------------------- | ||
* CollectionSlot | ||
* --------------------------------------------------------------------------------------------- */ | ||
|
||
const COLLECTION_SLOT_NAME = `${name}CollectionSlot` | ||
|
||
const CollectionSlot = defineComponent({ | ||
name: COLLECTION_SLOT_NAME, | ||
inheritAttrs: false, | ||
props: { | ||
...CollectionProps, | ||
}, | ||
setup(props, { slots }) { | ||
const inject = useCollectionInject(COLLECTION_SLOT_NAME, props.scope) | ||
const forwaredRef = useForwardRef() | ||
const composedRefs = useComposedRefs(forwaredRef, inject.value.collectionRef) | ||
return () => h(OkuSlot, { ref: composedRefs }, { | ||
default: () => slots.default?.(), | ||
}) | ||
}, | ||
}) | ||
|
||
/* ----------------------------------------------------------------------------------------------- | ||
* CollectionItem | ||
* --------------------------------------------------------------------------------------------- */ | ||
|
||
const ITEM_SLOT_NAME = `${name}CollectionItemSlot` | ||
const ITEM_DATA_ATTR = 'data-oku-collection-item' | ||
|
||
type CollectionItemSlotProps = ItemData & { | ||
scope: any | undefined | ||
} & ReservedProps | ||
|
||
const CollectionItemSlot: FunctionalComponent<CollectionItemSlotProps> = (props, context) => { | ||
const { scope, ...itemData } = props | ||
const refValue = ref<ItemElement | null>() | ||
const forwaredRef = useForwardRef() | ||
const composedRefs = useComposedRefs(refValue, forwaredRef) | ||
|
||
const inject = useCollectionInject(ITEM_SLOT_NAME, scope) | ||
|
||
watchEffect((clearMap) => { | ||
inject.value.itemMap.set(refValue, { ref: refValue, ...(itemData as any) }) | ||
clearMap(() => inject.value.itemMap.delete(refValue)) | ||
}) | ||
|
||
return h(OkuSlot, { ref: composedRefs, ...{ [ITEM_DATA_ATTR]: '' } }, { | ||
default: () => context.slots.default?.(), | ||
}) | ||
} | ||
|
||
/* ----------------------------------------------------------------------------------------------- | ||
* useCollection | ||
* --------------------------------------------------------------------------------------------- */ | ||
|
||
function useCollection(scope: any) { | ||
const inject = useCollectionInject(`${name}CollectionConsumer`, scope) | ||
|
||
const getItems = computed(() => { | ||
const collectionNode = inject.value.collectionRef.value | ||
if (!collectionNode) | ||
return [] | ||
|
||
const orderedNodes = Array.from(collectionNode.querySelectorAll(`[${ITEM_DATA_ATTR}]`)) | ||
|
||
const items = Array.from(inject.value.itemMap.values()) | ||
|
||
const orderedItems = items.sort( | ||
(a, b) => orderedNodes.indexOf(a.ref.value!) - orderedNodes.indexOf(b.ref.value!), | ||
) | ||
return orderedItems | ||
}) | ||
|
||
return getItems | ||
} | ||
|
||
return { | ||
CollectionProvider, | ||
CollectionSlot, | ||
CollectionItemSlot, | ||
useCollection, | ||
createCollectionScope, | ||
} | ||
} | ||
|
||
export { createCollection, CollectionProps } |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
export { | ||
createCollection, | ||
CollectionProps, | ||
} from './collection' |
59 changes: 59 additions & 0 deletions
59
packages/components/collection/src/stories/CollectionDemo.vue
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,59 @@ | ||
<!-- eslint-disable no-console --> | ||
<script setup lang="ts"> | ||
import { onMounted, ref } from 'vue' | ||
import { createCollection } from '@oku-ui/collection' | ||
export interface OkuCollectionProps { | ||
template: '#1' | '#2' | ||
allshow?: boolean | ||
} | ||
withDefaults(defineProps<OkuCollectionProps>(), { | ||
template: '#1', | ||
}) | ||
type ItemData = { disabled?: boolean } | ||
const { CollectionSlot, CollectionItemSlot, CollectionProvider, useCollection } = createCollection<HTMLLIElement, ItemData >('List') | ||
const labelRef = ref<any>() | ||
onMounted(() => { | ||
console.log(labelRef.value, 'ref') | ||
}) | ||
const alert = () => window.alert('clicked') | ||
function LogsItem() { | ||
const getItems = useCollection(undefined) | ||
console.log(getItems.value[0].ref.value) | ||
} | ||
</script> | ||
|
||
<template> | ||
<div class="cursor-default inline-block"> | ||
<div v-if="template === '#1' || allshow" class="flex flex-col"> | ||
<CollectionProvider :scope="undefined"> | ||
<CollectionSlot :scope="undefined"> | ||
<ul clas="w-52"> | ||
<CollectionItemSlot ref="labelRef" :scope="undefined"> | ||
<li> | ||
Red | ||
</li> | ||
</CollectionItemSlot> | ||
<CollectionItemSlot :scope="undefined" :disabled="true"> | ||
<li class="opacity-50"> | ||
Green | ||
</li> | ||
</CollectionItemSlot> | ||
|
||
<CollectionItemSlot :scope="undefined"> | ||
<li> | ||
Blue | ||
</li> | ||
</CollectionItemSlot> | ||
</ul> | ||
</CollectionSlot> | ||
<LogsItem /> | ||
</CollectionProvider> | ||
</div> | ||
</div> | ||
</template> |
58 changes: 58 additions & 0 deletions
58
packages/components/collection/src/stories/collection.stories.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,58 @@ | ||
import type { Meta, StoryObj } from '@storybook/vue3' | ||
|
||
import type { OkuCollectionProps } from './CollectionDemo.vue' | ||
import OkuCollectionComponent from './CollectionDemo.vue' | ||
|
||
interface StoryProps extends OkuCollectionProps { | ||
} | ||
|
||
const meta = { | ||
title: 'Utilities/Collection', | ||
component: OkuCollectionComponent, | ||
args: { | ||
template: '#1', | ||
}, | ||
argTypes: { | ||
template: { | ||
control: 'text', | ||
}, | ||
}, | ||
tags: ['autodocs'], | ||
} satisfies Meta<typeof OkuCollectionComponent> & { | ||
args: StoryProps | ||
} | ||
|
||
export default meta | ||
type Story = StoryObj<typeof meta> & { | ||
args: StoryProps | ||
} | ||
|
||
export const Styled: Story = { | ||
args: { | ||
template: '#1', | ||
}, | ||
render: (args: any) => ({ | ||
components: { OkuCollectionComponent }, | ||
setup() { | ||
return { args } | ||
}, | ||
template: ` | ||
<OkuCollectionComponent v-bind="args" /> | ||
`, | ||
}), | ||
} | ||
|
||
export const WithControl: Story = { | ||
args: { | ||
template: '#2', | ||
}, | ||
render: (args: any) => ({ | ||
components: { OkuCollectionComponent }, | ||
setup() { | ||
return { args } | ||
}, | ||
template: ` | ||
<OkuCollectionComponent v-bind="args" /> | ||
`, | ||
}), | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
{ | ||
"extends": "tsconfig/node16.json", | ||
"compilerOptions": { | ||
"rootDir": "src", | ||
"outDir": "dist" | ||
}, | ||
"include": [ | ||
"src" | ||
] | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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' }), | ||
}, | ||
] | ||
}) |
Oops, something went wrong.