!21 更新Notify组件

Merge pull request !21 from Sagi/feature/input-group
This commit is contained in:
Sagi 2022-09-26 15:59:15 +00:00 committed by Gitee
commit 797b4a66f1
No known key found for this signature in database
GPG Key ID: 173E9B9CA92EEF8F
8 changed files with 415 additions and 349 deletions

View File

@ -6,104 +6,107 @@ import { useClear } from './composition/use-clear';
import { useTextBox } from './composition/use-text-box';
export default defineComponent({
name: 'FButtonEdit',
props: buttonEditProps,
emits: [
'updateExtendInfo',
'clear',
'change',
'click',
'clickButton',
'blur',
'focus',
'mouseEnterIcon',
'mouseLeaveIcon',
'keyup',
'keydown',
'inputClick',
'input',
'update:modelValue',
],
setup(props: ButtonEditProps, context: SetupContext) {
const modelValue = ref(props.modelValue);
const { buttonClass, onClickButton, onMouseEnterButton, onMouseLeaveButton } = useButton(props, context);
const displayText = ref('');
const {
hasFocusedTextBox,
isTextBoxReadonly,
textBoxClass,
textBoxPlaceholder,
textBoxTitle,
onBlurTextBox,
onClickTextBox,
onFocusTextBox,
onInput,
onKeyDownTextBox,
onKeyUpTextBox,
onMouseDownTextBox,
onTextBoxValueChange,
} = useTextBox(props, context, modelValue, displayText);
name: 'FButtonEdit',
props: buttonEditProps,
emits: [
'updateExtendInfo',
'clear',
'change',
'click',
'clickButton',
'blur',
'focus',
'mouseEnterIcon',
'mouseLeaveIcon',
'keyup',
'keydown',
'inputClick',
'input',
'update:modelValue',
],
setup(props: ButtonEditProps, context: SetupContext) {
const modelValue = ref(props.modelValue);
const { buttonClass, onClickButton, onMouseEnterButton, onMouseLeaveButton } = useButton(props, context);
const displayText = ref('');
const {
hasFocusedTextBox,
isTextBoxReadonly,
textBoxClass,
textBoxPlaceholder,
textBoxTitle,
onBlurTextBox,
onClickTextBox,
onFocusTextBox,
onInput,
onKeyDownTextBox,
onKeyUpTextBox,
onMouseDownTextBox,
onTextBoxValueChange,
} = useTextBox(props, context, modelValue, displayText);
const { enableClearButton, showClearButton, onClearValue, onMouseEnterTextBox, onMouseLeaveTextBox } = useClear(
props,
context,
modelValue,
hasFocusedTextBox,
displayText
);
const { enableClearButton, showClearButton, onClearValue, onMouseEnterTextBox, onMouseLeaveTextBox } = useClear(
props,
context,
modelValue,
hasFocusedTextBox,
displayText
);
const inputGroupClass = computed(() => ({
'input-group': true,
'f-state-disable': props.disable,
'f-state-editable': props.editable && !props.disable && !props.readonly,
'f-state-readonly': props.readonly && !props.disable,
'f-state-focus': hasFocusedTextBox,
}));
const inputGroupClass = computed(() => ({
'input-group': true,
'f-state-disable': props.disable,
'f-state-editable': props.editable && !props.disable && !props.readonly,
'f-state-readonly': props.readonly && !props.disable,
'f-state-focus': hasFocusedTextBox,
}));
return () => {
return (
<div class="f-cmp-inputgroup" id={props.id}>
<div class={[props.customClass, inputGroupClass.value]} onMouseenter={onMouseEnterTextBox} onMouseleave={onMouseLeaveTextBox}>
<input
name="input-group-value"
autocomplete={'' + props.autoComplete}
class={textBoxClass.value}
disabled={props.disable}
maxlength={props.maxLength}
minlength={props.minLength}
placeholder={textBoxPlaceholder.value}
readonly={isTextBoxReadonly.value}
tabindex={props.tabIndex}
title={textBoxTitle.value}
type={props.inputType}
value={modelValue.value}
onBlur={onBlurTextBox}
onChange={onTextBoxValueChange}
onClick={onClickTextBox}
onFocus={onFocusTextBox}
onInput={onInput}
onKeydown={onKeyDownTextBox}
onKeyup={onKeyUpTextBox}
onMousedown={onMouseDownTextBox}
/>
<div class={buttonClass.value}>
{enableClearButton.value && (
<span class="input-group-text input-group-clear" v-show={showClearButton.value} onClick={onClearValue}>
<i class="f-icon modal_close"></i>
</span>
)}
{props.buttonContent && (
<span
class="input-group-text input-group-append-button"
onClick={onClickButton}
onMouseenter={onMouseEnterButton}
onMouseleave={onMouseLeaveButton}
v-html={props.buttonContent}></span>
)}
</div>
</div>
</div>
);
};
},
return () => {
return (
<div class="f-cmp-inputgroup" id={props.id}>
<div
class={[props.customClass, inputGroupClass.value]}
onMouseenter={onMouseEnterTextBox}
onMouseleave={onMouseLeaveTextBox}>
<input
name="input-group-value"
autocomplete={'' + props.autoComplete}
class={textBoxClass.value}
disabled={props.disable}
maxlength={props.maxLength}
minlength={props.minLength}
placeholder={textBoxPlaceholder.value}
readonly={isTextBoxReadonly.value}
tabindex={props.tabIndex}
title={textBoxTitle.value}
type={props.inputType}
value={modelValue.value}
onBlur={onBlurTextBox}
onChange={onTextBoxValueChange}
onClick={onClickTextBox}
onFocus={onFocusTextBox}
onInput={onInput}
onKeydown={onKeyDownTextBox}
onKeyup={onKeyUpTextBox}
onMousedown={onMouseDownTextBox}
/>
<div class={buttonClass.value}>
{enableClearButton.value && (
<span class="input-group-text input-group-clear" v-show={showClearButton.value} onClick={onClearValue}>
<i class="f-icon modal_close"></i>
</span>
)}
{props.buttonContent && (
<span
class="input-group-text input-group-append-button"
onClick={onClickButton}
onMouseenter={onMouseEnterButton}
onMouseleave={onMouseLeaveButton}
v-html={props.buttonContent}></span>
)}
</div>
</div>
</div>
);
};
},
});

