Skip to content
New issue

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

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

Already on GitHub? # to your account

feat(QFab): add scoped slots for icon, icon-active and label #7689 #11618

Merged
merged 1 commit into from
Dec 13, 2021
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
67 changes: 67 additions & 0 deletions docs/src/examples/QFab/FabSlots.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
<template>
<div class="q-px-sm q-py-lg">
<div class="column items-center" style="margin-top: 100px; margin-bottom: 100px;">
<q-fab color="purple" direction="up">
<template v-slot:icon="{ opened }">
<q-icon :class="{ 'example-fab-animate--hover': opened !== true }" name="keyboard_arrow_up" />
</template>

<template v-slot:active-icon="{ opened }">
<q-icon :class="{ 'example-fab-animate': opened === true }" name="close" />
</template>

<q-fab-action color="primary" external-label @click="onClick">
<template v-slot:icon>
<q-icon name="mail" />
</template>
<template v-slot:label>
Mail
</template>
</q-fab-action>

<q-fab-action color="secondary" external-label @click="onClick" icon="alarm" label="Alarm" />
</q-fab>

<br>

<q-fab color="amber" text-color="black" icon="keyboard_arrow_left" direction="left">
<template v-slot:label="{ opened }">
<div :class="{ 'example-fab-animate--hover': opened !== true }">
{{ opened !== true ? 'Open' : 'Close' }}
</div>
</template>

<q-fab-action color="primary" @click="onClick" icon="mail" />
<q-fab-action color="secondary" @click="onClick" icon="alarm" />
</q-fab>
</div>
</div>
</template>

<script>
export default {
methods: {
onClick () {
// console.log('Clicked on a fab action')
}
}
}
</script>

<style lang="sass" scoped>
.example-fab-animate,
.q-fab:hover .example-fab-animate--hover
animation: example-fab-animate 0.82s cubic-bezier(.36,.07,.19,.97) both
transform: translate3d(0, 0, 0)
backface-visibility: hidden
perspective: 1000px
@keyframes example-fab-animate
10%, 90%
transform: translate3d(-1px, 0, 0)
20%, 80%
transform: translate3d(2px, 0, 0)
30%, 50%, 70%
transform: translate3d(-4px, 0, 0)
40%, 60%
transform: translate3d(4px, 0, 0)
</style>
6 changes: 6 additions & 0 deletions docs/src/pages/vue-components/floating-action-button.md
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,12 @@ The default padding for QFab is "md" and for QFabAction is "sm". However, you ca

<doc-example title="Square style" file="QFab/SquareStyle" />

### Slots <q-badge align="top" color="brand-primary" label="v1.17+" />

Notice the slots for QFab and the slots for QFabAction below:

<doc-example title="Slots: icon, active-icon and label" file="QFab/FabSlots" />

### With QPageSticky

<doc-example title="With QPageSticky" file="QFab/PageSticky" />
Expand Down
57 changes: 54 additions & 3 deletions ui/dev/src/pages/components/fab.vue
Original file line number Diff line number Diff line change
Expand Up @@ -73,9 +73,42 @@
</q-fab-action>
</q-fab>

<div style="height: 1000px">
&nbsp;
</div>
<q-fab
class="fixed-bottom"
style="right: auto; bottom: 18px; left: 50%; transform: translateX(-50%)"
direction="up"
color="primary"
>
<template #icon="{ opened }">
<q-icon :class="{ 'test-fab-animate--hover': opened !== true }" :name="mdiMenu" />
</template>
<template #active-icon="{ opened }">
<q-icon :class="{ 'test-fab-animate': opened === true }" name="close" />
</template>
<template #label="{ opened }">
<div :class="{ 'test-fab-animate--hover': opened === true }">Label</div>
</template>

<q-fab-action color="blue" class="white" external-label>
<template #icon>
<q-icon name="person_add" />
</template>
<template #label>
Label
</template>

<q-tooltip anchor="center left" self="center right" :offset="[20, 0]">
Add person
</q-tooltip>
</q-fab-action>
<q-fab-action color="blue" class="white" icon="group_add">
<q-tooltip anchor="center left" self="center right" :offset="[20, 0]">
Add group
</q-tooltip>
</q-fab-action>
</q-fab>

<div style="height: 1000px">&nbsp;</div>

<div class="q-gutter-lg">
<q-btn fab :icon="mdiMenu" />
Expand Down Expand Up @@ -110,6 +143,24 @@
</div>
</template>

