Refactor #3965 - For SpeedDial

pull/4011/head
Tuğçe Küçükoğlu 2023-05-30 17:55:13 +03:00
parent 16117a2995
commit 16abc9e835
3 changed files with 258 additions and 237 deletions

View File

@ -0,0 +1,235 @@
<script>
import BaseComponent from 'primevue/basecomponent';
import { useStyle } from 'primevue/usestyle';
const styles = `
.p-speeddial {
position: absolute;
display: flex;
}
.p-speeddial-button {
z-index: 1;
}
.p-speeddial-list {
margin: 0;
padding: 0;
list-style: none;
display: flex;
align-items: center;
justify-content: center;
transition: top 0s linear 0.2s;
pointer-events: none;
z-index: 2;
}
.p-speeddial-item {
transform: scale(0);
opacity: 0;
transition: transform 200ms cubic-bezier(0.4, 0, 0.2, 1) 0ms, opacity 0.8s;
will-change: transform;
}
.p-speeddial-action {
display: flex;
align-items: center;
justify-content: center;
border-radius: 50%;
position: relative;
overflow: hidden;
}
.p-speeddial-circle .p-speeddial-item,
.p-speeddial-semi-circle .p-speeddial-item,
.p-speeddial-quarter-circle .p-speeddial-item {
position: absolute;
}
.p-speeddial-rotate {
transition: transform 250ms cubic-bezier(0.4, 0, 0.2, 1) 0ms;
will-change: transform;
}
.p-speeddial-mask {
position: absolute;
left: 0;
top: 0;
width: 100%;
height: 100%;
opacity: 0;
transition: opacity 250ms cubic-bezier(0.25, 0.8, 0.25, 1);
}
.p-speeddial-mask-visible {
pointer-events: none;
opacity: 1;
transition: opacity 400ms cubic-bezier(0.25, 0.8, 0.25, 1);
}
.p-speeddial-opened .p-speeddial-list {
pointer-events: auto;
}
.p-speeddial-opened .p-speeddial-item {
transform: scale(1);
opacity: 1;
}
.p-speeddial-opened .p-speeddial-rotate {
transform: rotate(45deg);
}
/* Direction */
.p-speeddial-direction-up {
align-items: center;
flex-direction: column-reverse;
}
.p-speeddial-direction-up .p-speeddial-list {
flex-direction: column-reverse;
}
.p-speeddial-direction-down {
align-items: center;
flex-direction: column;
}
.p-speeddial-direction-down .p-speeddial-list {
flex-direction: column;
}
.p-speeddial-direction-left {
justify-content: center;
flex-direction: row-reverse;
}
.p-speeddial-direction-left .p-speeddial-list {
flex-direction: row-reverse;
}
.p-speeddial-direction-right {
justify-content: center;
flex-direction: row;
}
.p-speeddial-direction-right .p-speeddial-list {
flex-direction: row;
}
`;
const classes = {
root: ({ instance, props }) => [
`p-speeddial p-component p-speeddial-${props.type}`,
{
[`p-speeddial-direction-${props.direction}`]: props.type !== 'circle',
'p-speeddial-opened': instance.d_visible,
'p-disabled': props.disabled
}
],
button: ({ props }) => [
'p-speeddial-button p-button-rounded',
{
'p-speeddial-rotate': props.rotateAnimation && !props.hideIcon
}
],
buttonIcon: ({ instance, props }) => (instance.d_visible && !!props.hideIcon ? props.hideIcon : props.showIcon),
menu: 'p-speeddial-list',
menuitem: ({ instance, id }) => [
'p-speeddial-item',
{
'p-focus': instance.isItemActive(id)
}
],
action: ({ item }) => [
'p-speeddial-action',
{
'p-disabled': item.disabled
}
],
actionIcon: 'p-speeddial-action-icon',
mask: ({ instance }) => [
'p-speeddial-mask',
{
'p-speeddial-mask-visible': instance.d_visible
}
]
};
const { load: loadStyle } = useStyle(styles, { id: 'primevue_speeddial_style', manual: true });
export default {
name: 'BaseSpeedDial',
extends: BaseComponent,
props: {
model: null,
visible: {
type: Boolean,
default: false
},
direction: {
type: String,
default: 'up'
},
transitionDelay: {
type: Number,
default: 30
},
type: {
type: String,
default: 'linear'
},
radius: {
type: Number,
default: 0
},
mask: {
type: Boolean,
default: false
},
disabled: {
type: Boolean,
default: false
},
hideOnClickOutside: {
type: Boolean,
default: true
},
buttonClass: null,
maskStyle: null,
maskClass: null,
showIcon: {
type: String,
default: undefined
},
hideIcon: {
type: String,
default: undefined
},
rotateAnimation: {
type: Boolean,
default: true
},
tooltipOptions: null,
style: null,
class: null,
'aria-labelledby': {
type: String,
default: null
},
'aria-label': {
type: String,
default: null
}
},
css: {
classes,
loadStyle
},
provide() {
return {
$parentInstance: this
};
}
};
</script>