View File

@ -3,79 +3,79 @@ import { ExtractPropTypes, PropType } from 'vue';
type TextAlignment = 'left' | 'center' | 'right';
export const buttonEditProps = {
/**
*
*/
id: String,
/**
* html标签
*/
buttonContent: { type: String, default: '<i class="f-icon f-icon-lookup"></i>' },
/**
*
*/
autoComplete: { type: Boolean, default: false },
/**
*
*/
customClass: { type: String, default: '' },
/**
*
*/
disable: { type: Boolean, default: false },
/**
*
*/
editable: { type: Boolean, default: true },
/**
*
*/
enableClear: { type: Boolean, default: false },
/**
*
*/
modelValue:{type:String,default:''},
/**
*
*/
readonly: { type: Boolean, default: false },
/**
*
*/
textAlign: { type: String as PropType<TextAlignment>, default: 'left' },
/**
*
*/
showButtonWhenDisabled: { type: Boolean, default: false },
/**
*
*/
id: String,
/**
* html标签
*/
buttonContent: { type: String, default: '<i class="f-icon f-icon-lookup"></i>' },
/**
*
*/
autoComplete: { type: Boolean, default: false },
/**
*
*/
customClass: { type: String, default: '' },
/**
*
*/
disable: { type: Boolean, default: false },
/**
*
*/
editable: { type: Boolean, default: true },
/**
*
*/
enableClear: { type: Boolean, default: false },
/**
*
*/
modelValue: { type: String, default: '' },
/**
*
*/
readonly: { type: Boolean, default: false },
/**
*
*/
textAlign: { type: String as PropType<TextAlignment>, default: 'left' },
/**
*
*/
showButtonWhenDisabled: { type: Boolean, default: false },
/**
*
*/
enableTitle: { type: Boolean, default: false },
/**
*
*/
inputType: { type: String, default: 'text' },
/**
*
*/
forcePlaceholder: { type: Boolean, default: false },
/**
*
*/
placeholder: { type: String, default: '' },
/**
*
*/
minLength: Number,
/**
*
*/
maxLength: Number,
/**
* Tab键索引
*/
tabIndex: Number,
/**
*
*/
enableTitle: { type: Boolean, default: false },
/**
*
*/
inputType: { type: String, default: 'text' },
/**
*
*/
forcePlaceholder: { type: Boolean, default: false },
/**
*
*/
placeholder: { type: String, default: '' },
/**
*
*/
minLength: Number,
/**
*
*/
maxLength: Number,
/**
* Tab键索引
*/
tabIndex: Number,
};
export type ButtonEditProps = ExtractPropTypes<typeof buttonEditProps>;

View File

