提交 13fd8bd5 编写于 作者: d-u-a's avatar d-u-a

feat(nvue): ad ad-draw checkbox checkbox-group form icon slider radio...

feat(nvue): ad ad-draw checkbox checkbox-group form icon slider radio radio-group swiper swiper-item switch
上级 0bd72540
const _adDataCache: Record<string, any> = {}
export function getAdData(
adpid: string | number,
width: any,
height: any,
onsuccess: Function,
onerror: Function
) {
const key = adpid + '-' + width
const adDataList = _adDataCache[key]
if (adDataList && adDataList.length > 0) {
onsuccess(adDataList.splice(0, 1)[0])
return
}
// TODO 缺少语法提示 getDrawAds
plus.ad.getAds(
{
adpid: String(adpid),
count: 3,
width,
},
(res: any) => {
const list = res.ads
onsuccess(list.splice(0, 1)[0])
_adDataCache[key] = adDataList ? adDataList.concat(list) : list
},
(err: any) => {
onerror({
errCode: err.code,
errMsg: err.message,
})
}
)
}
export const adDrawProps = {
adpid: {
type: [Number, String],
default: '',
},
data: {
type: String,
default: '',
},
width: {
type: String,
default: '',
},
}
const _adDataCache: Record<string, any> = {}
export function getAdData(data: any, onsuccess: Function, onerror: Function) {
const { adpid, width } = data
const key = adpid + '-' + width
const adDataList = _adDataCache[key]
if (adDataList && adDataList.length > 0) {
onsuccess(adDataList.splice(0, 1)[0])
return
}
plus.ad.getAds(
data,
(res) => {
const list = res.ads
onsuccess(list.splice(0, 1)[0])
_adDataCache[key] = adDataList ? adDataList.concat(list) : list
},
(err) => {
onerror({
errCode: err.code,
errMsg: err.message,
})
}
)
}
export const adProps = {
adpid: {
type: [Number, String],
default: '',
},
data: {
type: String,
default: '',
},
width: {
type: String,
default: '',
},
channel: {
type: String,
default: '',
},
}
import { PolySymbol } from '@dcloudio/uni-core'
export const uniCheckGroupKey = PolySymbol(__DEV__ ? 'uniCheckGroup' : 'ucg')
export const checkboxProps = {
checked: {
type: [Boolean, String],
default: false,
},
id: {
type: String,
default: '',
},
disabled: {
type: [Boolean, String],
default: false,
},
color: {
type: String,
default: '#007aff',
},
value: {
type: String,
default: '',
},
}
import { PolySymbol } from '@dcloudio/uni-core'
export const uniFormKey = PolySymbol(__DEV__ ? 'uniForm' : 'uf')
export interface UniFormCtx {
addField: (field: UniFormFieldCtx) => void
removeField: (field: UniFormFieldCtx) => void
submit: (evt: Event) => void
reset: (evt: Event) => void
}
export interface UniFormFieldCtx {
submit?: () => [string, any]
reset?: () => void
}
export const iconProps = {
type: {
type: String,
default: '',
},
size: {
type: [String, Number],
default: 23,
},
color: {
type: String,
default: '',
},
}
export const iconColors: Record<string, string> = {
success: '#09bb07',
info: '#10aeff',
warn: '#f76260',
waiting: '#10aeff',
safe_success: '#09bb07',
safe_warn: '#ffbe00',
success_circle: '#09bb07',
success_no_circle: '#09bb07',
waiting_circle: '#10aeff',
circle: '#c9c9c9',
download: '#09bb07',
info_circle: '#09bb07',
cancel: '#f43530',
search: '#b2b2b2',
clear: '#b2b2b2',
}
import { PolySymbol } from '@dcloudio/uni-core'
export const uniRadioGroupKey = PolySymbol(__DEV__ ? 'uniRadioGroup' : 'ucg')
export const radioProps = {
checked: {
type: [Boolean, String],
default: false,
},
id: {
type: String,
default: '',
},
disabled: {
type: [Boolean, String],
default: false,
},
color: {
type: String,
default: '#007aff',
},
value: {
type: String,
default: '',
},
}
export const sliderProps = {
name: {
type: String,
default: '',
},
min: {
type: [Number, String],
default: 0,
},
max: {
type: [Number, String],
default: 100,
},
value: {
type: [Number, String],
default: 0,
},
step: {
type: [Number, String],
default: 1,
},
disabled: {
type: [Boolean, String],
default: false,
},
color: {
type: String,
default: '#e9e9e9',
},
backgroundColor: {
type: String,
default: '#e9e9e9',
},
activeColor: {
type: String,
default: '#007aff',
},
selectedColor: {
type: String,
default: '#007aff',
},
blockColor: {
type: String,
default: '#ffffff',
},
blockSize: {
type: [Number, String],
default: 28,
},
showValue: {
type: [Boolean, String],
default: false,
},
}
export const swiperItemProps = {
itemId: {
type: String,
default: '',
},
}
export const swiperProps = {
indicatorDots: {
type: [Boolean, String],
default: false,
},
vertical: {
type: [Boolean, String],
default: false,
},
autoplay: {
type: [Boolean, String],
default: false,
},
circular: {
type: [Boolean, String],
default: false,
},
interval: {
type: [Number, String],
default: 5e3,
},
duration: {
type: [Number, String],
default: 500,
},
current: {
type: [Number, String],
default: 0,
},
indicatorColor: {
type: String,
default: 'rgba(0,0,0,.3)',
},
indicatorActiveColor: {
type: String,
default: '#000000',
},
previousMargin: {
type: String,
default: '',
},
nextMargin: {
type: String,
default: '',
},
currentItemId: {
type: String,
default: '',
},
skipHiddenItemLayout: {
type: [Boolean, String],
default: false,
},
displayMultipleItems: {
type: [Number, String],
default: 1,
},
disableTouch: {
type: [Boolean, String],
default: false,
},
}
export const switchProps = {
name: {
type: String,
default: '',
},
checked: {
type: [Boolean, String],
default: false,
},
type: {
type: String,
default: 'switch',
},
id: {
type: String,
default: '',
},
disabled: {
type: [Boolean, String],
default: false,
},
color: {
type: String,
default: '#007aff',
},
}
import {
defineComponent,
Ref,
ref,
reactive,
watch,
onMounted,
ExtractPropTypes,
} from 'vue'
import {
useCustomEvent,
EmitEvent,
CustomEventTrigger,
} from '../../helpers/useNVueEvent'
import { getComponentSize } from '../helpers'
import { adDrawProps, getAdData } from '../../components/ad-draw'
type AdDrawProps = ExtractPropTypes<typeof adDrawProps>
type AdDrawState = ReturnType<typeof useAdDrawState>
const AdEventType = {
load: 'load',
close: 'close',
error: 'error',
}
export default defineComponent({
name: 'AdDraw',
props: adDrawProps,
emits: [AdEventType.load, AdEventType.close, AdEventType.error],
setup(props, { emit }) {
const adRef: Ref<HTMLElement | null> = ref(null)
const trigger = useCustomEvent<EmitEvent<typeof emit>>(adRef, emit)
const state = useAdDrawState(props)
watch(
() => props.adpid,
(value) => {
_loadAdData(state, props, trigger)
}
)
watch(
() => props.data,
(value) => {
state.data = value
}
)
const listeners = {
dislike(e: Event) {
trigger(AdEventType.close, e)
},
}
onMounted(() => {
setTimeout(() => {
getComponentSize(adRef.value!).then(({ width, height }) => {
state.width = width === 0 ? -1 : width
state.height = height === 0 ? -1 : height
_loadAdData(state, props, trigger)
})
}, 50)
})
return () => {
const { data } = state
return (
<u-ad-draw
ref="ad"
{...{ data, rendering: true }}
{...listeners}
></u-ad-draw>
)
}
},
})
function useAdDrawState(props: AdDrawProps) {
const data = ref('')
const state = reactive({
width: 0,
height: 0,
data,
})
return state
}
function _loadAdData(
state: AdDrawState,
props: AdDrawProps,
trigger: CustomEventTrigger
) {
getAdData(
props.adpid,
state.width,
state.height,
(res: any) => {
state.data = res.data
trigger(AdEventType.load, {})
},
(err: any) => {
trigger(AdEventType.error, err)
}
)
}
import {
defineComponent,
Ref,
ref,
reactive,
watch,
onMounted,
ExtractPropTypes,
} from 'vue'
import {
useCustomEvent,
EmitEvent,
CustomEventTrigger,
} from '../../helpers/useNVueEvent'
import { getComponentSize } from '../helpers'
import { adProps, getAdData } from '../../components/ad'
type AdProps = ExtractPropTypes<typeof adProps>
type AdState = ReturnType<typeof useAdState>
const AdEventType = {
load: 'load',
close: 'close',
error: 'error',
downloadchange: 'downloadchange',
}
export default defineComponent({
name: 'Ad',
props: adProps,
emits: [
AdEventType.load,
AdEventType.close,
AdEventType.error,
AdEventType.downloadchange,
],
setup(props, { emit }) {
const adRef: Ref<HTMLElement | null> = ref(null)
const trigger = useCustomEvent<EmitEvent<typeof emit>>(adRef, emit)
const state = useAdState(props)
watch(
() => props.adpid,
(value) => {
_loadAdData(state, props, trigger)
}
)
watch(
() => props.data,
(value) => {
state.data = value
}
)
onMounted(() => {
setTimeout(() => {
getComponentSize(adRef.value!).then(({ width }) => {
state.width = width === 0 ? -1 : width
_loadAdData(state, props, trigger)
})
}, 50)
})
const listeners = {
downloadchange(e: Event) {
trigger(AdEventType.downloadchange, e)
},
dislike(e: Event) {
trigger(AdEventType.close, e)
},
}
return () => {
const { data } = state
return <u-ad ref="ad" {...{ data, rendering: true }} {...listeners} />
}
},
})
function useAdState(props: AdProps) {
const data = ref('')
const state = reactive({
width: 0,
data,
})
return state
}
function _loadAdData(
state: AdState,
props: AdProps,
trigger: CustomEventTrigger
) {
getAdData(
{
adpid: props.adpid,
width: state.width,
},
(res: any) => {
state.data = res.data
trigger(AdEventType.load, {})
},
(err: any) => {
trigger(AdEventType.error, err)
}
)
}
...@@ -16,6 +16,7 @@ import { ...@@ -16,6 +16,7 @@ import {
} from '../utils' } from '../utils'
import { buttonProps } from '../../components/button' import { buttonProps } from '../../components/button'
import { extend } from '@vue/shared' import { extend } from '@vue/shared'
import { uniFormKey, UniFormCtx } from '../../components/form'
const buttonStyle: NVueComponentStyles = [ const buttonStyle: NVueComponentStyles = [
{ {
...@@ -306,13 +307,17 @@ export default defineComponent({ ...@@ -306,13 +307,17 @@ export default defineComponent({
}) })
const type = props.type as keyof typeof TYPES const type = props.type as keyof typeof TYPES
const rootRef = ref<HTMLElement | null>(null) const rootRef = ref<HTMLElement | null>(null)
const uniForm = inject<UniFormCtx>(
uniFormKey,
false as unknown as UniFormCtx
)
const onClick = (e: Event, isLabelClick?: boolean) => { const onClick = (e: Event, isLabelClick?: boolean) => {
const _onClick = ($listeners.value as any).onClick || (() => {}) const _onClick = ($listeners.value as any).onClick || (() => {})
if (props.disabled) { if (props.disabled) {
return return
} }
_onClick(e) _onClick(e)
/* const formType = props.formType const formType = props.formType
if (formType) { if (formType) {
if (!uniForm) { if (!uniForm) {
return return
...@@ -322,7 +327,7 @@ export default defineComponent({ ...@@ -322,7 +327,7 @@ export default defineComponent({
} else if (formType === 'reset') { } else if (formType === 'reset') {
uniForm.reset(e) uniForm.reset(e)
} }
} */ }
} }
const _getClass = (t: string) => { const _getClass = (t: string) => {
......
import {
defineComponent,
inject,
provide,
ComputedRef,
ref,
ExtractPropTypes,
} from 'vue'
import { uniCheckGroupKey } from '../../components/checkbox-group'
import { UniFormCtx, uniFormKey } from '../../components/form'
import {
CustomEventTrigger,
useCustomEvent,
EmitEvent,
} from '../../helpers/useEvent'
type UniCheckGroupFieldCtx = ComputedRef<{
checkboxChecked: boolean
value: string
}>
const props = {
name: {
type: String,
default: '',
},
}
type CheckBoxGroupProps = ExtractPropTypes<typeof props>
export interface UniCheckGroupCtx {
addField: (field: UniCheckGroupFieldCtx) => void
removeField: (field: UniCheckGroupFieldCtx) => void
checkboxChange: ($event: Event) => void
}
export default defineComponent({
name: 'CheckboxGroup',
props,
emits: ['change'],
setup(props, { slots, emit }) {
const rootRef = ref<HTMLElement | null>(null)
const trigger = useCustomEvent<EmitEvent<typeof emit>>(rootRef, emit)
useProvideCheckGroup(props, trigger)
return () => {
return (
<div ref={rootRef} class="uni-checkbox-group">
{slots.default && slots.default()}
</div>
)
}
},
})
function useProvideCheckGroup(
props: CheckBoxGroupProps,
trigger: CustomEventTrigger
) {
const fields: UniCheckGroupFieldCtx[] = []
const getFieldsValue = () =>
fields.reduce((res, field) => {
if (field.value.checkboxChecked) {
res.push(field.value.value)
}
return res
}, new Array())
provide<UniCheckGroupCtx>(uniCheckGroupKey, {
addField(field: UniCheckGroupFieldCtx) {
fields.push(field)
},
removeField(field: UniCheckGroupFieldCtx) {
fields.splice(fields.indexOf(field), 1)
},
checkboxChange($event) {
trigger('change', $event, {
value: getFieldsValue(),
})
},
})
const uniForm = inject<UniFormCtx>(uniFormKey, false as unknown as UniFormCtx)
if (uniForm) {
uniForm.addField({
submit: () => {
let data: [string, any] = ['', null]
if (props.name !== '') {
data[0] = props.name
data[1] = getFieldsValue()
}
return data
},
})
}
return getFieldsValue
}
import {
defineComponent,
inject,
onBeforeUnmount,
ref,
computed,
watch,
} from 'vue'
import { uniLabelKey, UniLabelCtx } from '../label'
import { useListeners } from '../../helpers/useListeners'
import { NVueComponentStyles, createNVueTextVNode } from '../utils'
import { checkboxProps } from '../../components/checkbox'
const checkboxStyles: NVueComponentStyles = [
{
'uni-checkbox': {
'': {
flexDirection: 'row',
alignItems: 'center',
},
},
'uni-checkbox-input': {
'': {
justifyContent: 'center',
alignItems: 'center',
position: 'relative',
borderWidth: '1',
borderColor: '#d1d1d1',
borderStyle: 'solid',
backgroundColor: '#ffffff',
borderRadius: '3',
width: '22',
height: '22',
},
},
'uni-icon': {
'': {
fontFamily: 'unincomponents',
fontSize: '16',
marginLeft: '2',
marginTop: '2',
color: '#007aff',
},
},
'uni-checkbox-input-disabled': {
'': {
backgroundColor: '#e1e1e1',
},
},
'uni-checkbox-input-disabled-before': {
'': {
color: '#adadad',
},
},
'uni-checkbox-slot': {
'': {
fontSize: '16',
marginLeft: '5',
},
},
},
]
export default defineComponent({
name: 'Checkbox',
props: checkboxProps,
styles: checkboxStyles,
setup(props, { slots }) {
const rootRef = ref<HTMLElement | null>(null)
const checkboxChecked = ref(props.checked)
const checkboxValue = ref(props.value)
const onClick = (e: Event, isLabelClick?: boolean) => {
if (props.disabled) {
return
}
if (isLabelClick) {
rootRef.value!.click()
}
checkboxChecked.value = !checkboxChecked.value
}
const checkboxColor = computed(() =>
props.disabled ? '#adadad' : props.color
)
watch(
[() => props.checked, () => props.value],
([newChecked, newModelValue]) => {
checkboxChecked.value = newChecked
checkboxValue.value = newModelValue
}
)
const uniLabel = inject<UniLabelCtx>(
uniLabelKey,
false as unknown as UniLabelCtx
)
if (uniLabel) {
uniLabel.addHandler(onClick)
onBeforeUnmount(() => {
uniLabel.removeHandler(onClick)
})
}
useListeners(props, { 'label-click': onClick })
const wrapSlots = () => {
if (!slots.default) return []
const vnodes = slots.default()
if (vnodes.length === 1 && vnodes[0].type === Text) {
return [
createNVueTextVNode(vnodes[0].children as string, {
class: 'uni-checkbox-slot',
}),
]
}
return vnodes
}
return () => {
return (
<div
ref={rootRef}
{...{ dataUncType: 'uni-checkbox' }}
onClick={onClick}
class="uni-checkbox"
>
<div
class="uni-checkbox-input"
// @ts-expect-error
class={{ 'uni-checkbox-input-disabled': props.disabled }}
>
{checkboxChecked.value
? createNVueTextVNode('\uEA08', {
class: 'uni-icon',
style: {
color: checkboxColor.value,
},
})
: null}
</div>
{...wrapSlots()}
</div>
)
}
},
})
...@@ -7,6 +7,14 @@ import Progress from './progress' ...@@ -7,6 +7,14 @@ import Progress from './progress'
import PickerView from './picker-view' import PickerView from './picker-view'
import PickerViewColumn from './picker-view-column' import PickerViewColumn from './picker-view-column'
import Picker from './picker' import Picker from './picker'
import USlider from './slider'
import Switch from './switch'
import Checkbox from './checkbox'
import CheckboxGroup from './checkbox-group'
import Radio from './radio'
import RadioGroup from './radio-group'
import Form from './form'
import Icon from './icon'
export default { export default {
Navigator, Navigator,
Label, Label,
...@@ -17,4 +25,12 @@ export default { ...@@ -17,4 +25,12 @@ export default {
PickerView, PickerView,
PickerViewColumn, PickerViewColumn,
Picker, Picker,
USlider,
Switch,
Checkbox,
CheckboxGroup,
Radio,
RadioGroup,
Form,
Icon,
} }
import { defineComponent, provide, ref } from 'vue'
import {
useCustomEvent,
EmitEvent,
CustomEventTrigger,
} from '../../helpers/useNVueEvent'
import { uniFormKey, UniFormCtx, UniFormFieldCtx } from '../../components/form'
const NATIVE_COMPONENTS = ['u-input', 'u-textarea']
export default defineComponent({
name: 'Form',
emits: ['submit', 'reset'],
setup({}, { slots, emit }) {
const rootRef = ref<HTMLElement | null>(null)
const trigger = useCustomEvent<EmitEvent<typeof emit>>(rootRef, emit)
let so = slots.default && slots.default()
console.log(so)
provideForm(trigger, slots.default && (slots.default() as any))
return () => {
return <view ref={rootRef}>{slots.default && slots.default()}</view>
}
},
})
function provideForm(trigger: CustomEventTrigger, children: any) {
const modulePlus = weex.requireModule('plus')
const fields: UniFormFieldCtx[] = []
const getOrClearNativeValue = (nodes: any, outResult: any): void => {
nodes.forEach(function (node: any) {
if (
NATIVE_COMPONENTS.indexOf(node.type) >= 0 &&
node.data.attrs &&
node.data.attrs.name
) {
if (outResult) {
outResult[node.attrs.name] = modulePlus.getValue(node.elm.nodeId)
} else {
node.elm.setValue('')
}
}
if (
Array.isArray(node.children) &&
node.children &&
node.children.length
) {
getOrClearNativeValue(node.children, outResult)
}
})
}
provide<UniFormCtx>(uniFormKey, {
addField(field: UniFormFieldCtx) {
fields.push(field)
},
removeField(field: UniFormFieldCtx) {
fields.splice(fields.indexOf(field), 1)
},
submit(evt: Event) {
// 获取原生组件值
let outFormData: any = {}
getOrClearNativeValue(children, outFormData)
let formData = fields.reduce((res, field) => {
if (field.submit) {
const [name, value] = field.submit()
name && (res[name] = value)
}
return res
}, Object.create(null))
Object.assign(outFormData, formData)
trigger('submit', {
value: outFormData,
})
},
reset(evt: Event) {
// 清空原生组件值
getOrClearNativeValue(children, null)
fields.forEach((field) => field.reset && field.reset())
trigger('reset', evt)
},
})
return fields
}
import { defineComponent } from 'vue'
import { iconProps, iconColors } from '../../components/icon'
import { NVueComponentStyles, createNVueTextVNode } from '../utils'
const iconChars: Record<string, string> = {
success: '\uEA06',
info: '\uEA03',
warn: '\uEA0B',
waiting: '\uEA09',
safe_success: '\uEA04',
safe_warn: '\uEA05',
success_circle: '\uEA07',
success_no_circle: '\uEA08',
waiting_circle: '\uEA0A',
circle: '\uEA01',
download: '\uEA02',
info_circle: '\uEA0C',
cancel: '\uEA0D',
search: '\uEA0E',
clear: '\uEA0F',
}
const iconStyles: NVueComponentStyles = [
{
'uni-icon': {
'': {
fontFamily: 'unincomponents',
},
},
},
]
export default defineComponent({
name: 'Icon',
props: iconProps,
styles: iconStyles,
setup(props, {}) {
return () => {
return createNVueTextVNode(iconChars[props.type] as string, {
class: 'uni-icon',
style: {
color: props.color || iconColors[props.type],
fontSize: props.size,
},
})
}
},
})
...@@ -14,26 +14,34 @@ import { ...@@ -14,26 +14,34 @@ import {
CustomEventTrigger, CustomEventTrigger,
} from '../../helpers/useNVueEvent' } from '../../helpers/useNVueEvent'
import { getComponentSize } from '../helpers' import { getComponentSize } from '../helpers'
import { createNVueTextVNode } from '../utils' import { NVueComponentStyles, createNVueTextVNode } from '../utils'
import { PROGRESS_VALUES, progressProps } from '../../components/progress' import { PROGRESS_VALUES, progressProps } from '../../components/progress'
const progressStyles: Record<string, Record<string, string | number>>[] = [ const progressStyles: NVueComponentStyles = [
{ {
'uni-progress': { 'uni-progress': {
'': {
flex: 1, flex: 1,
flexDirection: 'row', flexDirection: 'row',
alignItems: 'center', alignItems: 'center',
}, },
},
'uni-progress-bar': { 'uni-progress-bar': {
'': {
flex: 1, flex: 1,
}, },
},
'uni-progress-inner-bar': { 'uni-progress-inner-bar': {
'': {
position: 'absolute', position: 'absolute',
}, },
},
'uni-progress-info': { 'uni-progress-info': {
'': {
marginLeft: '15px', marginLeft: '15px',
}, },
}, },
},
] ]
type ProgressProps = ExtractPropTypes<typeof progressProps> type ProgressProps = ExtractPropTypes<typeof progressProps>
......
import {
defineComponent,
inject,
provide,
ComputedRef,
ref,
ExtractPropTypes,
} from 'vue'
import { uniRadioGroupKey } from '../../components/radio-group'
import { UniFormCtx, uniFormKey } from '../../components/form'
import {
CustomEventTrigger,
useCustomEvent,
EmitEvent,
} from '../../helpers/useEvent'
type UniRadioGroupFieldCtx = ComputedRef<{
radioChecked: boolean
value: string
}>
const props = {
name: {
type: String,
default: '',
},
}
type RadioGroupProps = ExtractPropTypes<typeof props>
export interface UniRadioGroupCtx {
addField: (field: UniRadioGroupFieldCtx) => void
removeField: (field: UniRadioGroupFieldCtx) => void
radioChange: ($event: Event) => void
}
export default defineComponent({
name: 'RadioGroup',
props,
emits: ['change'],
setup(props, { slots, emit }) {
const rootRef = ref<HTMLElement | null>(null)
const trigger = useCustomEvent<EmitEvent<typeof emit>>(rootRef, emit)
useProvideRadioGroup(props, trigger)
return () => {
return <div ref={rootRef}>{slots.default && slots.default()}</div>
}
},
})
function useProvideRadioGroup(
props: RadioGroupProps,
trigger: CustomEventTrigger
) {
const fields: UniRadioGroupFieldCtx[] = []
const getFieldsValue = () =>
fields.reduce((res, field) => {
if (field.value.radioChecked) {
res.push(field.value.value)
}
return res
}, new Array())
provide<UniRadioGroupCtx>(uniRadioGroupKey, {
addField(field: UniRadioGroupFieldCtx) {
fields.push(field)
},
removeField(field: UniRadioGroupFieldCtx) {
fields.splice(fields.indexOf(field), 1)
},
radioChange($event) {
trigger('change', $event, {
value: getFieldsValue(),
})
},
})
const uniForm = inject<UniFormCtx>(uniFormKey, false as unknown as UniFormCtx)
if (uniForm) {
uniForm.addField({
submit: () => {
let data: [string, any] = ['', null]
if (props.name !== '') {
data[0] = props.name
data[1] = getFieldsValue()
}
return data
},
})
}
return getFieldsValue
}
import {
defineComponent,
inject,
onBeforeUnmount,
ref,
computed,
watch,
reactive,
ExtractPropTypes,
} from 'vue'
import { uniLabelKey, UniLabelCtx } from '../label'
import { useListeners } from '../../helpers/useListeners'
import { NVueComponentStyles, createNVueTextVNode } from '../utils'
import { radioProps } from '../../components/radio'
const radioStyles: NVueComponentStyles = [
{
'uni-radio': {
'': {
alignItems: 'center',
flexDirection: 'row',
},
},
'uni-radio-input': {
'': {
position: 'relative',
alignItems: 'center',
justifyContent: 'center',
marginRight: '5',
borderStyle: 'solid',
borderWidth: '1',
borderColor: '#d1d1d1',
borderRadius: 50,
width: '22',
height: '22',
outline: 0,
},
},
'uni-radio-input-icon': {
'': {
fontFamily: 'unincomponents',
fontSize: '14',
color: '#ffffff',
},
},
'uni-radio-input-disabled': {
'': {
backgroundColor: '#e1e1e1',
borderColor: '#d1d1d1',
color: '#adadad',
},
},
'uni-radio-slot': {
'': {
fontSize: '16',
marginLeft: '5',
},
},
},
]
type RadioProps = ExtractPropTypes<typeof radioProps>
export default defineComponent({
name: 'Radio',
props: radioProps,
styles: radioStyles,
emits: ['change'],
setup(props, { slots }) {
const rootRef = ref<HTMLElement | null>(null)
const state = useRadioState(props)
const onClick = (e: Event, isLabelClick?: boolean) => {
if (props.disabled) {
return
}
if (isLabelClick) {
rootRef.value!.click()
}
state.radioChecked = !state.radioChecked
/* const formType = props.formType
if (formType) {
if (!uniForm) {
return
}
if (formType === 'submit') {
uniForm.submit(e)
} else if (formType === 'reset') {
uniForm.reset(e)
}
} */
}
const uniLabel = inject<UniLabelCtx>(
uniLabelKey,
false as unknown as UniLabelCtx
)
if (uniLabel) {
uniLabel.addHandler(onClick)
onBeforeUnmount(() => {
uniLabel.removeHandler(onClick)
})
}
useListeners(props, { 'label-click': onClick })
watch(
[() => props.checked, () => props.value],
([newChecked, newModelValue]) => {
state.radioChecked = newChecked
state.radioValue = newModelValue
}
)
const wrapSlots = () => {
if (!slots.default) return []
const vnodes = slots.default()
if (vnodes.length === 1 && vnodes[0].type === Text) {
return [
createNVueTextVNode(vnodes[0].children as string, {
class: 'uni-radio-slot',
}),
]
}
return vnodes
}
return () => {
const { disabled } = props
const { radioChecked, radioStyle } = state
return (
<div
ref={rootRef}
{...{ dataUncType: 'uni-radio' }}
onClick={onClick}
class="uni-radio"
>
<div
style={radioStyle}
class="uni-radio-input"
// @ts-expect-error
class={{ 'uni-radio-input-disabled': disabled }}
>
{radioChecked
? createNVueTextVNode('\uEA08', {
class: 'uni-radio-input-icon',
})
: null}
</div>
{...wrapSlots()}
</div>
)
}
},
})
function useRadioState(props: RadioProps) {
const radioChecked = ref(props.checked)
const radioValue = ref(props.value)
const radioStyle = computed(() => {
if (radioChecked.value) {
return {
backgroundColor: props.color,
borderColor: props.color,
}
}
return {
borderColor: '#d1d1d1',
}
})
const radioColor = computed(() => (props.disabled ? '#adadad' : props.color))
const state = reactive({
radioStyle,
radioColor,
radioChecked,
radioValue,
})
return state
}
import {
defineComponent,
Ref,
ref,
computed,
watch,
onMounted,
reactive,
ExtractPropTypes,
} from 'vue'
import {
useCustomEvent,
EmitEvent,
CustomEventTrigger,
} from '../../helpers/useNVueEvent'
import { getComponentSize } from '../helpers'
import { NVueComponentStyles, createNVueTextVNode } from '../utils'
import { sliderProps } from '../../components/slider'
const slierStyles: NVueComponentStyles = [
{
'uni-slider': {
'': {
flex: 1,
flexDirection: 'column',
marginTop: '12',
marginRight: 0,
marginBottom: '12',
marginLeft: 0,
paddingTop: 0,
paddingRight: 0,
paddingBottom: 0,
paddingLeft: 0,
},
},
'uni-slider-wrapper': {
'': {
flexDirection: 'row',
alignItems: 'center',
minHeight: '30',
},
},
'uni-slider-tap-area': {
'': {
position: 'relative',
flex: 1,
flexDirection: 'column',
paddingTop: '15',
paddingRight: 0,
paddingBottom: '15',
paddingLeft: 0,
},
},
'uni-slider-handle-wrapper': {
'': {
position: 'relative',
marginTop: 0,
marginRight: '18',
marginBottom: 0,
marginLeft: '18',
height: '2',
borderRadius: '5',
backgroundColor: '#e9e9e9',
transitionProperty: 'backgroundColor',
transitionDuration: 300,
transitionTimingFunction: 'ease',
},
},
'uni-slider-track': {
'': {
height: '2',
borderRadius: '6',
backgroundColor: '#007aff',
transitionProperty: 'backgroundColor',
transitionDuration: 300,
transitionTimingFunction: 'ease',
},
},
'uni-slider-thumb': {
'': {
position: 'absolute',
width: '28',
height: '28',
borderRadius: 50,
boxShadow: '0 0 4px #ebebeb',
transitionProperty: 'borderColor',
transitionDuration: 300,
transitionTimingFunction: 'ease',
},
},
'uni-slider-step': {
'': {
position: 'absolute',
width: 100,
height: '2',
background: 'transparent',
zIndex: 1,
},
},
'uni-slider-value': {
'': {
color: '#888888',
fontSize: '14',
marginRight: '14',
},
},
},
]
type SliderProps = ExtractPropTypes<typeof sliderProps>
type SliderState = ReturnType<typeof useSliderState>
export default defineComponent({
name: 'USlider',
props: sliderProps,
styles: slierStyles,
setup(props, { emit }) {
const sliderRef: Ref<HTMLElement | null> = ref(null)
const sliderTrackRef: Ref<HTMLElement | null> = ref(null)
const trigger = useCustomEvent<EmitEvent<typeof emit>>(sliderRef, emit)
const state = useSliderState(props)
const listeners = useSliderListeners(props, state, trigger)
watch(
() => props.value,
(val) => {
state.sliderValue = Number(val)
}
)
onMounted(() => {
setTimeout(() => {
getComponentSize(sliderRef.value!).then(({ width }) => {
state.sliderWidth = width || 0
state.sliderValue = Number(props.value)
})
}, 100)
})
return () => {
const { showValue } = props
const { trackStyle, trackActiveStyle, thumbStyle, sliderValue } = state
return (
<div class="uni-slider" ref={sliderRef}>
<div class="uni-slider-wrapper">
<div class="uni-slider-tap-area" {...listeners}>
<div
class="uni-slider-handle-wrapper"
ref={sliderTrackRef}
style={trackStyle}
>
<div class="uni-slider-track" style={trackActiveStyle}></div>
</div>
<div class="uni-slider-thumb" style={thumbStyle}></div>
</div>
{showValue
? createNVueTextVNode(sliderValue as unknown as string, {
class: 'uni-slider-value',
})
: null}
</div>
</div>
)
}
},
})
function useSliderState(props: SliderProps) {
const sliderWidth = ref<number>(0)
const sliderValue = ref<number>(0)
const _getBgColor = () => {
return props.backgroundColor !== '#e9e9e9'
? props.backgroundColor
: props.color !== '#007aff'
? props.color
: '#007aff'
}
const _getActiveColor = () => {
return props.activeColor !== '#007aff'
? props.activeColor
: props.selectedColor !== '#e9e9e9'
? props.selectedColor
: '#e9e9e9'
}
const _getValueWidth = () => {
const max = Number(props.max)
const min = Number(props.min)
return ((sliderValue.value - min) / max - min) * sliderWidth.value
}
const state = reactive({
sliderWidth,
sliderValue,
trackStyle: computed(() => ({ backgroundColor: _getBgColor() })),
trackActiveStyle: computed(() => ({
backgroundColor: _getActiveColor(),
width: _getValueWidth(),
})),
thumbStyle: computed(() => ({
width: props.blockSize,
height: props.blockSize,
marginTop: -props.blockSize / 2,
left: _getValueWidth(),
backgroundColor: props.blockColor,
})),
})
return state
}
function useSliderListeners(
props: SliderProps,
state: SliderState,
trigger: CustomEventTrigger
) {
let eventOld: any = null
function onTrack(action: string, x: number) {
if (!props.disabled) {
if (action === 'move') {
changedValue(x)
trigger('changing', {
value: state.sliderValue,
})
} else if (action === 'end') {
changedValue(x)
trigger('change', {
value: state.sliderValue,
})
}
}
}
function changedValue(x: number) {
if (x < 0) {
x = 0
}
if (x > state.sliderWidth) {
x = state.sliderWidth
}
const max = Number(props.max)
const min = Number(props.min)
const step = Number(props.step)
let value: number = (x / state.sliderWidth) * max - min
if (step > 0 && value > step && (value % step) / step !== 0) {
value -= value % step
}
state.sliderValue = value + min
}
const listeners = {
onTouchstart(e: TouchEvent) {
if (e.changedTouches.length === 1 && !eventOld) {
eventOld = e
onTrack('start', e.changedTouches[0].pageX)
}
},
onTouchmove(e: TouchEvent) {
if (e.changedTouches.length === 1 && eventOld) {
onTrack('move', e.changedTouches[0].pageX)
}
},
onTouchend(e: TouchEvent) {
if (e.changedTouches.length === 1 && eventOld) {
eventOld = null
onTrack('end', e.changedTouches[0].pageX)
}
},
}
return listeners
}
import { defineComponent } from 'vue'
import { swiperItemProps } from '../../components/swiper-item'
export default defineComponent({
name: 'SwiperItem',
props: swiperItemProps,
setup(props, { emit }) {
return () => {
return (
<div
class="uni-swiper-item"
style={{
position: 'absolute',
left: 0,
top: 0,
right: 0,
bottom: 0,
overflow: 'hidden',
}}
></div>
)
}
},
})
import {
defineComponent,
ref,
onMounted,
watch,
computed,
reactive,
ExtractPropTypes,
VNode,
} from 'vue'
import {
useCustomEvent,
EmitEvent,
CustomEventTrigger,
} from '../../helpers/useNVueEvent'
import { getComponentSize } from '../helpers'
import { NVueComponentStyles } from '../utils'
import { flatVNode } from '../../helpers/flatVNode'
import { swiperProps } from '../../components/swiper'
const swiperStyles: NVueComponentStyles = [
{
'uni-swiper': {
'': {
position: 'relative',
height: '150px',
},
},
'uni-swiper-slider': {
'': {
position: 'absolute',
left: 0,
top: 0,
right: 0,
bottom: 0,
},
},
'uni-swiper-dots': {
'': {
position: 'absolute',
left: 0,
right: 0,
bottom: '10',
height: '10',
},
},
},
]
type SwiperProps = ExtractPropTypes<typeof swiperProps>
type SwiperState = ReturnType<typeof useSwiperState>
export default defineComponent({
name: 'Swiper',
props: swiperProps,
styles: swiperStyles,
emits: ['change', 'transition', 'animationfinish'],
setup(props, { slots, emit }) {
const rootRef = ref<HTMLElement | null>(null)
let swiperItems: VNode[] = []
const trigger = useCustomEvent<EmitEvent<typeof emit>>(rootRef, emit)
const state = useSwiperState(props)
const listeners = useSwiperListeners(state, props, swiperItems, trigger)
watch(
[() => props.current, () => props.currentItemId],
([newChecked, newModelValue]) => {
currentCheck(state, props, swiperItems)
}
)
onMounted(() => {
setTimeout(() => {
getComponentSize(rootRef.value!).then(({ width, height }) => {
state.swiperWidth = width
state.swiperHeight = height
})
}, 50)
})
return () => {
const defaultSlots = slots.default && slots.default()
const { indicatorStyle } = state
swiperItems = flatVNode(defaultSlots)
return (
<div ref={rootRef} class="uni-swiper">
<slider class="uni-swiper-slider" {...listeners}>
{swiperItems}
<indicator class="uni-swiper-dots" styles={indicatorStyle} />
</slider>
</div>
)
}
},
})
function useSwiperState(props: SwiperProps) {
let swiperWidth = ref<number>(0)
let swiperHeight = ref<number>(0)
const currentSync = ref(props.current)
const currentChangeSource = ref<string>('autoplay')
const indicatorStyle = computed(() => ({
itemColor: props.indicatorColor,
itemSelectedColor: props.indicatorActiveColor,
itemSize: 8,
// 动态创建 indicator 在安卓上有问题,改成透明度控制显示和隐藏
opacity: props.indicatorDots ? 1 : 0,
}))
const state = reactive({
swiperWidth,
swiperHeight,
indicatorStyle,
currentSync,
currentChangeSource,
})
return state
}
function useSwiperListeners(
state: SwiperState,
props: SwiperProps,
swiperItems: VNode[],
trigger: CustomEventTrigger
) {
let lastOffsetRatio: number = 0
const onScroll = (event: any) => {
let offsetRatio = props.vertical
? event.detail.offsetYRatio
: event.detail.offsetXRatio
if (event.drag || event.detail.drag) {
state.currentChangeSource = 'touch'
}
// 纠正 offsetRatio 数值
if (offsetRatio === 0) {
const lastOffsetRatio2 = Math.abs(lastOffsetRatio)
if (lastOffsetRatio2 === 1) {
return
} else if (lastOffsetRatio2 > 0.5) {
offsetRatio = 1
}
}
lastOffsetRatio = offsetRatio
trigger('transition', {
dx: props.vertical ? 0 : -state.swiperWidth * offsetRatio,
dy: props.vertical ? -state.swiperHeight * offsetRatio : 0,
})
}
const onScrollEnd = (event: CustomEvent) => {
const end = () => {
trigger('animationfinish', getDetail())
state.currentChangeSource = 'autoplay'
}
// 解决 iOS change 事件早于 scrollend 的问题
if (weex.config.env.platform === 'iOS') {
setTimeout(end, 50)
} else {
end()
}
}
const onChange = (event: CustomEvent) => {
const detail = event.detail
if (typeof detail.source === 'string') {
state.currentChangeSource = detail.source
}
state.currentSync = detail.index
lastOffsetRatio = 0
}
function getDetail() {
const current = Number(state.currentSync)
const currentItem: any = swiperItems[current] || {}
const currentItemId =
(currentItem.componentInstance && currentItem.componentInstance.itemId) ||
''
return {
current,
currentItemId,
source: state.currentChangeSource,
}
}
// TODO
watch(
() => state.currentSync,
(val) => {
trigger('change', getDetail())
}
)
const listeners = {
scroll: onScroll,
scrollend: onScrollEnd,
change: onChange,
}
return listeners
}
function currentCheck(
state: SwiperState,
props: SwiperProps,
swiperItems: VNode[]
) {
let current = -1
if (props.currentItemId) {
for (let i = 0, items = swiperItems; i < items.length; i++) {
const componentInstance = (items[i] as any).componentInstance
if (
componentInstance &&
componentInstance.itemId === props.currentItemId
) {
current = i
break
}
}
}
if (current < 0) {
current = Math.round(Number(props.current)) || 0
}
current = current < 0 ? 0 : current
if (state.currentSync !== current) {
state.currentChangeSource = ''
state.currentSync = current
}
}
import {
defineComponent,
Ref,
ref,
onBeforeUnmount,
onUnmounted,
watch,
inject,
ExtractPropTypes,
} from 'vue'
import { useCustomEvent, EmitEvent } from '../../helpers/useNVueEvent'
import { uniFormKey, UniFormCtx } from '../../components/form'
import { uniLabelKey, UniLabelCtx } from '../label'
import { useListeners } from '../../helpers/useListeners'
import { switchProps } from '../../components/switch'
const SwitchType = {
switch: 'switch',
checkbox: 'checkbox',
}
const DCSwitchSize = {
width: 52,
height: 32,
}
type SwitchProps = ExtractPropTypes<typeof switchProps>
export default defineComponent({
name: 'Switch',
props: switchProps,
emits: ['change'],
setup(props, { emit }) {
const rootRef = ref<HTMLElement | null>(null)
const switchChecked = ref(props.checked)
const uniLabel = useSwitchInject(props, switchChecked)
const trigger = useCustomEvent<EmitEvent<typeof emit>>(rootRef, emit)
watch(
() => props.checked,
(val) => {
switchChecked.value = val
}
)
const listeners = {
onChange(e: CustomEvent) {
switchChecked.value = e.detail.value
trigger('change', {
value: switchChecked.value,
})
},
}
const _onClick = ($event: Event) => {
if (props.disabled) {
return
}
switchChecked.value = !switchChecked.value
trigger('change', {
value: switchChecked.value,
})
}
if (!!uniLabel) {
uniLabel.addHandler(_onClick)
onBeforeUnmount(() => {
uniLabel.removeHandler(_onClick)
})
}
useListeners(props, { 'label-click': _onClick })
return () => {
const { color, type } = props
return (
<div ref={rootRef}>
{type === SwitchType.switch ? (
<dc-switch
{...{ dataUncType: 'uni-switch' }}
{...listeners}
{...{ checked: switchChecked.value }}
style={DCSwitchSize}
/>
) : null}
{type === SwitchType.checkbox ? (
<checkbox
style={{ color: color }}
{...{ checked: switchChecked.value }}
{...listeners}
/>
) : null}
</div>
)
}
},
})
function useSwitchInject(
props: SwitchProps,
switchChecked: Ref<string | boolean>
) {
const uniForm = inject<UniFormCtx>(uniFormKey, false as unknown as UniFormCtx)
const uniLabel = inject<UniLabelCtx>(
uniLabelKey,
false as unknown as UniLabelCtx
)
const formField = {
submit: () => {
const data: [string, any] = ['', null]
if (props.name) {
data[0] = props.name
data[1] = switchChecked.value
}
return data
},
reset: () => {
switchChecked.value = false
},
}
if (!!uniForm) {
uniForm.addField(formField)
onUnmounted(() => {
uniForm.removeField(formField)
})
}
return uniLabel
}
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册