| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486 |
- <template>
- <Touchable
- :innerStyle="{
- ...currentStyle.style,
- ...innerStyle,
- }"
- :innerClass="['nana-button', props.block ? 'nana-button-block' : 'nana-button-auto']"
- center
- direction="row"
- v-bind="viewProps"
- :pressedColor="finalPressedColor"
- :touchable="touchable && !loading"
- :gap="iconMargin"
- @state="(v) => state = v"
- @click="(e) => emit('click', e)"
- >
- <slot name="leftIcon">
- <ActivityIndicator
- v-if="loading"
- :size="selectStyleType(size, 'medium', FonstSizes)"
- :color="themeContext.resolveThemeColor(loadingColor) || currentStyle.color"
- />
- <Icon
- v-else-if="icon"
- :icon="icon"
- :size="selectStyleType(size, 'medium', FonstSizes)"
- :color="currentStyle.color"
- v-bind="iconProps"
- />
- </slot>
- <slot>
- <Text
- :color="textColorFinal"
- :fontSize="selectStyleType(size, 'medium', FonstSizes)"
- :fontWeight="type === 'text' ? 'bold' : undefined"
- :innerStyle="textStyle"
- :text="currentText"
- />
- </slot>
- <slot name="rightIcon">
- <Icon
- v-if="rightIcon"
- :icon="rightIcon"
- :size="selectStyleType(size, 'medium', FonstSizes)"
- :color="currentStyle.color"
- v-bind="rightIconProps"
- />
- </slot>
- </Touchable>
- </template>
- <script setup lang="ts">
- import { computed, inject, ref } from 'vue';
- import { propGetThemeVar, useTheme, type ViewStyle } from '../theme/ThemeDefine';
- import { configPadding, DynamicColor, DynamicSize, selectStyleType } from '../theme/ThemeTools';
- import type { IconProps } from './Icon.vue';
- import type { FlexProps } from '../layout/FlexView.vue';
- import Text from './Text.vue';
- import ActivityIndicator from './ActivityIndicator.vue';
- import Icon from './Icon.vue';
- import Touchable from '../feedback/Touchable.vue';
- import type { ButtonGroupContext } from './ButtonGroup.vue';
- import { useChildLinkChild } from '../composeabe/ChildItem';
- export type ButtonType = 'default'|'primary'|'success'|'warning'|'danger'|'custom'|'text';
- export type ButtomSizeType = 'small'|'medium'|'large'|'larger'|'mini';
- export interface ButtonProp {
- /**
- * 按钮文字
- */
- text?: string,
- /**
- * 按钮支持 default、primary、success、warning、danger、custom 自定义 六种类型
- * @default 'default'
- */
- type?: ButtonType,
- /**
- * 占满父级主轴
- * @default false
- */
- block?: boolean,
- /**
- * * plain 将按钮设置为朴素按钮,朴素按钮的文字为按钮颜色,背景为白色。
- * * light 将按钮设置为浅色按钮,浅色按钮的文字为按钮颜色,背景为主色调浅色。
- * @default 'default'
- */
- scheme?: 'default'|'plain'|'light',
- /**
- * 通过 loading 属性设置按钮为加载状态,加载状态下默认会隐藏按钮文字,可以通过 loadingText 设置加载状态下的文字。
- * @default false
- */
- loading?: boolean,
- /**
- * 加载状态下的文字。
- * @default false
- */
- loadingText?: string,
- /**
- * 加载状态圆圈颜色
- */
- loadingColor?: string,
- /**
- * 按钮形状 通过 square 设置方形按钮,通过 round 设置圆形按钮。
- * @default 'round'
- */
- shape?: 'square'|'round',
- /**
- * 左侧图标。支持 Icon 组件里的所有图标,也可以传入图标的图片 URL(http/https)。
- */
- icon?: string,
- /**
- * 当使用图标时,左侧图标的附加属性
- */
- iconProps?: IconProps;
- /**
- * 图标与文字之间的间距
- * @default 10
- */
- iconMargin?: number|string;
- /**
- * 右侧图标。支持 Icon 组件里的所有图标,也可以传入图标的图片 URL(http/https)。
- */
- rightIcon?: string,
- /**
- * 当使用图标时,右侧图标的附加属性
- */
- rightIconProps?: IconProps;
- /**
- * 是否可以点击
- * @default true
- */
- touchable?: boolean,
- /**
- * 当按扭为round圆形按扭时的圆角大小。
- * @default 5
- */
- radius?: number|string,
- /**
- * 按钮尺寸. 支持 large、medium、small、mini 四种尺寸。
- * @default 'medium'
- */
- size?: ButtomSizeType,
- /**
- * 通过 color 属性可以自定义按钮的背景颜色,仅在 type 为 `custom` 时有效
- * @default grey
- */
- color?: string;
- /**
- * 按钮文字的颜色。
- */
- textColor?: string;
- /**
- * 按下时按钮文字的颜色。
- */
- pressedTextColor?: string;
- /**
- * 按钮文字的样式。
- */
- textStyle?: object;
- /**
- * 按下时的颜色,仅在 type 为 `custom` 时有效
- * @default PressedColor(primary)
- */
- pressedColor?: string;
- /**
- * 禁用时的颜色,仅在 type 为 `custom` 时有效
- * @default grey
- */
- disabledColor?: string;
- /**
- * 自定义样式
- */
- innerStyle?: object,
- /**
- * 按扭的文字,等同于 text 属性
- */
- children?: string;
- /**
- * 强制控制按钮的边距
- * * 如果是数字,则设置所有方向边距
- * * 两位数组 [vetical,horizontal]
- * * 四位数组 [top,right,down,left]
- */
- padding?: number|number[],
- /**
- * 外层容器参数
- */
- viewProps?: FlexProps,
- formType?: string;
- openType?: string;
- appParameter?: string;
- }
- defineOptions({
- options: {
- styleIsolation: "shared",
- virtualHost: true,
- }
- })
- const emit = defineEmits([
- 'click',
- ])
- const themeContext = useTheme();
- const props = withDefaults(defineProps<ButtonProp>(), {
- touchable: true,
- loading: false,
- color: () => propGetThemeVar('ButtonColor', 'primary'),
- pressedColor: () => propGetThemeVar('ButtonPressedColor', 'pressed.primary'),
- disabledColor: () => propGetThemeVar('ButtonDisabledColor', 'grey'),
- type: () => propGetThemeVar('ButtonType', 'default'),
- size: () => propGetThemeVar('ButtonSize', 'medium'),
- block: () => propGetThemeVar('ButtonBlock', false),
- radius: () => propGetThemeVar('ButtonRadius', 16),
- iconMargin: () => propGetThemeVar('ButtonIconMargin', 10),
- shape: () => propGetThemeVar('ButtonShape', "round"),
- });
- const FonstSizes = computed(() => ({
- mini: themeContext.resolveThemeSize('ButtonMiniFonstSize', 'fontSize.xs'),
- small: themeContext.resolveThemeSize('ButtonSmallFonstSize', 'fontSize.sm'),
- medium: themeContext.resolveThemeSize('ButtonMediumFonstSize', 'fontSize.md'),
- large: themeContext.resolveThemeSize('ButtonLargeFonstSize', 'fontSize.lg'),
- larger: themeContext.resolveThemeSize('ButtonLargerFonstSize', 'fontSize.xl'),
- }));
- const themeVars = themeContext.getVars({
- ButtonBorderWidth: 1.5,
- ButtonDisableOpacity: 0.5,
- });
- const themeStyles = themeContext.useThemeStyles({
- plainButtonDefault: {
- borderStyle: 'solid',
- borderWidth: DynamicSize('ButtonBorderWidth', 1.5),
- borderColor: DynamicColor('ButtonPlainDefaultBorderColor', 'border.default'),
- color: DynamicColor('ButtonPlainDefaultColor', 'text.content'),
- },
- plainButtonPrimary: {
- borderStyle: 'solid',
- borderWidth: DynamicSize('ButtonBorderWidth', 1.5),
- borderColor: DynamicColor('ButtonPlainPrimaryBorderColor', 'primary'),
- color: DynamicColor('ButtonPlainPrimaryColor', 'primary'),
- },
- plainButtonSuccess: {
- borderStyle: 'solid',
- borderWidth: DynamicSize('ButtonBorderWidth', 1.5),
- borderColor: DynamicColor('ButtonPlainSuccessBorderColor', 'success'),
- color: DynamicColor('ButtonPlainSuccessColor', 'success'),
- },
- plainButtonWarning: {
- borderStyle: 'solid',
- borderWidth: DynamicSize('ButtonBorderWidth', 1.5),
- borderColor: DynamicColor('ButtonPlainWarningBorderColor', 'warning'),
- color: DynamicColor('ButtonPlainWarningColor', 'warning'),
- },
- plainButtonDanger: {
- borderStyle: 'solid',
- borderWidth: DynamicSize('ButtonBorderWidth', 1.5),
- borderColor: DynamicColor('ButtonPlainDangerBorderColor', 'danger'),
- color: DynamicColor('ButtonPlainDangerColor', 'danger'),
- },
- lightButtonDefault: {
- backgroundColor: DynamicColor('ButtonLightDefaultBackgroundColor', 'background.button'),
- color: DynamicColor('ButtonLightDefaultColor', 'text.content'),
- },
- lightButtonPrimary: {
- backgroundColor: DynamicColor('ButtonLightPrimaryBackgroundColor', 'background.primary'),
- color: DynamicColor('ButtonLightPrimaryColor', 'text.primary'),
- },
- lightButtonSuccess: {
- backgroundColor: DynamicColor('ButtonLightSuccessBackgroundColor', 'background.success'),
- color: DynamicColor('ButtonLightSuccessColor', 'text.success'),
- },
- lightButtonWarning: {
- backgroundColor: DynamicColor('ButtonLightWarningBackgroundColor', 'background.warning'),
- color: DynamicColor('ButtonLightWarningColor', 'text.warning'),
- },
- lightButtonDanger: {
- backgroundColor: DynamicColor('ButtonLightDangerBackgroundColor', 'background.danger'),
- color: DynamicColor('ButtonLightDangerColor', 'text.danger'),
- },
- buttonSizeLarger: {
- paddingVertical: DynamicSize('ButtonPaddingVerticalLarger', 25),
- paddingHorizontal: DynamicSize('ButtonPaddingHorizontalLarger', 30),
- },
- buttonSizeLarge: {
- paddingVertical: DynamicSize('ButtonPaddingVerticalLarge', 20),
- paddingHorizontal: DynamicSize('ButtonPaddingHorizontalLarge', 25),
- },
- buttonSizeMedium: {
- paddingVertical: DynamicSize('ButtonPaddingVerticalMedium', 15),
- paddingHorizontal: DynamicSize('ButtonPaddingHorizontalMedium', 20),
- },
- buttonSizeSmall: {
- paddingVertical: DynamicSize('ButtonPaddingVerticalSmall', 10),
- paddingHorizontal: DynamicSize('ButtonPaddingHorizontalSmall', 15),
- },
- buttonSizeMini: {
- paddingVertical: DynamicSize('ButtonPaddingVerticalMini', 5),
- paddingHorizontal: DynamicSize('ButtonPaddingHorizontalMini', 6),
- },
- buttonDefault: {
- backgroundColor: DynamicColor('ButtonDefaultBackgroundColor', 'button'),
- color: DynamicColor('ButtonDefaultColor', 'black'),
- },
- buttonPrimary: {
- backgroundColor: DynamicColor('ButtonPrimaryBackgroundColor', 'primary'),
- color: DynamicColor('ButtonPrimaryColor', 'white'),
- },
- buttonSuccess: {
- backgroundColor: DynamicColor('ButtonSuccessBackgroundColor', 'success'),
- color: DynamicColor('ButtonSuccessColor', 'white'),
- },
- buttonWarning: {
- backgroundColor: DynamicColor('ButtonWarningBackgroundColor', 'warning'),
- color: DynamicColor('ButtonWarningColor', 'white'),
- },
- buttonDanger: {
- backgroundColor: DynamicColor('ButtonDangerBackgroundColor', 'danger'),
- color: DynamicColor('ButtonDangerColor', 'white'),
- },
- });
- //按钮样式生成
- const currentStyle = computed(() => {
- const colorStyle = selectStyleType<ViewStyle, ButtonType>(props.type, 'default',
- selectStyleType(props.scheme, 'default', {
- default: {
- default: themeStyles.buttonDefault.value,
- primary: themeStyles.buttonPrimary.value,
- success: themeStyles.buttonSuccess.value,
- warning: themeStyles.buttonWarning.value,
- danger: themeStyles.buttonDanger.value,
- custom: {
- backgroundColor: themeContext.resolveThemeColor(touchable.value ? props.color : props.disabledColor),
- color: themeContext.resolveThemeColor(props.textColor),
- },
- text: {
- color: themeContext.resolveThemeColor(props.textColor),
- },
- },
- plain: {
- default: themeStyles.plainButtonDefault.value,
- primary: themeStyles.plainButtonPrimary.value,
- success: themeStyles.plainButtonSuccess.value,
- warning: themeStyles.plainButtonWarning.value,
- danger: themeStyles.plainButtonDanger.value,
- custom: {
- borderStyle: 'solid',
- borderWidth: themeContext.resolveThemeSize(themeVars.ButtonBorderWidth),
- borderColor: themeContext.resolveThemeColor(props.color),
- color: themeContext.resolveThemeColor(props.color),
- },
- text: {
- color: themeContext.resolveThemeColor(props.color),
- },
- },
- light: {
- default: themeStyles.lightButtonDefault.value,
- primary: themeStyles.lightButtonPrimary.value,
- success: themeStyles.lightButtonSuccess.value,
- warning: themeStyles.lightButtonWarning.value,
- danger: themeStyles.lightButtonDanger.value,
- custom: {
- backgroundColor: themeContext.resolveThemeColor(touchable.value ? props.color : props.disabledColor),
- color: themeContext.resolveThemeColor(props.textColor),
- },
- text: {
- color: themeContext.resolveThemeColor(props.color),
- },
- },
- })
- );
- const speicalStyle : ViewStyle = {
- opacity: touchable.value ? 1 : themeVars.ButtonDisableOpacity,
- borderRadius: props.shape === 'round' ? themeContext.resolveThemeSize(props.radius) : 0,
- };
- if (props.shape === 'round') {
- if (noLeftRadius.value) {
- speicalStyle.borderTopLeftRadius = 0;
- speicalStyle.borderBottomLeftRadius = 0;
- }
- if (noRightRadius.value) {
- speicalStyle.borderTopRightRadius = 0;
- speicalStyle.borderBottomRightRadius = 0;
- }
- }
- //自定义状态下的禁用颜色
- if (props.disabledColor && !touchable.value && props.type === 'custom')
- speicalStyle.backgroundColor = themeContext.resolveThemeColor(props.disabledColor);
- const sizeStyle = selectStyleType<ViewStyle, ButtomSizeType>(props.size, 'medium', {
- large: themeStyles.buttonSizeLarge.value,
- larger: themeStyles.buttonSizeLarger.value,
- medium: themeStyles.buttonSizeMedium.value,
- small: themeStyles.buttonSizeSmall.value,
- mini: themeStyles.buttonSizeMini.value,
- });
- //if (props.shape === 'round')
- // sizeStyle.paddingHorizontal = `calc(${sizeStyle.paddingHorizontal} + ${themeContext.resolveSize(props.radius / 4)})`;
- //内边距样式的强制设置
- configPadding(speicalStyle, themeContext, props.padding);
- return {
- color: (colorStyle).color,
- style: {
- ...colorStyle,
- ...sizeStyle,
- ...speicalStyle,
- },
- };
- });
- const state = ref('');
- const currentText = computed(() => (props.loading ? (props.loadingText || props.text) : props.text));
- const textColorFinal = computed(() => (
- state.value === 'active' ?
- themeContext.resolveThemeColor(props.pressedTextColor) :
- themeContext.resolveThemeColor(props.textColor)
- ) || currentStyle.value.color);
- const finalPressedColor = computed(() => {
- if (props.type === 'custom')
- return themeContext.resolveThemeColor(props.pressedColor);
- return (themeContext.resolveThemeColor((
- props.scheme === 'plain'
- || props.scheme === 'light'
- || props.type === 'text'
- ) ?
- 'pressed.notice' :
- 'pressed.' + props.type))
- });
- //按钮组的处理
- const buttonGroupContext = inject<ButtonGroupContext>('buttonGroupContext', undefined as any);
- const { position } = useChildLinkChild(() => buttonGroupContext?.getPosition());
- const noRightRadius = computed(() => buttonGroupContext?.mergeRadius.value && position.value !== buttonGroupContext.length.value - 1);
- const noLeftRadius = computed(() => buttonGroupContext?.mergeRadius.value && position.value !== 0);
- const touchable = computed(() => props.touchable !== false && !buttonGroupContext?.groupDisabled.value);
- </script>
- <style>
- .nana-button {
- width: auto;
- user-select: none;
- cursor: pointer;
- appearance: none;
- }
- .nana-button-inner {
- display: flex;
- flex-direction: row;
- justify-content: center;
- align-items: center;
- background-color: transparent;
- border: none;
- outline: none;
- line-height: auto;
- padding: 0;
- margin: 0;
- min-height: 0;
- }
- .nana-button-inner::after {
- display: none;
- }
- .nana-button-auto {
- flex-shrink: 0;
- flex-grow: 0;
- flex-basis: auto;
- }
- .nana-button-block {
- align-self: stretch;
- max-width: 100%;
- }
- </style>
|