@ -3,37 +3,37 @@ import { computed, SetupContext } from 'vue';
import { ButtonEditProps } from '../button-edit.props';
export function useButton(props: ButtonEditProps, context: SetupContext): UseButton {
const buttonClass = computed(() => ({
'input-group-append': true,
'append-force-show': props.showButtonWhenDisabled && (props.readonly || props.disable),
}));
const buttonClass = computed(() => ({
'input-group-append': true,
'append-force-show': props.showButtonWhenDisabled && (props.readonly || props.disable),
}));
const canClickAppendButton = computed(() => props.showButtonWhenDisabled || ((!props.editable || !props.readonly) && !props.disable));
const canClickAppendButton = computed(() => props.showButtonWhenDisabled || ((!props.editable || !props.readonly) && !props.disable));
function onClickButton($event: Event) {
if (canClickAppendButton.value) {
context.emit('clickButton', { origin: $event, value: props.modelValue });
function onClickButton($event: Event) {
if (canClickAppendButton.value) {
context.emit('clickButton', { origin: $event, value: props.modelValue });
}
$event.stopPropagation();
}
$event.stopPropagation();
}
function onMouseEnterButton($event: MouseEvent) {
context.emit('mouseEnterIcon', $event);
}
function onMouseEnterButton($event: MouseEvent) {
context.emit('mouseEnterIcon', $event);
}
function onMouseLeaveButton($event: MouseEvent) {
context.emit('mouseLeaveIcon', $event);
}
function onMouseLeaveButton($event: MouseEvent) {
context.emit('mouseLeaveIcon', $event);
}
function onMouseOverButton() {
context.emit('mouseOverButton');
}
function onMouseOverButton() {
context.emit('mouseOverButton');
}
return {
buttonClass,
onClickButton,
onMouseEnterButton,
onMouseLeaveButton,
onMouseOverButton,
};
return {
buttonClass,
onClickButton,
onMouseEnterButton,
onMouseLeaveButton,
onMouseOverButton,
};
}

View File

@ -4,64 +4,64 @@ import { UseClear } from './types';
import { useTextBox } from './use-text-box';
export function useClear(
props: ButtonEditProps,
context: SetupContext,
modelValue: Ref<string>,
hasFocusedTextBox: ComputedRef<boolean>,
displayText: Ref<string>
props: ButtonEditProps,
context: SetupContext,
modelValue: Ref<string>,
hasFocusedTextBox: ComputedRef<boolean>,
displayText: Ref<string>
): UseClear {
const showClearButton = ref(false);
const enableClearButton = computed(() => props.enableClear && !props.readonly && !props.disable);
const { changeTextBoxValue } = useTextBox(props, context, modelValue, displayText);
const showClearButton = ref(false);
const enableClearButton = computed(() => props.enableClear && !props.readonly && !props.disable);
const { changeTextBoxValue } = useTextBox(props, context, modelValue, displayText);
function toggleClearIcon(isShow: boolean) {
showClearButton.value = isShow;
}
function toggleClearIcon(isShow: boolean) {
showClearButton.value = isShow;
}
watch(displayText, () => {
if (hasFocusedTextBox.value) {
toggleClearIcon(!!displayText.value);
} else {
toggleClearIcon(false);
}
});
watch(displayText, () => {
if (hasFocusedTextBox.value) {
toggleClearIcon(!!displayText.value);
} else {
toggleClearIcon(false);
}
});
function onClearValue($event: Event) {
const flag1 = !props.readonly && !props.disable && props.editable;
const flag2 = !props.editable;
$event.stopPropagation();
if (flag1 || flag2) {
changeTextBoxValue('', false);
toggleClearIcon(!showClearButton.value);
context.emit('clear');
function onClearValue($event: Event) {
const flag1 = !props.readonly && !props.disable && props.editable;
const flag2 = !props.editable;
$event.stopPropagation();
if (flag1 || flag2) {
changeTextBoxValue('', false);
toggleClearIcon(!showClearButton.value);
context.emit('clear');
}
}
}
function onMouseEnterTextBox($event: Event) {
if (!enableClearButton.value) {
return;
function onMouseEnterTextBox($event: Event) {
if (!enableClearButton.value) {
return;
}
if (!modelValue.value) {
toggleClearIcon(false);
return;
}
if ((!props.editable || !props.readonly) && !props.disable) {
toggleClearIcon(true);
}
}
if (!modelValue.value) {
toggleClearIcon(false);
return;
}
if ((!props.editable || !props.readonly) && !props.disable) {
toggleClearIcon(true);
}
}
function onMouseLeaveTextBox($event: Event) {
if (!enableClearButton.value) {
return;
function onMouseLeaveTextBox($event: Event) {
if (!enableClearButton.value) {
return;
}
toggleClearIcon(false);
}
toggleClearIcon(false);
}
return {
enableClearButton,
showClearButton,
onClearValue,
onMouseEnterTextBox,
onMouseLeaveTextBox,
};
return {
enableClearButton,
showClearButton,
onClearValue,
onMouseEnterTextBox,
onMouseLeaveTextBox,
};
}

View File

@ -1,59 +1,118 @@
import { computed, defineComponent, SetupContext } from 'vue';
import { NotifyData } from '../notify.props';
import { computed, defineComponent, ref, SetupContext, watch } from 'vue';
import { NotifyButton, NotifyData } from '../notify.props';
import { ToastProps, toastProps } from './toast.props';
export default defineComponent({
name: 'Toast',
props: toastProps,
emits: [],
setup: (props: ToastProps, context: SetupContext) => {
const toastClass = computed(() => ({
toast: true,
}));
name: 'Toast',
props: toastProps,
emits: ['close', 'click'],
setup: (props: ToastProps, context: SetupContext) => {
const animateIn = ref(props.animate);
const animateEnd = 'fadeOut';
const toast = computed(() => {
return props.options as NotifyData;
});
const showingToast = ref(false);
const toast = computed(() => {
return {} as NotifyData;
});
const toastClass = computed(() => {
const classObject = {
animated: showingToast.value,
toast: true,
};
classObject[props.animate] = false;
classObject[animateEnd] = showingToast.value;
classObject[toast.value.type] = true;
if (toast.value.theme) {
classObject[toast.value.theme] = true;
}
return classObject;
});
const toastIconClass = computed(() => ({
'f-icon': true,
}));
const toastIconClass = computed(() => {
const hasSpecialToastType = toast.value && toast.value.type;
const iconType = hasSpecialToastType ? toast.value.type.replace('toasty-type-', '') : 'default';
const iconTypeName = `f-icon-${iconType}`;
const classObject = { 'f-icon': true };
classObject[iconTypeName] = true;
return classObject;
});
const shouldShowTips = computed(() => toast.value.title || toast.value.msg);
const shouldShowTips = computed(() => toast.value.title || toast.value.msg);
const shouldShowTitle = computed(() => toast.value.title && toast.value.msg);
const shouldShowTitle = computed(() => toast.value.title && toast.value.msg);
const shouldShowCloseButton = computed(() => {
return true;
});
const shouldShowCloseButton = computed(() => {
return true;
});
function onCloseToast($event: Event) {}
const shouldShowButtonsInTitle = computed(() => !!toast.value.buttons || !!context.slots.default);
return () => {
return (
<div class={toastClass}>
{shouldShowCloseButton.value && (
<button class="toast-close f-btn-icon f-bare" onClick={onCloseToast}>
<span class="f-icon modal_close"></span>
</button>
)}
{shouldShowTips.value && (
<section class="modal-tips">
<div class="float-left modal-tips-iconwrap">
<span class={toastIconClass}></span>
</div>
<div class="modal-tips-content">
{shouldShowTitle.value && (
<>
<h5 class="toast-title modal-tips-title" v-html={toast.value.title}></h5>
<p class="toast-msg" v-html={toast.value.msg}></p>
</>
)}
</div>
</section>
)}
</div>
);
};
},
function onCloseToast($event: Event) {
$event.stopPropagation();
$event.preventDefault();
showingToast.value = false;
setTimeout(() => {
context.emit('close', toast.value);
}, 200);
}
function onClickButton($event: Event, notifyButton: NotifyButton) {}
function getNotifyButtonClass(notifyButton: NotifyButton) {
return `f-preten-link ${notifyButton.customClass ? notifyButton.customClass : ''}`;
}
watch(animateIn, () => {
const animateInClass = animateIn.value || 'bounceInRight';
const animateOutClass = 'fadeOut';
});
const renderNotifyButtons = () => {
return (
<>
<div class="after-toast-msg text-right">
{!context.slots.default &&
toast.value.buttons?.map((notifyButton: NotifyButton) => {
return (
<span
class={getNotifyButtonClass(notifyButton)}
onClick={($event) => onClickButton($event, notifyButton)}>
{notifyButton.text}
</span>
);
})}
{context.slots.default && context.slots.default()}
</div>
</>
);
};
return () => {
return (
<div class={toastClass}>
{shouldShowCloseButton.value && (
<button class="toast-close f-btn-icon f-bare" onClick={onCloseToast}>
<span class="f-icon modal_close"></span>
</button>
)}
{shouldShowTips.value && (
<section class="modal-tips">
<div class="float-left modal-tips-iconwrap">
<span class={toastIconClass}></span>
</div>
<div class="modal-tips-content">
{shouldShowTitle.value && (
<>
<h5 class="toast-title modal-tips-title" v-html={toast.value.title}></h5>
<p class="toast-msg" v-html={toast.value.msg}></p>
{shouldShowButtonsInTitle.value && renderNotifyButtons()}
</>
)}
</div>
</section>
)}
</div>
);
};
},
});

View File

@ -1,8 +1,9 @@
import { ExtractPropTypes, PropType } from 'vue';
import { ToastyAnimate } from '../notify.props';
import { NotifyData, ToastyAnimate } from '../notify.props';
export const toastProps = {
animate: { type: String as PropType<ToastyAnimate>, default: 'fadeIn' },
animate: { type: String as PropType<ToastyAnimate>, default: 'fadeIn' },
options: { type: Object as PropType<NotifyData> },
};
export type ToastProps = ExtractPropTypes<typeof toastProps>;

View File

@ -1,35 +1,37 @@
import { computed, defineComponent, SetupContext } from 'vue';
import { computed, defineComponent, ref, SetupContext } from 'vue';
import { NotifyContainerComponent } from './notify-container.component';
import { NotifyData, NotifyProps, notifyProps } from './notify.props';
export default defineComponent({
name: 'Notify',
props: notifyProps,
emits: [],
setup(props: NotifyProps, context: SetupContext) {
const notifyClass = computed(() => ({
'farris-notify': true,
}));
name: 'Notify',
props: notifyProps,
emits: [],
setup(props: NotifyProps, context: SetupContext) {
const notifyClass = computed(() => ({
'farris-notify': true,
}));
const notifyStyle = computed(() => ({
left: '',
right: '',
top: '',
bottom: '',
}));
const notifyStyle = computed(() => ({
left: '',
right: '',
top: '',
bottom: '',
}));
const toasts = computed(() => {
return props.toasts ? props.toasts : [];
});
const toasts = computed(() => {
return props.toasts ? props.toasts : [];
});
return () => {
return (
<div id={props.id} class={notifyClass.value} style={notifyStyle.value}>
{toasts.value.map((toastData: NotifyData) => {
return <f-toast v-model={toastData} animate={props.animate} onClose={} onClick={}></f-toast>;
})}
</div>
);
};
},
return () => {
return (
<div id={props.id} class={notifyClass.value} style={notifyStyle.value}>
{toasts.value.map((toastData: NotifyData) => {
return <f-toast v-model={toastData} animate={props.animate} onClose={} onClick={}></f-toast>;
})}
</div>
);
};
},
});

View File

@ -1,44 +1,45 @@
import { NotifyData } from './notify.props';
import { ExtractPropTypes, PropType } from 'vue';
export type NotifyPosition = 'bottom-right' | 'bottom-left' | 'top-right' | 'top-left' | 'top-center' | 'bottom-center' | 'center-center';
export type ToastyAnimate =
| 'bounceInRight'
| 'bounceInLeft'
| 'bounceInRight'
| 'bounceInLeft'
| 'bounceInDown'
| 'bounceInUp'
| 'bounceIn'
| 'fadeIn';
| 'bounceInRight'
| 'bounceInLeft'
| 'bounceInRight'
| 'bounceInLeft'
| 'bounceInDown'
| 'bounceInUp'
| 'bounceIn'
| 'fadeIn';
export interface NotifyButton {
customClass?: string;
text: string;
disable?: boolean;
onClick?: ($event: Event, component: any) => any;
customClass?: string;
text: string;
disable?: boolean;
onClick?: ($event: Event, component: any) => any;
}
export interface NotifyData {
type: string;
title?: string;
msg?: string;
/** 按钮列表模板 */
buttons?: Array<NotifyButton>;
showClose?: boolean;
theme?: string;
timeout?: number;
onAdd?: () => void;
onRemove?: () => void;
id?: number | string;
type: string;
title?: string;
msg?: string;
/** 按钮列表模板 */
buttons?: Array<NotifyButton>;
showClose?: boolean;
theme?: string;
timeout?: number;
onAdd?: () => void;
onRemove?: () => void;
id?: number | string;
}
// export interface NotifyData extends NotifyOptions {}
export const notifyProps = {
id: { type: String },
animate: { type: String as PropType<ToastyAnimate>, default: 'fadeIn' },
position: { type: String as PropType<NotifyPosition>, default: 'toasty-position-top-center' },
toasts: { type: Array<NotifyData> },
id: { type: String },
animate: { type: String as PropType<ToastyAnimate>, default: 'fadeIn' },
position: { type: String as PropType<NotifyPosition>, default: 'toasty-position-top-center' },
toasts: { type: Array<NotifyData> },
};
export type NotifyProps = ExtractPropTypes<typeof notifyProps>;