Skip to content

Commit

Permalink
feat: select
Browse files Browse the repository at this point in the history
  • Loading branch information
jaskang committed Jun 30, 2023
1 parent 5b90c8a commit f802128
Show file tree
Hide file tree
Showing 9 changed files with 173 additions and 115 deletions.
23 changes: 22 additions & 1 deletion docs/components/select.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,27 @@

```vue preview
<template>
<TSelect />
<TSelect
class="max-w-lg"
:options="[
{
value: 'jack',
label: 'Jack',
},
{
value: 'lucy',
label: 'Lucy',
},
{
value: 'disabled',
label: 'Disabled',
disabled: true,
},
{
value: 'yiminghe',
label: 'Yiminghe',
},
]"
/>
</template>
```
39 changes: 4 additions & 35 deletions src/components/Input/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -51,20 +51,20 @@ export const Input = defineComponent({
setup(props, { slots, emit }) {
const { colors } = useTheme()

const [value, setValue] = useControllable(
const [val, setVal] = useControllable(
() => props.value,
val => {
emit('update:value', val)
emit('change', val)
},
''
)
const open = ref(false)

const focused = ref(false)

const onInput = (e: Event) => {
const el = e.currentTarget as HTMLInputElement
setValue(el.value)
setVal(el.value)
emit('input', e)
}
const onFocus = (e: FocusEvent) => {
Expand All @@ -73,7 +73,6 @@ export const Input = defineComponent({
return false
} else {
focused.value = true
open.value = true
emit('focus', e)
}
}
Expand All @@ -83,7 +82,6 @@ export const Input = defineComponent({
return false
} else {
focused.value = false
open.value = false
emit('blur', e)
}
}
Expand All @@ -92,14 +90,6 @@ export const Input = defineComponent({
'--t-input-ring-color': colors.value.primary[500],
}))

const reference = ref(null)
const floating = ref(null)

const { floatingStyles } = useFloating(reference, floating, {
placement: 'bottom-start',
middleware: [offset(0)],
})

return () => (
// <div
// style={cssVars.value}
Expand All @@ -115,7 +105,6 @@ export const Input = defineComponent({
// </span>
// )}
<div
ref={reference}
style={cssVars.value}
class={[
't-input relative inline-flex w-full items-center rounded-md border text-sm shadow-sm',
Expand All @@ -135,7 +124,7 @@ export const Input = defineComponent({
style="box-shadow: none"
type="text"
size="1"
value={value.value}
value={val.value}
readonly={props.readonly}
disabled={props.disabled}
placeholder={props.placeholder}
Expand All @@ -146,26 +135,6 @@ export const Input = defineComponent({
{(slots.suffix || props.suffix) && (
<span class="t-input_suffix flex flex-initial items-center pr-3">{slots.suffix?.() || props.suffix}</span>
)}
{slots.dropdown && open.value && (
<Teleport to="#t-teleports">
<div
ref={floating}
style={floatingStyles.value}
class={[
't-dropdown absolute z-10 mt-1 max-h-60 w-full overflow-auto rounded-md bg-white text-sm shadow-lg',
open.value ? 'is-open' : 'is-closed',
]}
>
<Transition
leave-active-class="transition ease-in duration-100"
leave-from-class="opacity-100"
leave-to-class="opacity-0"
>
{slots.dropdown?.()}
</Transition>
</div>
</Teleport>
)}
</div>
// {slots.after && (
// <span class="t-input_after inline-flex items-center rounded-r-md border border-r-0 border-gray-300 bg-gray-50 px-3">
Expand Down
103 changes: 75 additions & 28 deletions src/components/List/index.tsx
Original file line number Diff line number Diff line change
@@ -1,69 +1,116 @@
import { classed } from '@tw-classed/core'
import {
cloneVNode,
computed,
defineComponent,
type EmitsOptions,
type ExtractPropTypes,
type ExtractPublicPropTypes,
type PropType,
ref,
type SlotsType,
toRef,
type VNode,
} from 'vue'

import { useControllable } from '@/hooks/controllable'
import { useTheme } from '@/theme'
import { type OptionItem, type OptionValue, toMultipleVal } from '@/utils/option'

import { SelectorIcon } from '../Icon'

const props = {
color: {
type: String as PropType<Color>,
value: [String, Number, Array] as PropType<OptionValue | Array<OptionValue>>,
options: {
type: Array as PropType<OptionItem[]>,
default: () => [],
},
loading: Boolean,
disabled: Boolean,
// 多选
multiple: Boolean,
}

export type ListProps = ExtractPropTypes<typeof props>

export type ListPublicProps = ExtractPublicPropTypes<typeof props>

export type ListCssVars = {
'--t-btn-text-color': string
'--t-btn-border-color': string
'--t-btn-bg': string

'--t-btn-text-color-hover': string
'--t-btn-border-color-hover': string
'--t-btn-bg-hover': string

'--t-btn-ring-color': string
'--t-list-accent-color': string
'--t-list-ring-color': string
}

export const List = defineComponent({
name: 'TList',
props,
emits: {
click: (payload: MouseEvent) => true,
'update:value': (val: Required<ListProps>['value']) => true,
change: (val: Required<ListProps>['value']) => true,
},
slots: Object as SlotsType<{
default: () => VNode
icon: () => VNode
}>,
setup(props, { slots, emit }) {
setup(props, { slots, emit, attrs }) {
const { colors } = useTheme()

const { cssVars, cls } = useStyle(() => {
return {
...props,
}
})
const [val, setVal] = useControllable(
() => (props.multiple ? toMultipleVal(props.value) : props.value),
val => {
emit('update:value', val)
emit('change', val)
},
props.multiple ? [] : undefined
)

const cssVars = computed<ListCssVars>(() => ({
'--t-list-accent-color': colors.value.primary[500],
'--t-list-ring-color': colors.value.primary[500],
}))

const hasIcon = computed(() => !!slots.icon || props.loading)
const onClick = (e: MouseEvent) => {
if (!props.disabled) {
emit('click', e)
const itemClickHandler = (item: OptionItem) => {
if (item.disabled) return
if (props.multiple) {
const set = new Set((val.value as any[]) || [])
if (set.has(item.value)) {
set.delete(item.value)
} else {
set.add(item.value)
}
setVal(Array.from(set))
} else {
setVal(item.value)
}
}
const isActived = (item: OptionItem) => {
if (props.multiple) {
return (val.value as any[]).includes(item.value)
}
return item.value === val.value
}
return () => (
<div style={cssVars.value} class={cls.value}>
{slots.default?.()}
</div>
<ul
style={cssVars.value}
class="w-full overflow-auto rounded-md bg-white py-1 text-sm shadow-md ring-1 ring-black ring-opacity-5 focus:outline-none "
tabindex="-1"
>
{props.options.length > 0 ? (
props.options.map(item => (
<li
class={[
'relative flex cursor-default select-none px-3 py-2 transition-colors ease-in-out',
isActived(item)
? 'bg-[--t-list-accent-color] font-semibold text-white'
: item.disabled
? 'font-normal text-gray-400'
: 'font-normal text-gray-700 hover:bg-gray-100',
]}
onClick={() => itemClickHandler(item)}
>
<span class="block truncate font-normal"> {item.label} </span>
</li>
))
) : (
<li>没有数据</li>
)}
</ul>
)
},
})
1 change: 1 addition & 0 deletions src/components/Popper/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ const popperInjectKey: InjectionKey<{

export const Popper = defineComponent({
name: 'TPopper',
inheritAttrs: false,
props,
emits: ['update:open', 'change'],
directives: {},
Expand Down
4 changes: 2 additions & 2 deletions src/components/Radio/Radio.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ export const Radio = defineComponent({
name: groupContext?.props.value.name || props.name,
}
})
const [realChecked, setChecked] = useControllable(
const [checked, setChecked] = useControllable(
() => {
return groupContext ? groupContext.props.value.value === props.value : props.checked
},
Expand Down Expand Up @@ -104,7 +104,7 @@ export const Radio = defineComponent({
name={groupProps.value.name}
value={groupProps.value.value}
disabled={groupProps.value.disabled}
checked={realChecked.value}
checked={checked.value}
onInput={onInput}
onFocus={onFocus}
onBlur={onBlur}
Expand Down
14 changes: 7 additions & 7 deletions src/components/Radio/RadioGroup.tsx
Original file line number Diff line number Diff line change
@@ -1,19 +1,19 @@
import {
ref,
computed,
type ComputedRef,
defineComponent,
type ExtractPropTypes,
type ExtractPublicPropTypes,
type PropType,
provide,
ref,
type SlotsType,
type VNode,
type ComputedRef,
provide,
} from 'vue'

import { useControllable } from '@/hooks/controllable'
import { useTheme } from '@/theme'
import { PropTypes } from '@/utils'
import { useControllable } from '@/hooks/controllable'

const props = {
value: PropTypes.any(),
Expand Down Expand Up @@ -42,7 +42,7 @@ export const RadioGroup = defineComponent({
change: (val: any) => true,
},
setup(props, { slots, emit }) {
const [value, setValue] = useControllable(
const [val, setVal] = useControllable(
() => props.value,
val => {
emit('update:value', val)
Expand All @@ -54,13 +54,13 @@ export const RadioGroup = defineComponent({
provide<RadioGroupContext>('RadioGroupContext', {
props: computed(() => {
return {
value: value.value,
value: val.value,
name: props.name,
disabled: props.disabled,
}
}),
onRadioChange: (val: any) => {
setValue(val)
setVal(val)
},
})
return () => slots.default?.()
Expand Down
Loading

1 comment on commit f802128

@vercel
Copy link

@vercel vercel bot commented on f802128 Jun 30, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please sign in to comment.