Skip to content

Commit

Permalink
fix: list rendering and default labels
Browse files Browse the repository at this point in the history
  • Loading branch information
CyanSalt committed Mar 31, 2024
1 parent 2f35d78 commit cbd14f1
Show file tree
Hide file tree
Showing 14 changed files with 73 additions and 50 deletions.
10 changes: 7 additions & 3 deletions docs/components/checkbox.md
Original file line number Diff line number Diff line change
Expand Up @@ -259,7 +259,7 @@ const treeValue = ref<string | undefined>()
</template>
<template #body:label:default>

`startCase(value)` if `value` is `string`
`startCase(keyOf(value))` if value exists.

</template>
<template #body:label:description>
Expand Down Expand Up @@ -306,7 +306,11 @@ const treeValue = ref<string | undefined>()

</template>
<template #body:value:description>
Item value when checked in the CheckboxGroup.

Item key or data when checked in the CheckboxGroup.

See [List Rendering](/guide/specs#list-rendering).

</template>

<template #body:...:description>
Expand Down Expand Up @@ -474,7 +478,7 @@ const treeValue = ref<string | undefined>()
<RText type="error">Required</RText>
</template>
<template #body:model-value:description>
Value(s) of the checked item(s) of the group.
Key(s) or data of the checked item(s) of the group.
</template>

<template #body:multiple:type>
Expand Down
10 changes: 5 additions & 5 deletions docs/components/select.md
Original file line number Diff line number Diff line change
Expand Up @@ -78,8 +78,8 @@ import { RCheckbox, RSpace, RSelect } from 'roughness'
<template>
<RSpace>
<RSelect :model-value="['basic']" multiple disabled>
<RCheckbox value="basic" label="Basic User Info" />
<RSelect :model-value="[1]" multiple disabled>
<RCheckbox :value="1" label="Basic User Info" />
</RSelect>
<RSelect loading placeholder="Arsenal" />
</RSpace>
Expand All @@ -89,8 +89,8 @@ import { RCheckbox, RSpace, RSelect } from 'roughness'
</RDetails>

<RSpace>
<RSelect :model-value="['basic']" multiple disabled>
<RCheckbox value="basic" label="Basic User Info" />
<RSelect :model-value="[1]" multiple disabled>
<RCheckbox :value="1" label="Basic User Info" />
</RSelect>
<RSelect loading placeholder="Arsenal" />
</RSpace>
Expand Down Expand Up @@ -189,7 +189,7 @@ See [Checkbox Tree](/components/checkbox#tree).
<RText type="error">Required</RText>
</template>
<template #body:model-value:description>
Value(s) of the selected item(s).
Key(s) or data of the selected item(s).
</template>

<template #body:multiple:type>
Expand Down
10 changes: 8 additions & 2 deletions docs/guide/specs.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<script lang="ts" setup>
import { keyOf, RDetails, RKey, RTabItem, RTabs } from 'roughness'
import { keyOf, RCard, RDetails, RKey, RTabItem, RTabs } from 'roughness'

const data = [{ [RKey]: 'darwin', name: 'macOS' }, { [RKey]: 'win32', name: 'Windows' }]
</script>
Expand All @@ -10,7 +10,7 @@ Most components of Roughness follow similar specifications. Understanding these

## List Rendering

For list item components (including `RTableColumn` and `RTabItem`), the value of the unique key prop for each rendered item can be in any of the following forms:
For list item components (including `RCheckbox`, `RTabItem` and `RTableColumn`), the value of the unique key prop for each rendered item can be in any of the following forms:

- `string | number`, which is the key of the rendered item. This means that keeping keys consistent can lead to better performance in list sorting, deletion, etc.
- Recommended to use all lowercase letters and hyphens and underscores as keys.
Expand Down Expand Up @@ -68,6 +68,12 @@ For list item components (including `RTableColumn` and `RTabItem`), the value of
</RTabItem>
</RTabs>
<RCard type="warning">
In order to cope with various situations, the order of list rendering is based on the mounting order of list items. Therefore, if you need to display list items according to conditions, it is recommended to use `v-show` instead of `v-if`.
</RCard>
## Controlled and Uncontrolled Components
In Roughness, all components that support `v-model` also support not binding the properties and events, which means:
Expand Down
6 changes: 3 additions & 3 deletions src/checkbox/checkbox-group.vue
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
<script lang="ts" setup>
import '../common/style.scss'
import { provide } from 'vue'
import type { RValueOrKey } from '../common/key'
import RSpace from '../space/index.vue'
import type { CheckboxValue } from './utils'
import { disabledInjection, modelInjection, multipleInjection } from './utils'
defineOptions({
Expand All @@ -16,8 +16,8 @@ const {
} = defineProps<{
/** Whether to disable all checking items */
disabled?: boolean,
/** Value(s) of the checked item(s) of the group */
modelValue: CheckboxValue[] | CheckboxValue | undefined,
/** Key(s) or data of the checked item(s) of the group */
modelValue: RValueOrKey[] | RValueOrKey | undefined,
/** Whether to support checking multiple items */
multiple?: boolean,
}>()
Expand Down
19 changes: 12 additions & 7 deletions src/checkbox/index.vue
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,15 @@ import { startCase } from 'lodash-es'
import type { Options } from 'roughjs/bin/core'
import type { RoughSVG } from 'roughjs/bin/svg'
import { inject, ref, watchEffect } from 'vue'
import type { RValueOrKey } from '../common/key'
import { keyOf } from '../common/key'
import { getLengthProperty } from '../common/property'
import { useReactionState } from '../common/reaction'
import { effectRef } from '../common/utils'
import RGraphics from '../graphics/index.vue'
import type { GraphicsProps } from '../graphics/utils'
import { getFilledSizeOptions, getSVGSize } from '../graphics/utils'
import RSpace from '../space/index.vue'
import type { CheckboxValue } from './utils'
import { disabledInjection, labelsInjection, modelInjection, multipleInjection } from './utils'
defineOptions({
Expand All @@ -37,8 +38,11 @@ const {
indeterminate?: boolean,
/** Item label when checked and displayed */
label?: string,
/** Item value when checked in the CheckboxGroup */
value?: CheckboxValue,
/**
* Item key or data when checked in the CheckboxGroup
* {@link https://roughness.vercel.app/guide/specs#list-rendering}
*/
value?: RValueOrKey,
} & GraphicsProps>()
const emit = defineEmits<{
Expand All @@ -52,17 +56,18 @@ defineSlots<{
const multiple = $(inject(multipleInjection, ref()))
let model = $(inject(modelInjection, ref()))
const disabledByGroup = $(inject(disabledInjection, ref()))
const labels = inject(labelsInjection, new Map<CheckboxValue, string>())
const labels = inject(labelsInjection, new Map<RValueOrKey, string>())
const label = $computed(() => {
return userLabel ?? (typeof value === 'string' ? startCase(value) : undefined)
return userLabel ?? (typeof value === 'undefined' ? value : startCase(keyOf(value)))
})
watchEffect(onInvalidate => {
if (value !== undefined && label !== undefined) {
labels.set(value, label)
const key = keyOf(value)
labels.set(key, label)
onInvalidate(() => {
labels.delete(value)
labels.delete(key)
})
}
})
Expand Down
7 changes: 3 additions & 4 deletions src/checkbox/utils.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
import type { InjectionKey, Ref } from 'vue'

export type CheckboxValue = string | number
import type { RValueOrKey } from '../common/key'

export const multipleInjection: InjectionKey<Ref<boolean>> = Symbol('RCheckboxGroup#multiple')
export const modelInjection: InjectionKey<Ref<CheckboxValue[] | CheckboxValue | undefined>> = Symbol('RCheckboxGroup#model')
export const modelInjection: InjectionKey<Ref<RValueOrKey[] | RValueOrKey | undefined>> = Symbol('RCheckboxGroup#model')
export const disabledInjection: InjectionKey<Ref<boolean>> = Symbol('RCheckboxGroup#disabled')
export const labelsInjection: InjectionKey<Map<CheckboxValue, string>> = Symbol('RCheckbox#labels')
export const labelsInjection: InjectionKey<Map<string, string>> = Symbol('RCheckbox#labels')
14 changes: 10 additions & 4 deletions src/common/list.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,14 @@
import type { InjectionKey, MaybeRefOrGetter, Ref } from 'vue'
import { inject, ref, toValue, watchEffect } from 'vue'
import type { InjectionKey, MaybeRefOrGetter } from 'vue'
import { inject, provide, reactive, toValue, watchEffect } from 'vue'

export function useListItem<T>(injection: InjectionKey<Ref<T[]>>, source: MaybeRefOrGetter<T>) {
const list = $(inject(injection, ref()))
export function useList<T>(injection: InjectionKey<T[]>) {
const list = reactive<T[]>([])
provide(injection, list as T[])
return list
}

export function useListItem<T>(injection: InjectionKey<T[]>, source: MaybeRefOrGetter<T>) {
const list = inject(injection, undefined)
watchEffect(onInvalidate => {
if (!list) return
const item = toValue(source)
Expand Down
2 changes: 1 addition & 1 deletion src/common/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ export interface SizeProps {
}

export function sentenceCase(text: string) {
return startCase(text).toLowerCase().replace(/\w/, matched => matched.toUpperCase())
return startCase(text).toLowerCase().replace(/^\w/, matched => matched.toUpperCase())
}

export function effectRef<T>(getterOrOptions: ComputedGetter<T> | WritableComputedOptions<T>) {
Expand Down
18 changes: 12 additions & 6 deletions src/select/index.vue
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,9 @@ import type { RoughSVG } from 'roughjs/bin/svg'
import type { SelectHTMLAttributes } from 'vue'
import { inject, provide, reactive, ref } from 'vue'
import RCheckboxGroup from '../checkbox/checkbox-group.vue'
import type { CheckboxValue } from '../checkbox/utils'
import { labelsInjection } from '../checkbox/utils'
import type { RValueOrKey } from '../common/key'
import { keyOf } from '../common/key'
import { getLengthProperty, getLengthPropertyAsArray } from '../common/property'
import { useReactionState } from '../common/reaction'
import { effectRef, sentenceCase } from '../common/utils'
Expand Down Expand Up @@ -40,8 +41,8 @@ const {
* It will be non-interactive in loading state
*/
loading?: boolean,
/** Value(s) of the selected item(s) */
modelValue?: CheckboxValue[] | CheckboxValue | undefined,
/** Key(s) or data of the selected item(s) */
modelValue?: RValueOrKey[] | RValueOrKey | undefined,
/** Whether to support selecting multiple items */
multiple?: boolean,
name?: SelectHTMLAttributes['name'],
Expand Down Expand Up @@ -81,12 +82,17 @@ let internalModelValue = $(effectRef({
},
}))
const labels = reactive(new Map<CheckboxValue, string>())
const labels = reactive(new Map<string, string>())
function labelOf(value: RValueOrKey) {
const key = keyOf(value)
return labels.get(key) ?? key
}
const displayText = $computed(() => {
const text = Array.isArray(internalModelValue)
? internalModelValue.map(value => labels.get(value) ?? value)
: (internalModelValue !== undefined ? labels.get(internalModelValue) ?? internalModelValue : internalModelValue)
? internalModelValue.map(value => labelOf(value))
: (internalModelValue !== undefined ? labelOf(internalModelValue) : internalModelValue)
return Array.isArray(text) ? text.join(', ') : text
})
Expand Down
8 changes: 3 additions & 5 deletions src/table/index.vue
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,16 @@ import { useResizeObserver } from '@vueuse/core'
import type { Options } from 'roughjs/bin/core'
import type { RoughSVG } from 'roughjs/bin/svg'
import type { Ref } from 'vue'
import { onMounted, provide, reactive } from 'vue'
import { onMounted, reactive } from 'vue'
import type { RValueOrKey } from '../common/key'
import { keyOf } from '../common/key'
import { useList } from '../common/list'
import { useReactionState } from '../common/reaction'
import RGraphics from '../graphics/index.vue'
import type { GraphicsProps } from '../graphics/utils'
import { getSVGSize } from '../graphics/utils'
import RTableCell from './table-cell.vue'
import RTableHeaderCell from './table-header-cell.vue'
import type { TableColumnData } from './utils'
import { columnsInjection } from './utils'
defineOptions({
Expand Down Expand Up @@ -48,9 +48,7 @@ defineSlots<{
default?: (props: {}) => any,
}>()
const columns = $ref<TableColumnData[]>([])
provide(columnsInjection, $$(columns))
const columns = useList(columnsInjection)
let head = $ref<HTMLTableSectionElement>()
let body = $ref<HTMLTableSectionElement>()
Expand Down
4 changes: 2 additions & 2 deletions src/table/utils.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import type { InjectionKey, Ref } from 'vue'
import type { InjectionKey } from 'vue'
import type { ComponentSlots } from '../common/renderable'
import type RTableColumn from './table-column.vue'

Expand All @@ -7,4 +7,4 @@ export interface TableColumnData {
slots: ComponentSlots<typeof RTableColumn>,
}

export const columnsInjection: InjectionKey<Ref<TableColumnData[]>> = Symbol('RTable#columns')
export const columnsInjection: InjectionKey<TableColumnData[]> = Symbol('RTable#columns')
8 changes: 3 additions & 5 deletions src/tabs/index.vue
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
<script lang="ts" setup>
import '../common/style.scss'
import { provide, reactive, watchEffect } from 'vue'
import { reactive, watchEffect } from 'vue'
import type { RValueOrKey } from '../common/key'
import { keyOf } from '../common/key'
import { useList } from '../common/list'
import { effectRef } from '../common/utils'
import type { GraphicsProps } from '../graphics/utils'
import RSpace from '../space/index.vue'
import RTabAnchor from './tab-anchor.vue'
import type { TabItemData } from './utils'
import { itemsInjection } from './utils'
defineOptions({
Expand Down Expand Up @@ -43,9 +43,7 @@ defineSlots<{
default?: (props: {}) => any,
}>()
const items = $ref<TabItemData[]>([])
provide(itemsInjection, $$(items))
const items = useList(itemsInjection)
let internalModelValue = $(effectRef({
get: () => modelValue,
Expand Down
3 changes: 2 additions & 1 deletion src/tabs/tab-anchor.vue
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import '../common/style.scss'
import { startCase } from 'lodash-es'
import type { RoughSVG } from 'roughjs/bin/svg'
import type { RValueOrKey } from '../common/key'
import { keyOf } from '../common/key'
import { getLengthProperty, getLengthPropertyAsArray } from '../common/property'
import { useReactionState } from '../common/reaction'
import RGraphics from '../graphics/index.vue'
Expand Down Expand Up @@ -46,7 +47,7 @@ defineSlots<{
}>()
const content = $computed(() => {
return startCase(String(value))
return startCase(keyOf(value))
})
function activate() {
Expand Down
4 changes: 2 additions & 2 deletions src/tabs/utils.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import type { InjectionKey, Ref } from 'vue'
import type { InjectionKey } from 'vue'
import type { RValueOrKey } from '../common/key'
import type { ComponentSlots } from '../common/renderable'
import type RTabItem from './tab-item.vue'
Expand All @@ -8,4 +8,4 @@ export interface TabItemData {
slots: ComponentSlots<typeof RTabItem>,
}

export const itemsInjection: InjectionKey<Ref<TabItemData[]>> = Symbol('RTabs#items')
export const itemsInjection: InjectionKey<TabItemData[]> = Symbol('RTabs#items')

0 comments on commit cbd14f1

Please sign in to comment.