<style lang="sass">
.test-fab-animate,
.q-fab:hover .test-fab-animate--hover
animation: test-fab-animate 0.82s cubic-bezier(.36,.07,.19,.97) both
transform: translate3d(0, 0, 0)
backface-visibility: hidden
perspective: 1000px
@keyframes test-fab-animate
10%, 90%
transform: translate3d(-1px, 0, 0)
20%, 80%
transform: translate3d(2px, 0, 0)
30%, 50%, 70%
transform: translate3d(-4px, 0, 0)
40%, 60%
transform: translate3d(4px, 0, 0)
</style>

<script>
import { mdiMenu } from '@quasar/extras/mdi-v4'

Expand Down
31 changes: 20 additions & 11 deletions ui/src/components/fab/QFab.js
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,10 @@ export default Vue.extend({
'aria-haspopup': 'true',
...this.qAttrs
}
},

slotScope () {
return { opened: this.showing }
}
},

Expand All @@ -90,6 +94,15 @@ export default Vue.extend({
if (this.$refs.trigger && this.$refs.trigger.$el) {
this.$refs.trigger.$el.focus()
}
},

__getIcon (h, kebab, camel) {
const slotFn = this.$scopedSlots[kebab]
const staticClass = `q-fab__${kebab} absolute-full`

return slotFn === void 0
? h(QIcon, { staticClass, props: { name: this[kebab] || this.$q.iconSet.fab[camel] } })
: h('div', { staticClass }, slotFn(this.slotScope))
}
},

Expand All @@ -98,20 +111,16 @@ export default Vue.extend({

this.hideIcon !== true && child.push(
h('div', { staticClass: 'q-fab__icon-holder', class: this.iconHolderClasses }, [
h(QIcon, {
staticClass: 'q-fab__icon absolute-full',
props: { name: this.icon || this.$q.iconSet.fab.icon }
}),
h(QIcon, {
staticClass: 'q-fab__active-icon absolute-full',
props: { name: this.activeIcon || this.$q.iconSet.fab.activeIcon }
})
this.__getIcon(h, 'icon', 'icon'),
this.__getIcon(h, 'active-icon', 'activeIcon')
])
)

this.label !== '' && child[this.labelProps.action](
h('div', this.labelProps.data, [ this.label ])
)
if (this.label !== '' || this.$scopedSlots.label !== void 0) {
child[this.labelProps.action](
h('div', this.labelProps.data, this.$scopedSlots.label !== void 0 ? this.$scopedSlots.label(this.slotScope) : [ this.label ])
)
}

return h('div', {
staticClass: 'q-fab z-fab row inline justify-center',
Expand Down
35 changes: 35 additions & 0 deletions ui/src/components/fab/QFab.json
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,41 @@
}
},

"scopedSlots": {
"icon": {
"desc": "Slot for icon shown when FAB is closed; Suggestion: QIcon",
"scope": {
"opened": {
"type": "Boolean",
"desc": "FAB is opened"
}
},
"addedIn": "v1.17"
},

"active-icon": {
"desc": "Slot for icon shown when FAB is opened; Suggestion: QIcon",
"scope": {
"opened": {
"type": "Boolean",
"desc": "FAB is opened"
}
},
"addedIn": "v1.17"
},

"label": {
"desc": "Slot for label",
"scope": {
"opened": {
"type": "Boolean",
"desc": "FAB is opened"
}
},
"addedIn": "v1.17"
}
},

"events": {
"input": {
"desc": "Emitted when fab actions are shown/hidden; Captured by v-model directive"
Expand Down
25 changes: 16 additions & 9 deletions ui/src/components/fab/QFabAction.js
Original file line number Diff line number Diff line change
Expand Up @@ -75,15 +75,22 @@ export default Vue.extend({
render (h) {
const child = []

this.icon !== '' && child.push(
h(QIcon, {
props: { name: this.icon }
})
)

this.label !== '' && child[this.labelProps.action](
h('div', this.labelProps.data, [ this.label ])
)
if (this.$scopedSlots.icon !== void 0) {
child.push(this.$scopedSlots.icon())
}
else if (this.icon !== '') {
child.push(
h(QIcon, {
props: { name: this.icon }
})
)
}

if (this.label !== '' || this.$scopedSlots.label !== void 0) {
child[this.labelProps.action](
h('div', this.labelProps.data, this.$scopedSlots.label !== void 0 ? this.$scopedSlots.label() : [ this.label ])
)
}

return h(QBtn, {
class: this.classes,
Expand Down
10 changes: 10 additions & 0 deletions ui/src/components/fab/QFabAction.json
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,16 @@
"slots": {
"default": {
"desc": "Suggestion for this slot: QTooltip"
},

"icon": {
"desc": "Slot for icon; Suggestion: QIcon",
"addedIn": "v1.17"
},

"label": {
"desc": "Slot for label",
"addedIn": "v1.17"
}
},

Expand Down