From ec5fa3a10c2cd16c2e8ec7c5cce675ef702899e6 Mon Sep 17 00:00:00 2001 From: Cassiel Date: Mon, 10 Oct 2022 16:08:55 +0800 Subject: [PATCH] update farris btn group --- packages/.DS_Store | Bin 6148 -> 6148 bytes .../button/src/button-group.component.tsx | 101 ++++- .../button/src/button-group.props.ts | 30 +- .../button/src/button.component.tsx | 4 +- .../components/button/src/button.props.ts | 4 +- .../button/src/composition/types-group.ts | 16 +- .../button/src/composition/types.ts | 2 - .../src/composition/use-button-group.ts | 422 +++++++----------- .../button/src/composition/use-button.ts | 2 +- packages/ui-vue/src/App.vue | 2 + .../ui-vue/src/components/button-group.vue | 59 +++ packages/ui-vue/src/components/button.vue | 22 +- 12 files changed, 335 insertions(+), 329 deletions(-) create mode 100644 packages/ui-vue/src/components/button-group.vue diff --git a/packages/.DS_Store b/packages/.DS_Store index 85bd9bf710df82e11eb34555bb250396bbd7399e..09b3cc2eb255a45b45e97c66a392539805dc183b 100644 GIT binary patch delta 59 zcmZoMXfc@J&&atkU^gQp=j3@za+|f7bQvYt7)lv38FU%SfOKkEa8X`PeqK5Q0|Vpc OMCR!%o7p-3@&f?a_7BSd delta 36 scmZoMXfc@J&&aVcU^gQp$K-iTa+|f7bQw37GEZWe*dVu=o#QV*0M2y_GXMYp diff --git a/packages/ui-vue/components/button/src/button-group.component.tsx b/packages/ui-vue/components/button/src/button-group.component.tsx index 3297b9c..a8d23f8 100644 --- a/packages/ui-vue/components/button/src/button-group.component.tsx +++ b/packages/ui-vue/components/button/src/button-group.component.tsx @@ -1,37 +1,90 @@ -import { defineComponent, computed } from 'vue'; +import { defineComponent, computed, ref, watch } from 'vue'; import type { SetupContext } from 'vue'; import { buttonGroupProps, ButtonGroupProps } from './button-group.props'; import { useButtonGroup } from './composition/use-button-group'; +import { ElForm } from 'element-plus'; export default defineComponent({ name: 'FButtonGroup', props: buttonGroupProps, emits: ['click', 'changeState', 'change', 'clickMenuOut'], setup(props: ButtonGroupProps, context: SetupContext) { - // const { onClickButton } = useButtonGroup(props, context); - const fButtonGroupSize = computed(() => ({ - 'btn-group-lg': props.size === 'large', - 'btn-group-sm': props.size === 'small' - })); - // 样式: - // 'btn '+ - // (btn.type?'btn-'+ btn.type:'btn-link')+ - // ' '+ - // (btn.type && btn.type !== 'link' ? 'f-btn-ml' :'') + /* 显示出来的按钮组 */ + const flatButtons = props.data && props.data.slice(0, props.count); + const dpButtons = props.data && props.data.slice(props.count); + const $dpMenu = ref>(null); + const $dpBtn = ref>(null); + const { showPanel, clickEvent, toggle, getRealPlacement, bindMenuMouseenter, unbindMenuMouseleave, bindMenuMouseleave } = + useButtonGroup(props, context, $dpMenu, $dpBtn); + getRealPlacement(props.placement); - // btn btn-btn.type f-btn-ml - // btn btn-link + return () => ( +
+
+ { + flatButtons.map((btn) => { + return ( +
+ { + btn.icon && + + } + { + !btn.icon && + + } +
+ ); + }) + } +
+ { + !dpButtons.length ? '' : +
unbindMenuMouseleave()}> + +
bindMenuMouseenter()} + onMouseleave={(event: MouseEvent) => bindMenuMouseleave()} + > + { + dpButtons.map((dpBtn) => { + return ( +
+ { +
+ {dpBtn.divider && } +
  • toggle(event, dpBtn)}> + { + dpBtn.icon && + + } + {dpBtn?.text} +
  • +
    + } +
    + ); - // const fButtonType = computed(() => ({ - // 'btn-primary': props.buttonType === 'primary', - // 'btn-warning': props.buttonType === 'warning', - // 'btn-danger': props.buttonType === 'danger', - // 'btn-success': props.buttonType === 'success', - // 'btn-link': props.buttonType === 'link', - // 'btn-secondary': props.buttonType === 'secondary', - // })); - const theFlatButtons = useButtonGroup(props, context); - - return () => ''; + }) + } +
    +
    + } +
    + ); } }); diff --git a/packages/ui-vue/components/button/src/button-group.props.ts b/packages/ui-vue/components/button/src/button-group.props.ts index 157d048..5b88e98 100644 --- a/packages/ui-vue/components/button/src/button-group.props.ts +++ b/packages/ui-vue/components/button/src/button-group.props.ts @@ -1,25 +1,36 @@ import { ExtractPropTypes, PropType } from 'vue'; -// eslint-disable-next-line max-len -type PlacementDirection = 'top' | 'top-left' | 'top-right' | 'left' | 'left-top' | 'left-bottom' | 'bottom' | 'bottom-left' | 'bottom-right' | 'right' | 'right-top' | 'right-bottom'; +type PlacementDirection = + | 'top' + | 'top-left' + | 'top-right' + | 'left' + | 'left-top' + | 'left-bottom' + | 'bottom' + | 'bottom-left' + | 'bottom-right' + | 'right' + | 'right-top' + | 'right-bottom'; export const buttonGroupProps = { /** - * 组件标识 - */ + * 组件标识 + */ id: String, /** * 横向纠正的参照 */ - rectifyReferenceH: { type: HTMLElement, default: 'referenceEl' }, + rectifyReferenceH: { type: HTMLElement}, /** * 纵向纠正的参照 */ - rectifyReferenceV: { type: HTMLElement, default: 'referenceEl' }, + rectifyReferenceV: { type: HTMLElement}, /** * 是否自动纠正位置 */ - autoRectify: { type: Boolean, default: 'true' }, + autoRectify: { type: Boolean, default: false }, /** * 计算方向的真正placement */ @@ -47,7 +58,7 @@ export const buttonGroupProps = { /** * 按钮展示位置 */ - placement: { type: String, default: 'bottom' }, /** + placement: { type: String as PropType, default: 'bottom' }, /** /** * 下拉面板显示 */ @@ -55,8 +66,7 @@ export const buttonGroupProps = { /** * 下拉面板显示标志 */ - dpFlag: { type: Boolean, default: false }, - + dpFlag: { type: Boolean, default: false } }; export default buttonGroupProps; diff --git a/packages/ui-vue/components/button/src/button.component.tsx b/packages/ui-vue/components/button/src/button.component.tsx index 5dec7ce..fde5c63 100644 --- a/packages/ui-vue/components/button/src/button.component.tsx +++ b/packages/ui-vue/components/button/src/button.component.tsx @@ -10,8 +10,8 @@ export default defineComponent({ setup(props: ButtonProps, context: SetupContext) { const { onClickButton } = useButton(props, context); return () => ( - diff --git a/packages/ui-vue/components/button/src/button.props.ts b/packages/ui-vue/components/button/src/button.props.ts index 745a562..f51a1ad 100644 --- a/packages/ui-vue/components/button/src/button.props.ts +++ b/packages/ui-vue/components/button/src/button.props.ts @@ -11,11 +11,11 @@ export const buttonProps = { /** * 设置按钮类型 */ - buttonType: { type: String as PropType, default: 'primary' }, + type: { type: String as PropType, default: 'primary' }, /** * 是否禁用 */ - disable: { type: Boolean, default: false }, + disabled: { type: Boolean, default: false }, /** * 按钮尺寸 */ diff --git a/packages/ui-vue/components/button/src/composition/types-group.ts b/packages/ui-vue/components/button/src/composition/types-group.ts index 302e597..0ae89ae 100644 --- a/packages/ui-vue/components/button/src/composition/types-group.ts +++ b/packages/ui-vue/components/button/src/composition/types-group.ts @@ -1,13 +1,9 @@ -import { ComputedRef } from 'vue'; - export interface UseButtonGroup { - /** - * 附加按钮的Class - */ - // buttonClass: ComputedRef>; - /** - * 点击附加按钮事件响应函数 - */ + showPanel: any; clickEvent: ($event: Event) => void; - + toggle: ($event: Event, btn: any) => void; + getRealPlacement: (pment: any) => void; + bindMenuMouseenter: (this: any) => void; + unbindMenuMouseleave: (this: any) => void; + bindMenuMouseleave: (this: any) => void; } diff --git a/packages/ui-vue/components/button/src/composition/types.ts b/packages/ui-vue/components/button/src/composition/types.ts index 54d9b9d..b3567a0 100644 --- a/packages/ui-vue/components/button/src/composition/types.ts +++ b/packages/ui-vue/components/button/src/composition/types.ts @@ -1,5 +1,3 @@ -import { ComputedRef } from 'vue'; - export interface UseButton { /** * 附加按钮的Class diff --git a/packages/ui-vue/components/button/src/composition/use-button-group.ts b/packages/ui-vue/components/button/src/composition/use-button-group.ts index a31b404..3d469a7 100644 --- a/packages/ui-vue/components/button/src/composition/use-button-group.ts +++ b/packages/ui-vue/components/button/src/composition/use-button-group.ts @@ -1,70 +1,89 @@ import { UseButtonGroup } from './types-group'; import { ButtonGroupProps } from '../button-group.props'; -import { computed, SetupContext } from 'vue'; +import { computed, SetupContext, ref, nextTick } from 'vue'; -export function useButtonGroup(props: ButtonGroupProps, context: SetupContext): UseButtonGroup { - // ngAfterViewChecked() { - // if (this.show) { - // this.setPosition(this.event) - // } - // } +export function useButtonGroup(props: ButtonGroupProps, context: SetupContext, dpMenu: any, dpBtn: any): UseButtonGroup { + /* menu size */ + let _menuWidth = 0; + let _menuHeight = 0; + const showPanel = ref(props.showPanel); + let realPlacement = ref(props.realPlacement); + let mouseEnter = false; - // function onClickButton($event: Event) { - // $event.stopPropagation(); - // // this.disabled - // if (props.disable) { - // context.emit('clickButton', $event); - // } - // } - - function clickEvent($event: Event) { - $event.stopPropagation(); - props.showPanel = !props.showPanel; - // body添加面板 - if (props.showPanel) { - this.appendBody(); - this.event = $event; - // 绑定相应的事件 - this.ngZone.runOutsideAngular(() => { - this.bindMenuMouseenter(); - this.bindiMenuMouseleave(); - this.mouseNotEnterLeave(); - }); - } - // 面板显示 脏值检测 - this.changeRef.detectChanges(); - this.changeRef.markForCheck(); - context.emit('changeState', props.showPanel); - } - - /* 按钮触发事件 */ - function toggle($event: any, btn) { - $event.stopPropagation(); - if (btn.disabled) return; - props.showPanel = false; - // 关闭下拉按钮面板 脏值检测 - this.changeRef.detectChanges(); - this.changeRef.markForCheck(); - context.emit('change', btn.id); - context.emit('click', btn); - } - - /* 显示出来的按钮组 */ - function flatButtons() { - return props.data && props.data.slice(0, this.count); - } - - function dpButtons() { - return props.data && props.data.slice(this.count); - } - - // 下拉按钮显示到body中 可改变面板方向 + /* 下拉按钮显示到body中 可改变面板方向*/ function appendBody() { - if (this.dpMenu.nativeElement) { + if (dpMenu.value) { // 添加到body 便于全部显示 - document.body.appendChild(this.dpMenu.nativeElement); + document.body.appendChild(dpMenu.value); } } + /* flag true */ + function changeFlagToTrue() { + showPanel.value = true; + } + + /* flag false */ + function changeFlagToFalse() { + showPanel.value = false; + } + /** 鼠标未移入下拉列表后一段时间关闭 */ + function unbindMenuMouseleave() { + if (showPanel.value && !mouseEnter) { + setTimeout(() => { + if (showPanel.value && !mouseEnter) { + changeFlagToFalse(); + mouseEnter = false; + } + }, 1000); + } + } + /* 鼠标离开时 关闭menu */ + function mouseLeave() { + setTimeout(() => { + if (showPanel.value) { + // close(); + changeFlagToFalse(); + mouseEnter = false; + } + }, 1000); + } + + /* 绑定下拉面板鼠标进入事件 */ + function bindMenuMouseenter() { + changeFlagToTrue(); + mouseEnter = true; + } + /* 绑定下拉面板鼠标离开事件 */ + function bindMenuMouseleave() { + mouseLeave(); + } + + /** + * 确认参照的边界 + */ + function getReferencePosition() { + let rRight = document.documentElement.clientWidth; + let rBottom = document.documentElement.clientHeight; + let rTop = 0; + let rLeft = 0; + // 横向参照 + if (props.rectifyReferenceH) { + const elemH = document.querySelector('props.rectifyReferenceH'); + if (elemH) { + rRight = elemH.getBoundingClientRect().right; + rLeft = elemH.getBoundingClientRect().left; + } + } + // 纵向参照 + if (props.rectifyReferenceV) { + const elemV = document.querySelector('props.rectifyReferenceV'); + if (elemV) { + rBottom = elemV.getBoundingClientRect().bottom; + rTop = elemV.getBoundingClientRect().top; + } + } + return { top: rTop, left: rLeft, right: rRight, bottom: rBottom }; + } /** * 当下拉超出边界时 转换方向, @@ -75,97 +94,97 @@ export function useButtonGroup(props: ButtonGroupProps, context: SetupContext): if (!props.autoRectify) { return; } - const referPosition = this.getReferencePosition(); - let newPlacement = props.realPlacement; + const referPosition = getReferencePosition(); + let newPlacement = realPlacement; if (newPlacement.indexOf('bottom') > -1) { - if (this._menuHeight > referPosition.bottom - btnSize.bottom) { + if (_menuHeight > referPosition.bottom - btnSize.bottom) { newPlacement = newPlacement.replace('bottom', 'top'); } } else if (newPlacement.indexOf('top') > -1) { - if (this._menuHeight > btnSize.top - referPosition.top) { + if (_menuHeight > btnSize.top - referPosition.top) { newPlacement = newPlacement.replace('top', 'bottom'); } } if (newPlacement.indexOf('left') > -1) { - if (this._menuWidth > btnSize.left - referPosition.left) { + if (_menuWidth > btnSize.left - referPosition.left) { newPlacement = newPlacement.replace('left', 'right'); } } else if (newPlacement.indexOf('right') > -1) { - if (this._menuWidth > referPosition.right - btnSize.right) { + if (_menuWidth > referPosition.right - btnSize.right) { newPlacement = newPlacement.replace('right', 'left'); } } - this.rectifyPlacement = newPlacement; + realPlacement = newPlacement; } - /** - * 确认参照的边界 + /* + * 计算的位置区分忒细化 */ - function getReferencePosition() { - let rRight = document.documentElement.clientWidth; - let rBottom = document.documentElement.clientHeight; - let rTop = 0; - let rLeft = 0; - // 横向参照 - if (props.rectifyReferenceH) { - rRight = props.rectifyReferenceH.getBoundingClientRect().right; - rLeft = props.rectifyReferenceH.getBoundingClientRect().left; + function changePosition(btnSize: any) { + let rplacement = ''; + if (props.autoRectify) { + rplacement = props.rectifyPlacement; + } else { + rplacement = realPlacement; } - // 纵向参照 - if (props.rectifyReferenceV) { - rBottom = props.rectifyReferenceV.getBoundingClientRect().bottom; - rTop = props.rectifyReferenceV.getBoundingClientRect().top; + let styleTop = 0; + let styleLeft = 0; + if (rplacement.indexOf('top') > -1) { + styleTop = btnSize.top - _menuHeight; + } else if (rplacement.indexOf('bottom') > -1) { + styleTop = btnSize.bottom; } - return { top: rTop, left: rLeft, right: rRight, bottom: rBottom }; + if (rplacement.indexOf('right') > -1) { + styleLeft = btnSize.right; + } else if (rplacement.indexOf('left') > -1) { + styleLeft = btnSize.left - _menuWidth; + } + // 开头 + if (rplacement.indexOf('-top') > -1) { + styleTop -= btnSize.height; + } else if (rplacement.indexOf('-bottom') > -1) { + styleTop += btnSize.height; + } + dpMenu.value.style.top = styleTop + 'px'; + dpMenu.value.style.left = styleLeft + 'px'; } - /** - * 变化对应的class - * @param position - */ - function _getClsName(position) { - let className = ''; - switch (position) { - case 'top-right': - case 'top': - // 朝上,朝上-朝右 - className = 'dropup'; - break; - case 'top-left': - // 朝上-朝左 - className = 'dropup-left'; - break; - case 'left-bottom': - case 'left': - // 横向——朝左——朝下 - className = 'dropleft'; - break; - case 'left-top': - // 横向——朝左——朝上 - className = 'dropleft-up'; - break; - case 'right-bottom': - case 'right': - // 横向——朝右——朝下 - className = 'dropright'; - break; - case 'right-top': - // 横向——朝右——朝上 - className = 'dropright-up'; - break; - case 'bottom-left': - // 朝下——朝左 - className = 'dropdown-left'; - break; - case 'bottom-right': - className = 'dropdown'; - break; - default: - // 朝下,朝下——朝右 - className = 'dropdown'; + /* 动态指定menu在body中的位置 */ + async function setPosition() { + await nextTick(); + // 下拉按钮 + const btnSize = dpBtn.value.getBoundingClientRect(); + // 下拉面板 + const menuRect = dpMenu.value.getBoundingClientRect(); + _menuHeight = menuRect.height; + _menuWidth = menuRect.width; + // 如果要自动纠正方向 + if (props.autoRectify) { + changePlacement(btnSize); } - return className; + changePosition(btnSize); } - function getRealPlacement(pment) { + /** 展示下拉列表 */ + function clickEvent($event: Event) { + $event.stopPropagation(); + showPanel.value = !showPanel.value; + // body添加面板 + if (showPanel.value) { + appendBody(); + setPosition(); + } + context.emit('changeState', showPanel.value); + } + + /* 按钮触发事件 */ + function toggle($event: any, btn: any) { + $event.stopPropagation(); + if (btn.disabled) return; + showPanel.value = false; + context.emit('change', btn.id); + context.emit('click', btn); + } + + function getRealPlacement(pment: any) { let result = 'bottom-right'; switch (pment) { case 'top': @@ -183,147 +202,16 @@ export function useButtonGroup(props: ButtonGroupProps, context: SetupContext): default: result = pment; } - return result; - } - - /* - * 计算的位置区分忒细化 - */ - function changePosition(btnSize: any) { - let rplacement = ''; - if (props.autoRectify) { - rplacement = this.rectifyPlacement; - } else { - rplacement = props.realPlacement; - } - let styleTop = 0; - let styleLeft = 0; - if (rplacement.indexOf('top') > -1) { - styleTop = btnSize.top - this._menuHeight; - } else if (rplacement.indexOf('bottom') > -1) { - styleTop = btnSize.bottom; - } - if (rplacement.indexOf('right') > -1) { - styleLeft = btnSize.right; - } else if (rplacement.indexOf('left') > -1) { - styleLeft = btnSize.left - this._menuWidth; - } - // 开头 - if (rplacement.indexOf('-top') > -1) { - styleTop -= btnSize.height; - } else if (rplacement.indexOf('-bottom') > -1) { - styleTop += btnSize.height; - } - this.dpMenu.nativeElement.style.top = styleTop + 'px'; - this.dpMenu.nativeElement.style.left = styleLeft + 'px'; - } - - /* 绑定下拉面板鼠标进入事件 */ - function bindMenuMouseenter() { - this.mouseenterEvent = this.changeFlagToTrue.bind(this); - this.dpMenu.nativeElement.addEventListener('mouseenter', this.mouseenterEvent); - } - - /* 绑定下拉面板鼠标离开事件 */ - function bindiMenuMouseleave() { - this.mouseleaveEvent = this.mouseLeave.bind(this); - this.dpMenu.nativeElement.addEventListener('mouseleave', this.mouseleaveEvent); - } - - /* 绑定点击面板区域之外触发的事件 */ - // bindDocClick() { - // this.documentClickEvent = this.clickDoc.bind(this); - // document.addEventListener('click', this.documentClickEvent); - // } - - /* 解绑事件 */ - function unbindMenuMouseenter() { - if (this.mouseenterEvent) { - this.dpMenu.nativeElement.removeEventListener('mouseenter', this.mouseenterEvent); - } - } - function unbindiMenuMouseleave() { - if (this.mouseleaveEvent) { - this.dpMenu.nativeElement.removeEventListener('mouseleave', this.mouseleaveEvent); - } - } - // unbindDocClick() { - // if (this.documentClickEvent) { - // document.removeEventListener('click', this.documentClickEvent); - // } - // } - - /* flag true */ - function changeFlagToTrue() { - props.dpFlag = true; - } - - /* flag false */ - function changeFlagToFalse() { - props.dpFlag = false; - } - - /* 鼠标离开时 关闭menu */ - function mouseLeave() { - if (props.dpFlag) { - this.changeFlagToFalse(); - // this.unbindDocClick(); - this.unbindiMenuMouseleave(); - this.unbindMenuMouseenter(); - this.close(); - if (this.setTimeObj) { - this.ngZone.runOutsideAngular(() => { - clearTimeout(this.setTimeObj); - }); - } - } - } - - /* 鼠标没有进入到面板 一段时间后面板自动消失 */ - function mouseNotEnterLeave() { - this.ngZone.runOutsideAngular(() => { - this.setTimeObj = setTimeout(() => { - if (!props.dpFlag) { - this.changeFlagToFalse(); - // this.unbindDocClick(); - this.unbindiMenuMouseleave(); - this.unbindMenuMouseenter(); - this.close(); - if (this.setTimeObj) { - clearTimeout(this.setTimeObj); - } - } - }, 2000); - }); - } - - /* 关闭下拉面板 */ - function close() { - props.showPanel = false; - // 关闭下拉按钮面板 脏值检测 - if (!this.changeRef.destroyed) { - this.changeRef.detectChanges(); - this.changeRef.markForCheck(); - } - this.dpBtn.nativeElement.blur(); - } - - /* 动态指定menu在body中的位置 */ - function setPosition(e) { - // 下拉按钮 - const btnSize = this.dpBtn.nativeElement.getBoundingClientRect(); - // 下拉面板 - const menuRect = this.dpMenu.nativeElement.getBoundingClientRect(); - this._menuHeight = menuRect.height; - this._menuWidth = menuRect.width; - // 如果要自动纠正方向 - if (props.autoRectify) { - this.changePlacement(btnSize); - } - this.changePosition(btnSize); + realPlacement = result; } return { - clickEvent + showPanel, + clickEvent, + toggle, + getRealPlacement, + bindMenuMouseenter, + unbindMenuMouseleave, + bindMenuMouseleave }; } diff --git a/packages/ui-vue/components/button/src/composition/use-button.ts b/packages/ui-vue/components/button/src/composition/use-button.ts index 34aab80..96667cb 100644 --- a/packages/ui-vue/components/button/src/composition/use-button.ts +++ b/packages/ui-vue/components/button/src/composition/use-button.ts @@ -1,6 +1,6 @@ import { UseButton } from './types'; import { ButtonProps } from '../button.props'; -import { computed, SetupContext } from 'vue'; +import { SetupContext } from 'vue'; export function useButton(props: ButtonProps, context: SetupContext): UseButton { diff --git a/packages/ui-vue/src/App.vue b/packages/ui-vue/src/App.vue index 4179ab1..8c09605 100644 --- a/packages/ui-vue/src/App.vue +++ b/packages/ui-vue/src/App.vue @@ -7,6 +7,7 @@ import Avatar from "./components/avatar.vue"; import Tabs from "./components/tabs.vue"; import ButtonEdit from "./components/button-edit.vue"; import FButton from "./components/button.vue"; +import FButtonGroup from "./components/button-group.vue"; import Switch from "./components/switch.vue"; import RadioGroup from "./components/radio-group.vue"; import Section from "./components/section.vue"; @@ -37,6 +38,7 @@ const canAutoComplete = ref(false); +
    diff --git a/packages/ui-vue/src/components/button-group.vue b/packages/ui-vue/src/components/button-group.vue new file mode 100644 index 0000000..5107c40 --- /dev/null +++ b/packages/ui-vue/src/components/button-group.vue @@ -0,0 +1,59 @@ + + + diff --git a/packages/ui-vue/src/components/button.vue b/packages/ui-vue/src/components/button.vue index fd44c7d..020c8be 100644 --- a/packages/ui-vue/src/components/button.vue +++ b/packages/ui-vue/src/components/button.vue @@ -3,24 +3,24 @@ import { ref } from 'vue'; import FButton from '../../components/button/src/button.component'; -const disable = ref(false); +const disabled = ref(false);