View File

@ -228,6 +228,11 @@ export interface SpeedDialProps {
* @type {SpeedDialPassThroughOptions} * @type {SpeedDialPassThroughOptions}
*/ */
pt?: SpeedDialPassThroughOptions; pt?: SpeedDialPassThroughOptions;
/**
* When enabled, it removes component related styles in the core.
* @defaultValue false
*/
unstyled?: boolean;
} }
/** /**

View File

@ -1,9 +1,9 @@
<template> <template>
<div :ref="containerRef" :class="containerClass" :style="style" v-bind="ptm('root')"> <div :ref="containerRef" :class="containerClass" :style="style" v-bind="ptm('root')" data-pc-name="speeddial">
<slot name="button" :toggle="onClick"> <slot name="button" :toggle="onClick">
<SDButton <SDButton
type="button" type="button"
:class="buttonClassName" :class="[cx('button'), buttonClass]"
@click="onClick($event)" @click="onClick($event)"
:disabled="disabled" :disabled="disabled"
@keydown="onTogglerKeydown" @keydown="onTogglerKeydown"
@ -16,24 +16,15 @@
> >
<template #icon> <template #icon>
<slot name="icon" :visible="d_visible"> <slot name="icon" :visible="d_visible">
<component v-if="d_visible && !!hideIcon" :is="hideIcon ? 'span' : 'PlusIcon'" :class="hideIcon" v-bind="ptm('button')['icon']" /> <component v-if="d_visible && !!hideIcon" :is="hideIcon ? 'span' : 'PlusIcon'" :class="cx('buttonIcon')" v-bind="ptm('button')['icon']" />
<component v-else :is="showIcon ? 'span' : 'PlusIcon'" :class="showIcon" v-bind="ptm('button')['icon']" /> <component v-else :is="showIcon ? 'span' : 'PlusIcon'" :class="cx('buttonIcon')" v-bind="ptm('button')['icon']" />
</slot> </slot>
</template> </template>
</SDButton> </SDButton>
</slot> </slot>
<ul :ref="listRef" :id="id + '_list'" class="p-speeddial-list" role="menu" @focus="onFocus" @blur="onBlur" @keydown="onKeyDown" :aria-activedescendant="focused ? focusedOptionId : undefined" tabindex="-1" v-bind="ptm('menu')"> <ul :ref="listRef" :id="id + '_list'" :class="cx('menu')" role="menu" @focus="onFocus" @blur="onBlur" @keydown="onKeyDown" :aria-activedescendant="focused ? focusedOptionId : undefined" tabindex="-1" v-bind="ptm('menu')">
<template v-for="(item, index) of model" :key="index"> <template v-for="(item, index) of model" :key="index">
<li <li v-if="isItemVisible(item)" :id="`${id}_${index}`" :aria-controls="`${id}_item`" :class="cx('menuitem', { id: `${id}_${index}` })" :style="getItemStyle(index)" role="menuitem" v-bind="getPTOptions(`${id}_${index}`, 'menuitem')">
v-if="isItemVisible(item)"
:id="`${id}_${index}`"
:aria-controls="`${id}_item`"
class="p-speeddial-item"
:class="itemClass(`${id}_${index}`)"
:style="getItemStyle(index)"
role="menuitem"
v-bind="getPTOptions(`${id}_${index}`, 'menuitem')"
>
<template v-if="!$slots.item"> <template v-if="!$slots.item">
<a <a
v-tooltip:[tooltipOptions]="{ value: item.label, disabled: !tooltipOptions }" v-tooltip:[tooltipOptions]="{ value: item.label, disabled: !tooltipOptions }"
@ -41,13 +32,13 @@
:tabindex="-1" :tabindex="-1"
:href="item.url || '#'" :href="item.url || '#'"
role="menuitem" role="menuitem"
:class="['p-speeddial-action', { 'p-disabled': item.disabled }]" :class="cx('action', { item })"
:target="item.target" :target="item.target"
@click="onItemClick($event, item)" @click="onItemClick($event, item)"
:aria-label="item.label" :aria-label="item.label"
v-bind="getPTOptions(`${id}_${index}`, 'action')" v-bind="getPTOptions(`${id}_${index}`, 'action')"
> >
<span v-if="item.icon" :class="['p-speeddial-action-icon', item.icon]" v-bind="getPTOptions(`${id}_${index}`, 'actionIcon')"></span> <span v-if="item.icon" :class="[cx('actionIcon'), item.icon]" v-bind="getPTOptions(`${id}_${index}`, 'actionIcon')"></span>
</a> </a>
</template> </template>
<component v-else :is="$slots.item" :item="item" :onClick="(event) => onItemClick(event, item)"></component> <component v-else :is="$slots.item" :item="item" :onClick="(event) => onItemClick(event, item)"></component>
@ -56,83 +47,22 @@
</ul> </ul>
</div> </div>
<template v-if="mask"> <template v-if="mask">
<div :class="maskClassName" :style="maskStyle" v-bind="ptm('mask')"></div> <div :class="[cx('mask'), maskClass]" :style="maskStyle" v-bind="ptm('mask')"></div>
</template> </template>
</template> </template>
<script> <script>
import BaseComponent from 'primevue/basecomponent';
import Button from 'primevue/button'; import Button from 'primevue/button';
import PlusIcon from 'primevue/icons/plus'; import PlusIcon from 'primevue/icons/plus';
import Ripple from 'primevue/ripple'; import Ripple from 'primevue/ripple';
import Tooltip from 'primevue/tooltip'; import Tooltip from 'primevue/tooltip';
import { DomHandler, UniqueComponentId } from 'primevue/utils'; import { DomHandler, UniqueComponentId } from 'primevue/utils';
import BaseSpeedDial from './BaseSpeedDial.vue';
export default { export default {
name: 'SpeedDial', name: 'SpeedDial',
extends: BaseComponent, extends: BaseSpeedDial,
emits: ['click', 'show', 'hide', 'focus', 'blur'], emits: ['click', 'show', 'hide', 'focus', 'blur'],
props: {
model: null,
visible: {
type: Boolean,
default: false
},
direction: {
type: String,
default: 'up'
},
transitionDelay: {
type: Number,
default: 30
},
type: {
type: String,
default: 'linear'
},
radius: {
type: Number,
default: 0
},
mask: {
type: Boolean,
default: false
},
disabled: {
type: Boolean,
default: false
},
hideOnClickOutside: {
type: Boolean,
default: true
},
buttonClass: null,
maskStyle: null,
maskClass: null,
showIcon: {
type: String,
default: undefined
},
hideIcon: {
type: String,
default: undefined
},
rotateAnimation: {
type: Boolean,
default: true
},
tooltipOptions: null,
style: null,
class: null,
'aria-labelledby': {
type: String,
default: null
},
'aria-label': {
type: String,
default: null
}
},
documentClickListener: null, documentClickListener: null,
container: null, container: null,
list: null, list: null,
@ -157,8 +87,8 @@ export default {
this.id = this.id || UniqueComponentId(); this.id = this.id || UniqueComponentId();
if (this.type !== 'linear') { if (this.type !== 'linear') {
const button = DomHandler.findSingle(this.container, '.p-speeddial-button'); const button = DomHandler.findSingle(this.container, '[data-pc-section="button"]');
const firstItem = DomHandler.findSingle(this.list, '.p-speeddial-item'); const firstItem = DomHandler.findSingle(this.list, '[data-pc-section="menuitem"]');
if (button && firstItem) { if (button && firstItem) {
const wDiff = Math.abs(button.offsetWidth - firstItem.offsetWidth); const wDiff = Math.abs(button.offsetWidth - firstItem.offsetWidth);
@ -306,7 +236,7 @@ export default {
event.preventDefault(); event.preventDefault();
}, },
onEnterKey(event) { onEnterKey(event) {
const items = DomHandler.find(this.container, '.p-speeddial-item'); const items = DomHandler.find(this.container, '[data-pc-section="menuitem"]');
const itemIndex = [...items].findIndex((item) => item.id === this.focusedOptionIndex); const itemIndex = [...items].findIndex((item) => item.id === this.focusedOptionIndex);
this.onItemClick(event, this.model[itemIndex]); this.onItemClick(event, this.model[itemIndex]);
@ -394,7 +324,7 @@ export default {
event.preventDefault(); event.preventDefault();
}, },
changeFocusedOptionIndex(index) { changeFocusedOptionIndex(index) {
const items = DomHandler.find(this.container, '.p-speeddial-item'); const items = DomHandler.find(this.container, '[data-pc-section="menuitem"]');
const filteredItems = [...items].filter((item) => !DomHandler.hasClass(DomHandler.findSingle(item, 'a'), 'p-disabled')); const filteredItems = [...items].filter((item) => !DomHandler.hasClass(DomHandler.findSingle(item, 'a'), 'p-disabled'));
if (filteredItems[index]) { if (filteredItems[index]) {
@ -402,7 +332,7 @@ export default {
} }
}, },
findPrevOptionIndex(index) { findPrevOptionIndex(index) {
const items = DomHandler.find(this.container, '.p-speeddial-item'); const items = DomHandler.find(this.container, '[data-pc-section="menuitem"]');
const filteredItems = [...items].filter((item) => !DomHandler.hasClass(DomHandler.findSingle(item, 'a'), 'p-disabled')); const filteredItems = [...items].filter((item) => !DomHandler.hasClass(DomHandler.findSingle(item, 'a'), 'p-disabled'));
const newIndex = index === -1 ? filteredItems[filteredItems.length - 1].id : index; const newIndex = index === -1 ? filteredItems[filteredItems.length - 1].id : index;
let matchedOptionIndex = filteredItems.findIndex((link) => link.getAttribute('id') === newIndex); let matchedOptionIndex = filteredItems.findIndex((link) => link.getAttribute('id') === newIndex);
@ -412,7 +342,7 @@ export default {
return matchedOptionIndex; return matchedOptionIndex;
}, },
findNextOptionIndex(index) { findNextOptionIndex(index) {
const items = DomHandler.find(this.container, '.p-speeddial-item'); const items = DomHandler.find(this.container, '[data-pc-section="menuitem"]');
const filteredItems = [...items].filter((item) => !DomHandler.hasClass(DomHandler.findSingle(item, 'a'), 'p-disabled')); const filteredItems = [...items].filter((item) => !DomHandler.hasClass(DomHandler.findSingle(item, 'a'), 'p-disabled'));
const newIndex = index === -1 ? filteredItems[0].id : index; const newIndex = index === -1 ? filteredItems[0].id : index;
let matchedOptionIndex = filteredItems.findIndex((link) => link.getAttribute('id') === newIndex); let matchedOptionIndex = filteredItems.findIndex((link) => link.getAttribute('id') === newIndex);
@ -512,44 +442,11 @@ export default {
}, },
listRef(el) { listRef(el) {
this.list = el; this.list = el;
},
itemClass(id) {
return [
{
'p-focus': this.isItemActive(id)
}
];
} }
}, },
computed: { computed: {
containerClass() { containerClass() {
return [ return [this.cx('root'), this.class];
`p-speeddial p-component p-speeddial-${this.type}`,
{
[`p-speeddial-direction-${this.direction}`]: this.type !== 'circle',
'p-speeddial-opened': this.d_visible,
'p-disabled': this.disabled
},
this.class
];
},
buttonClassName() {
return [
'p-speeddial-button p-button-rounded',
{
'p-speeddial-rotate': this.rotateAnimation && !this.hideIcon
},
this.buttonClass
];
},
maskClassName() {
return [
'p-speeddial-mask',
{
'p-speeddial-mask-visible': this.d_visible
},
this.maskClass
];
}, },
focusedOptionId() { focusedOptionId() {
return this.focusedOptionIndex !== -1 ? this.focusedOptionIndex : null; return this.focusedOptionIndex !== -1 ? this.focusedOptionIndex : null;
@ -565,119 +462,3 @@ export default {
} }
}; };
</script> </script>
<style>
.p-speeddial {
position: absolute;
display: flex;
}
.p-speeddial-button {
z-index: 1;
}
.p-speeddial-list {
margin: 0;
padding: 0;
list-style: none;
display: flex;
align-items: center;
justify-content: center;
transition: top 0s linear 0.2s;
pointer-events: none;
z-index: 2;
}
.p-speeddial-item {
transform: scale(0);
opacity: 0;
transition: transform 200ms cubic-bezier(0.4, 0, 0.2, 1) 0ms, opacity 0.8s;
will-change: transform;
}
.p-speeddial-action {
display: flex;
align-items: center;
justify-content: center;
border-radius: 50%;
position: relative;
overflow: hidden;
}
.p-speeddial-circle .p-speeddial-item,
.p-speeddial-semi-circle .p-speeddial-item,
.p-speeddial-quarter-circle .p-speeddial-item {
position: absolute;
}
.p-speeddial-rotate {
transition: transform 250ms cubic-bezier(0.4, 0, 0.2, 1) 0ms;
will-change: transform;
}
.p-speeddial-mask {
position: absolute;
left: 0;
top: 0;
width: 100%;
height: 100%;
opacity: 0;
transition: opacity 250ms cubic-bezier(0.25, 0.8, 0.25, 1);
}
.p-speeddial-mask-visible {
pointer-events: none;
opacity: 1;
transition: opacity 400ms cubic-bezier(0.25, 0.8, 0.25, 1);
}
.p-speeddial-opened .p-speeddial-list {
pointer-events: auto;
}
.p-speeddial-opened .p-speeddial-item {
transform: scale(1);
opacity: 1;
}
.p-speeddial-opened .p-speeddial-rotate {
transform: rotate(45deg);
}
/* Direction */
.p-speeddial-direction-up {
align-items: center;
flex-direction: column-reverse;
}
.p-speeddial-direction-up .p-speeddial-list {
flex-direction: column-reverse;
}
.p-speeddial-direction-down {
align-items: center;
flex-direction: column;
}
.p-speeddial-direction-down .p-speeddial-list {
flex-direction: column;
}
.p-speeddial-direction-left {
justify-content: center;
flex-direction: row-reverse;
}
.p-speeddial-direction-left .p-speeddial-list {
flex-direction: row-reverse;
}
.p-speeddial-direction-right {
justify-content: center;
flex-direction: row;
}
.p-speeddial-direction-right .p-speeddial-list {
flex-direction: row;
}
</style>