UNPKG

naive-ui

Version:

A Vue 3 Component Library. Fairly Complete, Theme Customizable, Uses TypeScript, Fast

322 lines 11.2 kB
import { depx, pxfy } from 'seemly'; import { useMergedState } from 'vooks'; import { computed, defineComponent, h, ref, toRef, watchEffect } from 'vue'; import { NBaseLoading, NIconSwitchTransition } from "../../_internal/index.mjs"; import { useConfig, useFormItem, useTheme, useThemeClass } from "../../_mixins/index.mjs"; import { call, createKey, isSlotEmpty, resolveWrappedSlot, warnOnce } from "../../_utils/index.mjs"; import { switchLight } from "../styles/index.mjs"; import style from "./styles/index.cssr.mjs"; export const switchProps = Object.assign(Object.assign({}, useTheme.props), { size: { type: String, default: 'medium' }, value: { type: [String, Number, Boolean], default: undefined }, loading: Boolean, defaultValue: { type: [String, Number, Boolean], default: false }, disabled: { type: Boolean, default: undefined }, round: { type: Boolean, default: true }, 'onUpdate:value': [Function, Array], onUpdateValue: [Function, Array], checkedValue: { type: [String, Number, Boolean], default: true }, uncheckedValue: { type: [String, Number, Boolean], default: false }, railStyle: Function, rubberBand: { type: Boolean, default: true }, /** @deprecated */ onChange: [Function, Array] }); let supportCssMax; export default defineComponent({ name: 'Switch', props: switchProps, slots: Object, setup(props) { if (process.env.NODE_ENV !== 'production') { watchEffect(() => { if (props.onChange) { warnOnce('switch', '`on-change` is deprecated, please use `on-update:value` instead.'); } }); } if (supportCssMax === undefined) { if (typeof CSS !== 'undefined') { if (typeof CSS.supports !== 'undefined') { supportCssMax = CSS.supports('width', 'max(1px)'); } else { supportCssMax = false; } } else { // If you are using SSR, we assume that you are targeting browsers with // recent versions supportCssMax = true; } } const { mergedClsPrefixRef, inlineThemeDisabled } = useConfig(props); const themeRef = useTheme('Switch', '-switch', style, switchLight, props, mergedClsPrefixRef); const formItem = useFormItem(props); const { mergedSizeRef, mergedDisabledRef } = formItem; const uncontrolledValueRef = ref(props.defaultValue); const controlledValueRef = toRef(props, 'value'); const mergedValueRef = useMergedState(controlledValueRef, uncontrolledValueRef); const checkedRef = computed(() => { return mergedValueRef.value === props.checkedValue; }); const pressedRef = ref(false); const focusedRef = ref(false); const mergedRailStyleRef = computed(() => { const { railStyle } = props; if (!railStyle) return undefined; return railStyle({ focused: focusedRef.value, checked: checkedRef.value }); }); function doUpdateValue(value) { const { 'onUpdate:value': _onUpdateValue, onChange, onUpdateValue } = props; const { nTriggerFormInput, nTriggerFormChange } = formItem; if (_onUpdateValue) call(_onUpdateValue, value); if (onUpdateValue) call(onUpdateValue, value); if (onChange) call(onChange, value); uncontrolledValueRef.value = value; nTriggerFormInput(); nTriggerFormChange(); } function doFocus() { const { nTriggerFormFocus } = formItem; nTriggerFormFocus(); } function doBlur() { const { nTriggerFormBlur } = formItem; nTriggerFormBlur(); } function handleClick() { if (props.loading || mergedDisabledRef.value) return; if (mergedValueRef.value !== props.checkedValue) { doUpdateValue(props.checkedValue); } else { doUpdateValue(props.uncheckedValue); } } function handleFocus() { focusedRef.value = true; doFocus(); } function handleBlur() { focusedRef.value = false; doBlur(); pressedRef.value = false; } function handleKeyup(e) { if (props.loading || mergedDisabledRef.value) return; if (e.key === ' ') { if (mergedValueRef.value !== props.checkedValue) { doUpdateValue(props.checkedValue); } else { doUpdateValue(props.uncheckedValue); } pressedRef.value = false; } } function handleKeydown(e) { if (props.loading || mergedDisabledRef.value) return; if (e.key === ' ') { e.preventDefault(); pressedRef.value = true; } } const cssVarsRef = computed(() => { const { value: size } = mergedSizeRef; const { self: { opacityDisabled, railColor, railColorActive, buttonBoxShadow, buttonColor, boxShadowFocus, loadingColor, textColor, iconColor, [createKey('buttonHeight', size)]: buttonHeight, [createKey('buttonWidth', size)]: buttonWidth, [createKey('buttonWidthPressed', size)]: buttonWidthPressed, [createKey('railHeight', size)]: railHeight, [createKey('railWidth', size)]: railWidth, [createKey('railBorderRadius', size)]: railBorderRadius, [createKey('buttonBorderRadius', size)]: buttonBorderRadius }, common: { cubicBezierEaseInOut } } = themeRef.value; let offset; let height; let width; if (supportCssMax) { offset = `calc((${railHeight} - ${buttonHeight}) / 2)`; height = `max(${railHeight}, ${buttonHeight})`; width = `max(${railWidth}, calc(${railWidth} + ${buttonHeight} - ${railHeight}))`; } else { offset = pxfy((depx(railHeight) - depx(buttonHeight)) / 2); height = pxfy(Math.max(depx(railHeight), depx(buttonHeight))); width = depx(railHeight) > depx(buttonHeight) ? railWidth : pxfy(depx(railWidth) + depx(buttonHeight) - depx(railHeight)); } return { '--n-bezier': cubicBezierEaseInOut, '--n-button-border-radius': buttonBorderRadius, '--n-button-box-shadow': buttonBoxShadow, '--n-button-color': buttonColor, '--n-button-width': buttonWidth, '--n-button-width-pressed': buttonWidthPressed, '--n-button-height': buttonHeight, '--n-height': height, '--n-offset': offset, '--n-opacity-disabled': opacityDisabled, '--n-rail-border-radius': railBorderRadius, '--n-rail-color': railColor, '--n-rail-color-active': railColorActive, '--n-rail-height': railHeight, '--n-rail-width': railWidth, '--n-width': width, '--n-box-shadow-focus': boxShadowFocus, '--n-loading-color': loadingColor, '--n-text-color': textColor, '--n-icon-color': iconColor }; }); const themeClassHandle = inlineThemeDisabled ? useThemeClass('switch', computed(() => { return mergedSizeRef.value[0]; }), cssVarsRef, props) : undefined; return { handleClick, handleBlur, handleFocus, handleKeyup, handleKeydown, mergedRailStyle: mergedRailStyleRef, pressed: pressedRef, mergedClsPrefix: mergedClsPrefixRef, mergedValue: mergedValueRef, checked: checkedRef, mergedDisabled: mergedDisabledRef, cssVars: inlineThemeDisabled ? undefined : cssVarsRef, themeClass: themeClassHandle === null || themeClassHandle === void 0 ? void 0 : themeClassHandle.themeClass, onRender: themeClassHandle === null || themeClassHandle === void 0 ? void 0 : themeClassHandle.onRender }; }, render() { const { mergedClsPrefix, mergedDisabled, checked, mergedRailStyle, onRender, $slots } = this; onRender === null || onRender === void 0 ? void 0 : onRender(); const { checked: checkedSlot, unchecked: uncheckedSlot, icon: iconSlot, 'checked-icon': checkedIconSlot, 'unchecked-icon': uncheckedIconSlot } = $slots; const hasIcon = !(isSlotEmpty(iconSlot) && isSlotEmpty(checkedIconSlot) && isSlotEmpty(uncheckedIconSlot)); return h("div", { role: "switch", "aria-checked": checked, class: [`${mergedClsPrefix}-switch`, this.themeClass, hasIcon && `${mergedClsPrefix}-switch--icon`, checked && `${mergedClsPrefix}-switch--active`, mergedDisabled && `${mergedClsPrefix}-switch--disabled`, this.round && `${mergedClsPrefix}-switch--round`, this.loading && `${mergedClsPrefix}-switch--loading`, this.pressed && `${mergedClsPrefix}-switch--pressed`, this.rubberBand && `${mergedClsPrefix}-switch--rubber-band`], tabindex: !this.mergedDisabled ? 0 : undefined, style: this.cssVars, onClick: this.handleClick, onFocus: this.handleFocus, onBlur: this.handleBlur, onKeyup: this.handleKeyup, onKeydown: this.handleKeydown }, h("div", { class: `${mergedClsPrefix}-switch__rail`, "aria-hidden": "true", style: mergedRailStyle }, resolveWrappedSlot(checkedSlot, checkedSlotChildren => resolveWrappedSlot(uncheckedSlot, uncheckedSlotChildren => { if (checkedSlotChildren || uncheckedSlotChildren) { return h("div", { "aria-hidden": true, class: `${mergedClsPrefix}-switch__children-placeholder` }, h("div", { class: `${mergedClsPrefix}-switch__rail-placeholder` }, h("div", { class: `${mergedClsPrefix}-switch__button-placeholder` }), checkedSlotChildren), h("div", { class: `${mergedClsPrefix}-switch__rail-placeholder` }, h("div", { class: `${mergedClsPrefix}-switch__button-placeholder` }), uncheckedSlotChildren)); } return null; })), h("div", { class: `${mergedClsPrefix}-switch__button` }, resolveWrappedSlot(iconSlot, icon => resolveWrappedSlot(checkedIconSlot, checkedIcon => resolveWrappedSlot(uncheckedIconSlot, uncheckedIcon => { return h(NIconSwitchTransition, null, { default: () => this.loading ? h(NBaseLoading, { key: "loading", clsPrefix: mergedClsPrefix, strokeWidth: 20 }) : this.checked && (checkedIcon || icon) ? h("div", { class: `${mergedClsPrefix}-switch__button-icon`, key: checkedIcon ? 'checked-icon' : 'icon' }, checkedIcon || icon) : !this.checked && (uncheckedIcon || icon) ? h("div", { class: `${mergedClsPrefix}-switch__button-icon`, key: uncheckedIcon ? 'unchecked-icon' : 'icon' }, uncheckedIcon || icon) : null }); }))), resolveWrappedSlot(checkedSlot, children => children && h("div", { key: "checked", class: `${mergedClsPrefix}-switch__checked` }, children)), resolveWrappedSlot(uncheckedSlot, children => children && h("div", { key: "unchecked", class: `${mergedClsPrefix}-switch__unchecked` }, children))))); } });