Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: 下拉类组件增加动画&输入类组件增加悬浮发光效果 #1816

Merged
merged 2 commits into from
Mar 30, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading