Skip to content

Commit

Permalink
feat: 下拉类组件增加动画&输入类组件增加悬浮发光效果 (#1816)
Browse files Browse the repository at this point in the history
  • Loading branch information
xingyan95 authored Mar 30, 2024
1 parent 12c2fc4 commit 75bd63f
Show file tree
Hide file tree
Showing 57 changed files with 806 additions and 184 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,10 @@ export const autoCompleteProps = {
type: Boolean,
default: false,
},
showGlowStyle: {
type: Boolean,
default: true,
},
} as const;

export type AutoCompleteProps = ExtractPropTypes<typeof autoCompleteProps>;
Expand Down
73 changes: 69 additions & 4 deletions packages/devui-vue/devui/auto-complete/src/auto-complete.scss
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,13 @@
box-sizing: border-box;
height: 100%;

&:not(.#{$devui-prefix}-auto-complete--focus) {
.#{$devui-prefix}-auto-complete--glow-style:hover {
box-shadow: 0 0 0 4px $devui-form-control-interactive-outline;
border-color: $devui-form-control-line;
}
}

&__wrapper {
display: inline-flex;
align-items: center;
Expand All @@ -157,7 +164,7 @@
border: 1px solid $devui-form-control-line;
border-radius: $devui-border-radius;
background-color: $devui-form-control-bg;
transition: border-color 0.3s $devui-animation-ease-in-out-smooth;
transition: border-color 0.3s $devui-animation-ease-in-out-smooth, box-shadow $devui-animation-duration-base $devui-animation-ease-in;

&:not(.#{$devui-prefix}-auto-complete--disabled):not(.#{$devui-prefix}-auto-complete-input__wrapper--error):hover {
border-color: $devui-form-control-line-hover;
Expand Down Expand Up @@ -206,10 +213,22 @@
}
}

&--focus .#{$devui-prefix}-auto-complete-input__wrapper:not(.#{$devui-prefix}-auto-complete-input__wrapper--error) {
border-color: $devui-form-control-line-active;
&--glow-style:not(.#{$devui-prefix}-auto-complete--disabled):not(.#{$devui-prefix}-auto-complete-input__wrapper--error):hover {
box-shadow: 0 0 0 4px $devui-form-control-interactive-outline;
border-color: $devui-form-control-line;
}

&:hover {
&--focus {
.#{$devui-prefix}-auto-complete-input__wrapper:not(.#{$devui-prefix}-auto-complete-input__wrapper--error) {
border-color: $devui-form-control-line-active;

&:hover {
border-color: $devui-form-control-line-active;
}
}

.#{$devui-prefix}-auto-complete--glow-style:not(.#{$devui-prefix}-auto-complete-input__wrapper--error) {
box-shadow: 0 0 0 4px $devui-form-control-interactive-outline;
border-color: $devui-form-control-line-active;
}
}
Expand Down Expand Up @@ -277,3 +296,49 @@
}
}
}

.#{$devui-prefix}-auto-complete--fade {
&-bottom {
&-enter-from,
&-leave-to {
opacity: 0.8;
transform: scaleY(0.8) translateY(-4px);
}

&-enter-to,
&-leave-from {
opacity: 1;
transform: scaleY(0.9999) translateY(0);
}

&-enter-active {
transition: transform 0.2s cubic-bezier(0.16, 0.75, 0.5, 1), opacity 0.2s cubic-bezier(0.16, 0.75, 0.5, 1);
}

&-leave-active {
transition: transform 0.2s cubic-bezier(0.5, 0, 0.84, 0.25), opacity 0.2s cubic-bezier(0.5, 0, 0.84, 0.25);
}
}

&-top {
&-enter-from,
&-leave-to {
opacity: 0.8;
transform: scaleY(0.8) translateY(4px);
}

&-enter-to,
&-leave-from {
opacity: 1;
transform: scaleY(0.9999) translateY(0);
}

&-enter-active {
transition: transform 0.2s cubic-bezier(0.16, 0.75, 0.5, 1), opacity 0.2s cubic-bezier(0.16, 0.75, 0.5, 1);
}

&-leave-active {
transition: transform 0.2s cubic-bezier(0.5, 0, 0.84, 0.25), opacity 0.2s cubic-bezier(0.5, 0, 0.84, 0.25);
}
}
}
16 changes: 14 additions & 2 deletions packages/devui-vue/devui/auto-complete/src/auto-complete.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ export default defineComponent({
const inputNs = useNamespace('auto-complete-input');
const isDisabled = computed(() => formContext?.disabled || disabled.value);
const autoCompleteSize = computed(() => formContext?.size || props.size);
const align = computed(() => (position.value.some((item) => item.includes('start') || item.includes('end')) ? 'start' : null));

const { handleSearch, searchList, showNoResultItemTemplate, recentlyFocus } = useSearchFn(
ctx,
Expand Down Expand Up @@ -104,20 +105,31 @@ export default defineComponent({
valueParser,
});
const origin = ref<HTMLElement>();
const currentPosition = ref('bottom');

const prefixVisible = ctx.slots.prefix || props.prefix;
const suffixVisible = ctx.slots.suffix || props.suffix || props.clearable;

const showClearable = computed(() => props.clearable && !isDisabled.value);
const overlayStyles = computed(() => ({
transformOrigin: currentPosition.value === 'top' ? '0% 100%' : '0% 0%',
zIndex: 'var(--devui-z-index-dropdown, 1052)',
}));

const handlePositionChange = (pos: string) => {
currentPosition.value = pos.includes('top') || pos.includes('right-end') || pos.includes('left-end') ? 'top' : 'bottom';
};

const renderBasicDropdown = () => {
return (
<Transition name={showAnimation ? 'fade' : ''}>
<Transition name={showAnimation ? ns.m(`fade-${currentPosition.value}`) : ''}>
<FlexibleOverlay
origin={origin.value}
position={position.value}
align={align.value}
v-model={visible.value}
style={{ zIndex: 'var(--devui-z-index-dropdown, 1052)' }}>
onPositionChange={handlePositionChange}
style={overlayStyles.value}>
<div
class={ns.e('menu')}
style={{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ export function useAutoCompleteRender(
const inputWrapperClasses = computed(() => ({
[inputNs.e('wrapper')]: true,
[inputNs.em('wrapper', 'error')]: isValidatorError.value,
[ns.m('glow-style')]: props.showGlowStyle,
[inputNs.em('wrapper', 'feedback')]: Boolean(formItemContext?.validateState) && formItemContext?.showFeedback,
[ns.m('disabled')]: isDisabled.value,
}));
Expand Down
42 changes: 38 additions & 4 deletions packages/devui-vue/devui/button/src/button.scss
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ $devui-btn-lg-padding: var(--devui-btn-lg-padding, 0 24px);
}

.#{$devui-prefix}-button {
position: relative;
padding: $devui-btn-padding;
font-size: $devui-font-size-md;
height: $devui-size-md;
Expand All @@ -26,6 +27,22 @@ $devui-btn-lg-padding: var(--devui-btn-lg-padding, 0 24px);
border-width: 1px;
border-color: transparent;
background-color: transparent;
overflow: hidden;

&.mousedown:not(:disabled) {
transform: scale(0.95);
}

.water-wave {
position: absolute;
background-color: $devui-base-bg;
border-radius: 50%;
opacity: 0;
width: 20px;
height: 20px;
transform: translate(-50%, -50%);
animation: waterWave $devui-animation-duration-slow $devui-animation-linear;
}

&:hover {
cursor: pointer;
Expand Down Expand Up @@ -320,8 +337,7 @@ $devui-btn-lg-padding: var(--devui-btn-lg-padding, 0 24px);
}

.#{$devui-prefix}-button {
transition:
background-color $devui-animation-duration-slow $devui-animation-ease-in-out-smooth,
transition: background-color $devui-animation-duration-slow $devui-animation-ease-in-out-smooth,
border-color $devui-animation-duration-slow $devui-animation-ease-in-out-smooth,
color $devui-animation-duration-slow $devui-animation-ease-in-out-smooth;
white-space: nowrap;
Expand Down Expand Up @@ -397,7 +413,25 @@ $devui-btn-lg-padding: var(--devui-btn-lg-padding, 0 24px);
}

@keyframes rotating {
0% { transform: rotate(0); }
0% {
transform: rotate(0);
}

100% { transform: rotate(180deg); }
100% {
transform: rotate(180deg);
}
}

@keyframes waterWave {
0% {
opacity: 0.2;
width: 30px;
height: 30px;
}

100% {
opacity: 0;
width: 200px;
height: 200px;
}
}
27 changes: 25 additions & 2 deletions packages/devui-vue/devui/button/src/button.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { defineComponent, toRefs } from 'vue';
import { defineComponent, toRefs, ref, reactive } from 'vue';
import type { SetupContext } from 'vue';
import { Icon } from '../../icon';
import LoadingDirective from '../../loading/src/loading-directive';
Expand All @@ -16,22 +16,45 @@ export default defineComponent({
setup(props: ButtonProps, ctx: SetupContext) {
const { icon, disabled, loading, nativeType } = toRefs(props);
const { classes, iconClass } = useButton(props, ctx);
const isMouseDown = ref(false);
const showWave = ref(false);
const waveStyle = reactive({
top: '0px',
left: '0px',
});

const showClickWave = (e: MouseEvent) => {
waveStyle.left = e.offsetX + 'px';
waveStyle.top = e.offsetY + 'px';
showWave.value = true;

setTimeout(() => {
showWave.value = false;
}, 300);
};
const onClick = (e: MouseEvent) => {
if (loading.value) {
return;
}
showClickWave(e);
ctx.emit('click', e);
};

return () => {
return (
<button class={classes.value} disabled={disabled.value} onClick={onClick} type={nativeType.value}>
<button
class={[classes.value, isMouseDown.value ? 'mousedown' : '']}
disabled={disabled.value}
onClick={onClick}
type={nativeType.value}
onMousedown={() => (isMouseDown.value = true)}
onMouseup={() => (isMouseDown.value = false)}>
{icon.value && <Icon name={icon.value} size="var(--devui-font-size, 12px)" color="" class={iconClass.value} />}
<div class="loading-icon__container" v-show={loading.value}>
<d-icon name="icon-loading" class="button-icon-loading" color="#BBDEFB"></d-icon>
</div>
<span class="button-content">{ctx.slots.default?.()}</span>
{showWave.value && <div class="water-wave" style={waveStyle}></div>}
</button>
);
};
Expand Down
6 changes: 5 additions & 1 deletion packages/devui-vue/devui/cascader/src/cascader-types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -125,7 +125,11 @@ export const cascaderProps = {
default: () => true,
},
size: {
type: String as PropType<InputSize>
type: String as PropType<InputSize>,
},
showGlowStyle: {
type: Boolean,
default: true,
},
} as const;

Expand Down
46 changes: 46 additions & 0 deletions packages/devui-vue/devui/cascader/src/cascader.scss
Original file line number Diff line number Diff line change
Expand Up @@ -111,3 +111,49 @@
transform: rotate(180deg);
}
}

.#{$devui-prefix}-cascader--fade {
&-bottom {
&-enter-from,
&-leave-to {
opacity: 0.8;
transform: scaleY(0.8) translateY(-4px);
}

&-enter-to,
&-leave-from {
opacity: 1;
transform: scaleY(0.9999) translateY(0);
}

&-enter-active {
transition: transform 0.2s cubic-bezier(0.16, 0.75, 0.5, 1), opacity 0.2s cubic-bezier(0.16, 0.75, 0.5, 1);
}

&-leave-active {
transition: transform 0.2s cubic-bezier(0.5, 0, 0.84, 0.25), opacity 0.2s cubic-bezier(0.5, 0, 0.84, 0.25);
}
}

&-top {
&-enter-from,
&-leave-to {
opacity: 0.8;
transform: scaleY(0.8) translateY(4px);
}

&-enter-to,
&-leave-from {
opacity: 1;
transform: scaleY(0.9999) translateY(0);
}

&-enter-active {
transition: transform 0.2s cubic-bezier(0.16, 0.75, 0.5, 1), opacity 0.2s cubic-bezier(0.16, 0.75, 0.5, 1);
}

&-leave-active {
transition: transform 0.2s cubic-bezier(0.5, 0, 0.84, 0.25), opacity 0.2s cubic-bezier(0.5, 0, 0.84, 0.25);
}
}
}
17 changes: 14 additions & 3 deletions packages/devui-vue/devui/cascader/src/cascader.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { defineComponent, Transition, SetupContext, provide, Teleport } from 'vue';
import { defineComponent, Transition, SetupContext, provide, Teleport, ref, computed } from 'vue';
import { cloneDeep } from 'lodash';
import { useNamespace } from '../../shared/hooks/use-namespace';
import DCascaderList from '../components/cascader-list';
Expand Down Expand Up @@ -45,6 +45,15 @@ export default defineComponent({
} = useCascader(props, ctx);
provide(POPPER_TRIGGER_TOKEN, origin);

const currentPosition = ref('bottom');
const styles = computed(() => ({
transformOrigin: currentPosition.value === 'top' ? '0% 100%' : '0% 0%',
'z-index': 'var(--devui-z-index-dropdown, 1052)',
}));
const handlePositionChange = (pos: string) => {
currentPosition.value = pos.split('-')[0] === 'top' ? 'top' : 'bottom';
};

return () => (
<div style={rootStyle.inputWidth}>
<PopperTrigger>
Expand All @@ -60,6 +69,7 @@ export default defineComponent({
placeholder={props.placeholder}
modelValue={inputValue.value}
size={props.size}
show-glow-style={props.showGlowStyle}
onInput={handleInput}
onFocus={onFocus}
onBlur={onBlur}
Expand All @@ -81,14 +91,15 @@ export default defineComponent({
)}
</PopperTrigger>
<Teleport to="body">
<Transition name="fade">
<Transition name={ns.m(`fade-${currentPosition.value}`)}>
<FlexibleOverlay
origin={origin.value}
ref={overlayRef}
v-model={menuShow.value}
position={position.value as Placement[]}
align="start"
style={{ zIndex: 'var(--devui-z-index-dropdown, 1052)' }}>
style={styles.value}
onPositionChange={handlePositionChange}>
<div class={ns.e('drop-menu-animation')}>
{!isSearching.value && (
<div class={`${menuOpenClass.value} ${ns.e('dropdown-menu')}`}>
Expand Down
Loading

0 comments on commit 75bd63f

Please sign in to comment.