Website Structure
This commit is contained in:
parent
62812f2090
commit
71f0676a62
22365 changed files with 4265753 additions and 791 deletions
382
Frontend-Learner/node_modules/quasar/src/components/btn/QBtn.js
generated
vendored
Normal file
382
Frontend-Learner/node_modules/quasar/src/components/btn/QBtn.js
generated
vendored
Normal file
|
|
@ -0,0 +1,382 @@
|
|||
import { h, ref, computed, Transition, onBeforeUnmount, withDirectives, getCurrentInstance } from 'vue'
|
||||
|
||||
import QIcon from '../icon/QIcon.js'
|
||||
import QSpinner from '../spinner/QSpinner.js'
|
||||
|
||||
import Ripple from '../../directives/ripple/Ripple.js'
|
||||
|
||||
import useBtn, { useBtnProps } from './use-btn.js'
|
||||
|
||||
import { createComponent } from '../../utils/private.create/create.js'
|
||||
import { hMergeSlot } from '../../utils/private.render/render.js'
|
||||
import { stop, prevent, stopAndPrevent, listenOpts } from '../../utils/event/event.js'
|
||||
import { isKeyCode } from '../../utils/private.keyboard/key-composition.js'
|
||||
|
||||
const { passiveCapture } = listenOpts
|
||||
|
||||
let
|
||||
touchTarget = null,
|
||||
keyboardTarget = null,
|
||||
mouseTarget = null
|
||||
|
||||
export default createComponent({
|
||||
name: 'QBtn',
|
||||
|
||||
props: {
|
||||
...useBtnProps,
|
||||
|
||||
percentage: Number,
|
||||
darkPercentage: Boolean,
|
||||
|
||||
onTouchstart: [ Function, Array ]
|
||||
},
|
||||
|
||||
emits: [ 'click', 'keydown', 'mousedown', 'keyup' ],
|
||||
|
||||
setup (props, { slots, emit }) {
|
||||
const { proxy } = getCurrentInstance()
|
||||
|
||||
const {
|
||||
classes, style, innerClasses,
|
||||
attributes,
|
||||
hasLink, linkTag, navigateOnClick,
|
||||
isActionable
|
||||
} = useBtn(props)
|
||||
|
||||
const rootRef = ref(null)
|
||||
const blurTargetRef = ref(null)
|
||||
|
||||
let localTouchTargetEl = null, avoidMouseRipple, mouseTimer = null
|
||||
|
||||
const hasLabel = computed(() =>
|
||||
props.label !== void 0 && props.label !== null && props.label !== ''
|
||||
)
|
||||
|
||||
const ripple = computed(() => (
|
||||
props.disable === true || props.ripple === false
|
||||
? false
|
||||
: {
|
||||
keyCodes: hasLink.value === true ? [ 13, 32 ] : [ 13 ],
|
||||
...(props.ripple === true ? {} : props.ripple)
|
||||
}
|
||||
))
|
||||
|
||||
const rippleProps = computed(() => ({ center: props.round }))
|
||||
|
||||
const percentageStyle = computed(() => {
|
||||
const val = Math.max(0, Math.min(100, props.percentage))
|
||||
return val > 0
|
||||
? { transition: 'transform 0.6s', transform: `translateX(${ val - 100 }%)` }
|
||||
: {}
|
||||
})
|
||||
|
||||
const onEvents = computed(() => {
|
||||
if (props.loading === true) {
|
||||
return {
|
||||
onMousedown: onLoadingEvt,
|
||||
onTouchstart: onLoadingEvt,
|
||||
onClick: onLoadingEvt,
|
||||
onKeydown: onLoadingEvt,
|
||||
onKeyup: onLoadingEvt
|
||||
}
|
||||
}
|
||||
|
||||
if (isActionable.value === true) {
|
||||
const acc = {
|
||||
onClick,
|
||||
onKeydown,
|
||||
onMousedown
|
||||
}
|
||||
|
||||
if (proxy.$q.platform.has.touch === true) {
|
||||
const suffix = props.onTouchstart !== void 0
|
||||
? ''
|
||||
: 'Passive'
|
||||
|
||||
acc[ `onTouchstart${ suffix }` ] = onTouchstart
|
||||
}
|
||||
|
||||
return acc
|
||||
}
|
||||
|
||||
return {
|
||||
// needed; especially for disabled <a> tags
|
||||
onClick: stopAndPrevent
|
||||
}
|
||||
})
|
||||
|
||||
const nodeProps = computed(() => ({
|
||||
ref: rootRef,
|
||||
class: 'q-btn q-btn-item non-selectable no-outline ' + classes.value,
|
||||
style: style.value,
|
||||
...attributes.value,
|
||||
...onEvents.value
|
||||
}))
|
||||
|
||||
function onClick (e) {
|
||||
// is it already destroyed?
|
||||
if (rootRef.value === null) return
|
||||
|
||||
if (e !== void 0) {
|
||||
if (e.defaultPrevented === true) return
|
||||
|
||||
const el = document.activeElement
|
||||
// focus button if it came from ENTER on form
|
||||
// prevent the new submit (already done)
|
||||
if (
|
||||
props.type === 'submit'
|
||||
&& el !== document.body
|
||||
&& rootRef.value.contains(el) === false
|
||||
// required for iOS and desktop Safari
|
||||
&& el.contains(rootRef.value) === false
|
||||
) {
|
||||
e.qAvoidFocus !== true && rootRef.value.focus()
|
||||
|
||||
const onClickCleanup = () => {
|
||||
document.removeEventListener('keydown', stopAndPrevent, true)
|
||||
document.removeEventListener('keyup', onClickCleanup, passiveCapture)
|
||||
rootRef.value?.removeEventListener('blur', onClickCleanup, passiveCapture)
|
||||
}
|
||||
|
||||
document.addEventListener('keydown', stopAndPrevent, true)
|
||||
document.addEventListener('keyup', onClickCleanup, passiveCapture)
|
||||
rootRef.value.addEventListener('blur', onClickCleanup, passiveCapture)
|
||||
}
|
||||
}
|
||||
|
||||
navigateOnClick(e)
|
||||
}
|
||||
|
||||
function onKeydown (e) {
|
||||
// is it already destroyed?
|
||||
if (rootRef.value === null) return
|
||||
|
||||
emit('keydown', e)
|
||||
|
||||
if (isKeyCode(e, [ 13, 32 ]) === true && keyboardTarget !== rootRef.value) {
|
||||
keyboardTarget !== null && cleanup()
|
||||
|
||||
if (e.defaultPrevented !== true) {
|
||||
// focus external button if the focus helper was focused before
|
||||
e.qAvoidFocus !== true && rootRef.value.focus()
|
||||
|
||||
keyboardTarget = rootRef.value
|
||||
rootRef.value.classList.add('q-btn--active')
|
||||
document.addEventListener('keyup', onPressEnd, true)
|
||||
rootRef.value.addEventListener('blur', onPressEnd, passiveCapture)
|
||||
}
|
||||
|
||||
stopAndPrevent(e)
|
||||
}
|
||||
}
|
||||
|
||||
function onTouchstart (e) {
|
||||
// is it already destroyed?
|
||||
if (rootRef.value === null) return
|
||||
|
||||
emit('touchstart', e)
|
||||
|
||||
if (e.defaultPrevented === true) return
|
||||
|
||||
if (touchTarget !== rootRef.value) {
|
||||
touchTarget !== null && cleanup()
|
||||
touchTarget = rootRef.value
|
||||
|
||||
localTouchTargetEl = e.target
|
||||
localTouchTargetEl.addEventListener('touchcancel', onPressEnd, passiveCapture)
|
||||
localTouchTargetEl.addEventListener('touchend', onPressEnd, passiveCapture)
|
||||
}
|
||||
|
||||
// avoid duplicated mousedown event
|
||||
// triggering another early ripple
|
||||
avoidMouseRipple = true
|
||||
mouseTimer !== null && clearTimeout(mouseTimer)
|
||||
mouseTimer = setTimeout(() => {
|
||||
mouseTimer = null
|
||||
avoidMouseRipple = false
|
||||
}, 200)
|
||||
}
|
||||
|
||||
function onMousedown (e) {
|
||||
// is it already destroyed?
|
||||
if (rootRef.value === null) return
|
||||
|
||||
e.qSkipRipple = avoidMouseRipple === true
|
||||
emit('mousedown', e)
|
||||
|
||||
if (e.defaultPrevented !== true && mouseTarget !== rootRef.value) {
|
||||
mouseTarget !== null && cleanup()
|
||||
mouseTarget = rootRef.value
|
||||
rootRef.value.classList.add('q-btn--active')
|
||||
document.addEventListener('mouseup', onPressEnd, passiveCapture)
|
||||
}
|
||||
}
|
||||
|
||||
function onPressEnd (e) {
|
||||
// is it already destroyed?
|
||||
if (rootRef.value === null) return
|
||||
|
||||
// needed for IE (because it emits blur when focusing button from focus helper)
|
||||
if (
|
||||
e?.type === 'blur'
|
||||
&& document.activeElement === rootRef.value
|
||||
) return
|
||||
|
||||
if (e?.type === 'keyup') {
|
||||
if (keyboardTarget === rootRef.value && isKeyCode(e, [ 13, 32 ]) === true) {
|
||||
// for click trigger
|
||||
const evt = new MouseEvent('click', e)
|
||||
evt.qKeyEvent = true
|
||||
e.defaultPrevented === true && prevent(evt)
|
||||
e.cancelBubble === true && stop(evt)
|
||||
rootRef.value.dispatchEvent(evt)
|
||||
|
||||
stopAndPrevent(e)
|
||||
|
||||
// for ripple
|
||||
e.qKeyEvent = true
|
||||
}
|
||||
|
||||
emit('keyup', e)
|
||||
}
|
||||
|
||||
cleanup()
|
||||
}
|
||||
|
||||
function cleanup (destroying) {
|
||||
const blurTarget = blurTargetRef.value
|
||||
|
||||
if (
|
||||
destroying !== true
|
||||
&& (touchTarget === rootRef.value || mouseTarget === rootRef.value)
|
||||
&& blurTarget !== null
|
||||
&& blurTarget !== document.activeElement
|
||||
) {
|
||||
blurTarget.setAttribute('tabindex', -1)
|
||||
blurTarget.focus()
|
||||
}
|
||||
|
||||
if (touchTarget === rootRef.value) {
|
||||
if (localTouchTargetEl !== null) {
|
||||
localTouchTargetEl.removeEventListener('touchcancel', onPressEnd, passiveCapture)
|
||||
localTouchTargetEl.removeEventListener('touchend', onPressEnd, passiveCapture)
|
||||
}
|
||||
touchTarget = localTouchTargetEl = null
|
||||
}
|
||||
|
||||
if (mouseTarget === rootRef.value) {
|
||||
document.removeEventListener('mouseup', onPressEnd, passiveCapture)
|
||||
mouseTarget = null
|
||||
}
|
||||
|
||||
if (keyboardTarget === rootRef.value) {
|
||||
document.removeEventListener('keyup', onPressEnd, true)
|
||||
rootRef.value?.removeEventListener('blur', onPressEnd, passiveCapture)
|
||||
keyboardTarget = null
|
||||
}
|
||||
|
||||
rootRef.value?.classList.remove('q-btn--active')
|
||||
}
|
||||
|
||||
function onLoadingEvt (evt) {
|
||||
stopAndPrevent(evt)
|
||||
evt.qSkipRipple = true
|
||||
}
|
||||
|
||||
onBeforeUnmount(() => {
|
||||
cleanup(true)
|
||||
})
|
||||
|
||||
// expose public methods
|
||||
Object.assign(proxy, {
|
||||
click: e => {
|
||||
if (isActionable.value === true) {
|
||||
onClick(e)
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
return () => {
|
||||
let inner = []
|
||||
|
||||
props.icon !== void 0 && inner.push(
|
||||
h(QIcon, {
|
||||
name: props.icon,
|
||||
left: props.stack !== true && hasLabel.value === true,
|
||||
role: 'img'
|
||||
})
|
||||
)
|
||||
|
||||
hasLabel.value === true && inner.push(
|
||||
h('span', { class: 'block' }, [ props.label ])
|
||||
)
|
||||
|
||||
inner = hMergeSlot(slots.default, inner)
|
||||
|
||||
if (props.iconRight !== void 0 && props.round === false) {
|
||||
inner.push(
|
||||
h(QIcon, {
|
||||
name: props.iconRight,
|
||||
right: props.stack !== true && hasLabel.value === true,
|
||||
role: 'img'
|
||||
})
|
||||
)
|
||||
}
|
||||
|
||||
const child = [
|
||||
h('span', {
|
||||
class: 'q-focus-helper',
|
||||
ref: blurTargetRef
|
||||
})
|
||||
]
|
||||
|
||||
if (props.loading === true && props.percentage !== void 0) {
|
||||
child.push(
|
||||
h('span', {
|
||||
class: 'q-btn__progress absolute-full overflow-hidden' + (props.darkPercentage === true ? ' q-btn__progress--dark' : '')
|
||||
}, [
|
||||
h('span', {
|
||||
class: 'q-btn__progress-indicator fit block',
|
||||
style: percentageStyle.value
|
||||
})
|
||||
])
|
||||
)
|
||||
}
|
||||
|
||||
child.push(
|
||||
h('span', {
|
||||
class: 'q-btn__content text-center col items-center q-anchor--skip ' + innerClasses.value
|
||||
}, inner)
|
||||
)
|
||||
|
||||
props.loading !== null && child.push(
|
||||
h(Transition, {
|
||||
name: 'q-transition--fade'
|
||||
}, () => (
|
||||
props.loading === true
|
||||
? [
|
||||
h('span', {
|
||||
key: 'loading',
|
||||
class: 'absolute-full flex flex-center'
|
||||
}, slots.loading !== void 0 ? slots.loading() : [ h(QSpinner) ])
|
||||
]
|
||||
: null
|
||||
))
|
||||
)
|
||||
|
||||
return withDirectives(
|
||||
h(
|
||||
linkTag.value,
|
||||
nodeProps.value,
|
||||
child
|
||||
),
|
||||
[ [
|
||||
Ripple,
|
||||
ripple.value,
|
||||
void 0,
|
||||
rippleProps.value
|
||||
] ]
|
||||
)
|
||||
}
|
||||
}
|
||||
})
|
||||
106
Frontend-Learner/node_modules/quasar/src/components/btn/QBtn.json
generated
vendored
Normal file
106
Frontend-Learner/node_modules/quasar/src/components/btn/QBtn.json
generated
vendored
Normal file
|
|
@ -0,0 +1,106 @@
|
|||
{
|
||||
"mixins": [ "components/btn/use-btn" ],
|
||||
|
||||
"meta": {
|
||||
"docsUrl": "https://v2.quasar.dev/vue-components/button"
|
||||
},
|
||||
|
||||
"props": {
|
||||
"round": {
|
||||
"type": "Boolean",
|
||||
"desc": "Makes a circle shaped button",
|
||||
"category": "style"
|
||||
},
|
||||
|
||||
"percentage": {
|
||||
"type": "Number",
|
||||
"desc": "Percentage (0.0 < x < 100.0); To be used along 'loading' prop; Display a progress bar on the background",
|
||||
"category": "behavior"
|
||||
},
|
||||
|
||||
"dark-percentage": {
|
||||
"type": "Boolean",
|
||||
"desc": "Progress bar on the background should have dark color; To be used along with 'percentage' and 'loading' props",
|
||||
"category": "behavior"
|
||||
}
|
||||
},
|
||||
|
||||
"slots": {
|
||||
"default": {
|
||||
"desc": "Use for custom content, instead of relying on 'icon' and 'label' props"
|
||||
},
|
||||
|
||||
"loading": {
|
||||
"desc": "Override the default QSpinner when in 'loading' state"
|
||||
}
|
||||
},
|
||||
|
||||
"methods": {
|
||||
"click": {
|
||||
"desc": "Emulate click on QBtn",
|
||||
"params": {
|
||||
"evt": {
|
||||
"extends": "evt"
|
||||
}
|
||||
},
|
||||
"returns": null
|
||||
}
|
||||
},
|
||||
|
||||
"events": {
|
||||
"click": {
|
||||
"desc": "Emitted when the component is clicked",
|
||||
"params": {
|
||||
"evt": {
|
||||
"extends": "evt",
|
||||
"desc": "JS event object; If you are using route navigation ('to'/'replace' props) and you want to cancel navigation then call evt.preventDefault() synchronously in your event handler"
|
||||
},
|
||||
"go": {
|
||||
"type": "Function",
|
||||
"desc": "Available ONLY if you are using route navigation ('to'/'replace' props); When you need to control the time at which the component should trigger the route navigation then call evt.preventDefault() synchronously and then call this function at your convenience; Useful if you have async work to be done before the actual route navigation or if you want to redirect somewhere else",
|
||||
"required": false,
|
||||
"addedIn": "v2.9",
|
||||
"params": {
|
||||
"opts": {
|
||||
"type": "Object",
|
||||
"desc": "Optional options",
|
||||
"required": false,
|
||||
"definition": {
|
||||
"to": {
|
||||
"type": [ "String", "Object" ],
|
||||
"desc": "Equivalent to Vue Router <router-link> 'to' property; Specify it explicitly otherwise it will be set with same value as component's 'to' prop",
|
||||
"required": false,
|
||||
"examples": [
|
||||
"'/home/dashboard'",
|
||||
"{ name: 'my-route-name' }"
|
||||
]
|
||||
},
|
||||
|
||||
"replace": {
|
||||
"type": "Boolean",
|
||||
"desc": "Equivalent to Vue Router <router-link> 'replace' property; Specify it explicitly otherwise it will be set with same value as component's 'replace' prop",
|
||||
"required": false
|
||||
},
|
||||
|
||||
"returnRouterError": {
|
||||
"type": "Boolean",
|
||||
"desc": "Return the router error, if any; Otherwise the returned Promise will always fulfill",
|
||||
"required": false
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"returns": {
|
||||
"type": "Promise<any>",
|
||||
"desc": "Returns the router's navigation promise"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
"touchstart": { "internal": true },
|
||||
"keydown": { "internal": true },
|
||||
"keyup": { "internal": true },
|
||||
"mousedown": { "internal": true }
|
||||
}
|
||||
}
|
||||
159
Frontend-Learner/node_modules/quasar/src/components/btn/QBtn.sass
generated
vendored
Normal file
159
Frontend-Learner/node_modules/quasar/src/components/btn/QBtn.sass
generated
vendored
Normal file
|
|
@ -0,0 +1,159 @@
|
|||
.q-btn
|
||||
display: inline-flex
|
||||
flex-direction: column
|
||||
align-items: stretch
|
||||
position: relative
|
||||
outline: 0
|
||||
border: 0
|
||||
vertical-align: middle
|
||||
font-size: $button-font-size
|
||||
line-height: $button-line-height
|
||||
text-decoration: none
|
||||
color: inherit
|
||||
background: transparent
|
||||
font-weight: $button-font-weight
|
||||
text-transform: uppercase
|
||||
text-align: center
|
||||
width: auto
|
||||
height: auto
|
||||
cursor: default
|
||||
padding: $button-padding
|
||||
min-height: 2.572em
|
||||
|
||||
.q-icon, .q-spinner
|
||||
font-size: $button-line-height
|
||||
|
||||
&.disabled
|
||||
opacity: .7 !important
|
||||
|
||||
&:before
|
||||
content: ''
|
||||
display: block
|
||||
position: absolute
|
||||
left: 0
|
||||
right: 0
|
||||
top: 0
|
||||
bottom: 0
|
||||
border-radius: inherit
|
||||
box-shadow: $button-shadow
|
||||
|
||||
&--actionable
|
||||
cursor: pointer
|
||||
|
||||
&.q-btn--standard
|
||||
&:before
|
||||
// This places the button active raise shadow behind adjacent elements
|
||||
// Active raise shadow will still be visible under adjacent transparent elements, this is ok and coherent with a desired transparency effect.
|
||||
// Visible active raise shadow can be removed by specifying a background color to the button
|
||||
// Visible active raise shadow can be removed by specifying a flat or outline button type
|
||||
transition: box-shadow $button-transition
|
||||
&:active,
|
||||
&.q-btn--active
|
||||
&:before
|
||||
box-shadow: $button-shadow-active
|
||||
|
||||
&--no-uppercase
|
||||
text-transform: none
|
||||
|
||||
&--rectangle
|
||||
border-radius: $button-border-radius
|
||||
|
||||
&--outline
|
||||
background: transparent !important
|
||||
|
||||
&:before
|
||||
border: 1px solid currentColor
|
||||
|
||||
&--push
|
||||
border-radius: $button-push-border-radius
|
||||
|
||||
&:before
|
||||
border-bottom: 3px solid rgba(0,0,0,.15)
|
||||
|
||||
&.q-btn--actionable
|
||||
transition: transform $button-transition
|
||||
|
||||
&:before
|
||||
transition: border-width $button-transition
|
||||
|
||||
&:active,
|
||||
&.q-btn--active
|
||||
transform: translateY(2px)
|
||||
|
||||
&:before
|
||||
border-bottom-width: 0
|
||||
|
||||
&--rounded
|
||||
border-radius: $button-rounded-border-radius
|
||||
|
||||
&--round
|
||||
border-radius: 50%
|
||||
|
||||
padding: 0
|
||||
min-width: 3em
|
||||
min-height: 3em
|
||||
|
||||
&--square
|
||||
border-radius: 0
|
||||
|
||||
&--flat, &--outline, &--unelevated
|
||||
&:before
|
||||
box-shadow: none
|
||||
|
||||
&--dense
|
||||
padding: $button-dense-padding
|
||||
min-height: 2em
|
||||
|
||||
&.q-btn--round
|
||||
padding: 0
|
||||
min-height: 2.4em
|
||||
min-width: 2.4em
|
||||
|
||||
.on-left
|
||||
margin-right: 6px
|
||||
|
||||
.on-right
|
||||
margin-left: 6px
|
||||
|
||||
&--fab, &--fab-mini
|
||||
.q-icon
|
||||
font-size: $button-fab-icon-font-size
|
||||
|
||||
&--fab
|
||||
padding: 16px
|
||||
min-height: 56px
|
||||
min-width: 56px
|
||||
|
||||
.q-icon
|
||||
margin: auto
|
||||
|
||||
&--fab-mini
|
||||
padding: 8px
|
||||
min-height: 40px
|
||||
min-width: 40px
|
||||
|
||||
// workaround for alignment/sizing change when showing loader
|
||||
&__content
|
||||
transition: opacity .3s
|
||||
z-index: 0
|
||||
|
||||
&--hidden
|
||||
opacity: 0
|
||||
pointer-events: none
|
||||
|
||||
&__progress
|
||||
border-radius: inherit
|
||||
z-index: 0
|
||||
|
||||
&-indicator
|
||||
z-index: -1
|
||||
transform: translateX(-100%)
|
||||
background: rgba(255,255,255,.25)
|
||||
&--dark
|
||||
.q-btn__progress-indicator
|
||||
background: rgba(0,0,0,.2)
|
||||
|
||||
&--flat, &--outline
|
||||
.q-btn__progress-indicator
|
||||
opacity: 0.2
|
||||
background: currentColor
|
||||
1386
Frontend-Learner/node_modules/quasar/src/components/btn/QBtn.test.js
generated
vendored
Normal file
1386
Frontend-Learner/node_modules/quasar/src/components/btn/QBtn.test.js
generated
vendored
Normal file
File diff suppressed because it is too large
Load diff
5
Frontend-Learner/node_modules/quasar/src/components/btn/index.js
generated
vendored
Normal file
5
Frontend-Learner/node_modules/quasar/src/components/btn/index.js
generated
vendored
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
import QBtn from './QBtn.js'
|
||||
|
||||
export {
|
||||
QBtn
|
||||
}
|
||||
225
Frontend-Learner/node_modules/quasar/src/components/btn/use-btn.js
generated
vendored
Normal file
225
Frontend-Learner/node_modules/quasar/src/components/btn/use-btn.js
generated
vendored
Normal file
|
|
@ -0,0 +1,225 @@
|
|||
import { computed } from 'vue'
|
||||
|
||||
import useAlign, { useAlignProps } from '../../composables/private.use-align/use-align.js'
|
||||
import useSize, { useSizeProps } from '../../composables/private.use-size/use-size.js'
|
||||
import useRouterLink, { useRouterLinkNonMatchingProps } from '../../composables/private.use-router-link/use-router-link.js'
|
||||
|
||||
export const btnPadding = {
|
||||
none: 0,
|
||||
xs: 4,
|
||||
sm: 8,
|
||||
md: 16,
|
||||
lg: 24,
|
||||
xl: 32
|
||||
}
|
||||
|
||||
export const defaultSizes = {
|
||||
xs: 8,
|
||||
sm: 10,
|
||||
md: 14,
|
||||
lg: 20,
|
||||
xl: 24
|
||||
}
|
||||
|
||||
const formTypes = [ 'button', 'submit', 'reset' ]
|
||||
const mediaTypeRE = /[^\s]\/[^\s]/
|
||||
|
||||
export const btnDesignOptions = [ 'flat', 'outline', 'push', 'unelevated' ]
|
||||
|
||||
export function getBtnDesign (props, defaultValue) {
|
||||
if (props.flat === true) return 'flat'
|
||||
if (props.outline === true) return 'outline'
|
||||
if (props.push === true) return 'push'
|
||||
if (props.unelevated === true) return 'unelevated'
|
||||
return defaultValue
|
||||
}
|
||||
|
||||
export function getBtnDesignAttr (props) {
|
||||
const design = getBtnDesign(props)
|
||||
return design !== void 0
|
||||
? { [ design ]: true }
|
||||
: {}
|
||||
}
|
||||
|
||||
export const nonRoundBtnProps = {
|
||||
...useSizeProps,
|
||||
...useRouterLinkNonMatchingProps,
|
||||
|
||||
type: {
|
||||
type: String,
|
||||
default: 'button'
|
||||
},
|
||||
|
||||
label: [ Number, String ],
|
||||
icon: String,
|
||||
iconRight: String,
|
||||
|
||||
...btnDesignOptions.reduce(
|
||||
(acc, val) => (acc[ val ] = Boolean) && acc,
|
||||
{}
|
||||
),
|
||||
|
||||
square: Boolean,
|
||||
rounded: Boolean,
|
||||
glossy: Boolean,
|
||||
|
||||
size: String,
|
||||
fab: Boolean,
|
||||
fabMini: Boolean,
|
||||
padding: String,
|
||||
|
||||
color: String,
|
||||
textColor: String,
|
||||
noCaps: Boolean,
|
||||
noWrap: Boolean,
|
||||
dense: Boolean,
|
||||
|
||||
tabindex: [ Number, String ],
|
||||
|
||||
ripple: {
|
||||
type: [ Boolean, Object ],
|
||||
default: true
|
||||
},
|
||||
|
||||
align: {
|
||||
...useAlignProps.align,
|
||||
default: 'center'
|
||||
},
|
||||
stack: Boolean,
|
||||
stretch: Boolean,
|
||||
loading: {
|
||||
type: Boolean,
|
||||
default: null
|
||||
},
|
||||
disable: Boolean
|
||||
}
|
||||
|
||||
export const useBtnProps = {
|
||||
...nonRoundBtnProps,
|
||||
round: Boolean
|
||||
}
|
||||
|
||||
export default function (props) {
|
||||
const sizeStyle = useSize(props, defaultSizes)
|
||||
const alignClass = useAlign(props)
|
||||
const { hasRouterLink, hasLink, linkTag, linkAttrs, navigateOnClick } = useRouterLink({
|
||||
fallbackTag: 'button'
|
||||
})
|
||||
|
||||
const style = computed(() => {
|
||||
const obj = props.fab === false && props.fabMini === false
|
||||
? sizeStyle.value
|
||||
: {}
|
||||
|
||||
return props.padding !== void 0
|
||||
? Object.assign({}, obj, {
|
||||
padding: props.padding
|
||||
.split(/\s+/)
|
||||
.map(v => (v in btnPadding ? btnPadding[ v ] + 'px' : v))
|
||||
.join(' '),
|
||||
minWidth: '0',
|
||||
minHeight: '0'
|
||||
})
|
||||
: obj
|
||||
})
|
||||
|
||||
const isRounded = computed(() =>
|
||||
props.rounded === true || props.fab === true || props.fabMini === true
|
||||
)
|
||||
|
||||
const isActionable = computed(() =>
|
||||
props.disable !== true && props.loading !== true
|
||||
)
|
||||
|
||||
const tabIndex = computed(() => (
|
||||
isActionable.value === true ? props.tabindex || 0 : -1
|
||||
))
|
||||
|
||||
const design = computed(() => getBtnDesign(props, 'standard'))
|
||||
|
||||
const attributes = computed(() => {
|
||||
const acc = { tabindex: tabIndex.value }
|
||||
|
||||
if (hasLink.value === true) {
|
||||
Object.assign(acc, linkAttrs.value)
|
||||
}
|
||||
else if (formTypes.includes(props.type) === true) {
|
||||
acc.type = props.type
|
||||
}
|
||||
|
||||
if (linkTag.value === 'a') {
|
||||
if (props.disable === true) {
|
||||
acc[ 'aria-disabled' ] = 'true'
|
||||
}
|
||||
else if (acc.href === void 0) {
|
||||
acc.role = 'button'
|
||||
}
|
||||
|
||||
if (hasRouterLink.value !== true && mediaTypeRE.test(props.type) === true) {
|
||||
acc.type = props.type
|
||||
}
|
||||
}
|
||||
else if (props.disable === true) {
|
||||
acc.disabled = ''
|
||||
acc[ 'aria-disabled' ] = 'true'
|
||||
}
|
||||
|
||||
if (props.loading === true && props.percentage !== void 0) {
|
||||
Object.assign(acc, {
|
||||
role: 'progressbar',
|
||||
'aria-valuemin': 0,
|
||||
'aria-valuemax': 100,
|
||||
'aria-valuenow': props.percentage
|
||||
})
|
||||
}
|
||||
|
||||
return acc
|
||||
})
|
||||
|
||||
const classes = computed(() => {
|
||||
let colors
|
||||
|
||||
if (props.color !== void 0) {
|
||||
if (props.flat === true || props.outline === true) {
|
||||
colors = `text-${ props.textColor || props.color }`
|
||||
}
|
||||
else {
|
||||
colors = `bg-${ props.color } text-${ props.textColor || 'white' }`
|
||||
}
|
||||
}
|
||||
else if (props.textColor) {
|
||||
colors = `text-${ props.textColor }`
|
||||
}
|
||||
|
||||
const shape = props.round === true
|
||||
? 'round'
|
||||
: `rectangle${ isRounded.value === true ? ' q-btn--rounded' : (props.square === true ? ' q-btn--square' : '') }`
|
||||
|
||||
return `q-btn--${ design.value } q-btn--${ shape }`
|
||||
+ (colors !== void 0 ? ' ' + colors : '')
|
||||
+ (isActionable.value === true ? ' q-btn--actionable q-focusable q-hoverable' : (props.disable === true ? ' disabled' : ''))
|
||||
+ (props.fab === true ? ' q-btn--fab' : (props.fabMini === true ? ' q-btn--fab-mini' : ''))
|
||||
+ (props.noCaps === true ? ' q-btn--no-uppercase' : '')
|
||||
+ (props.dense === true ? ' q-btn--dense' : '')
|
||||
+ (props.stretch === true ? ' no-border-radius self-stretch' : '')
|
||||
+ (props.glossy === true ? ' glossy' : '')
|
||||
+ (props.square ? ' q-btn--square' : '')
|
||||
})
|
||||
|
||||
const innerClasses = computed(() =>
|
||||
alignClass.value + (props.stack === true ? ' column' : ' row')
|
||||
+ (props.noWrap === true ? ' no-wrap text-no-wrap' : '')
|
||||
+ (props.loading === true ? ' q-btn__content--hidden' : '')
|
||||
)
|
||||
|
||||
return {
|
||||
classes,
|
||||
style,
|
||||
innerClasses,
|
||||
attributes,
|
||||
hasLink,
|
||||
linkTag,
|
||||
navigateOnClick,
|
||||
isActionable
|
||||
}
|
||||
}
|
||||
187
Frontend-Learner/node_modules/quasar/src/components/btn/use-btn.json
generated
vendored
Normal file
187
Frontend-Learner/node_modules/quasar/src/components/btn/use-btn.json
generated
vendored
Normal file
|
|
@ -0,0 +1,187 @@
|
|||
{
|
||||
"mixins": [ "composables/private.use-size/use-size" ],
|
||||
|
||||
"props": {
|
||||
"type":{
|
||||
"type": "String",
|
||||
"desc": "1) Define the button native type attribute (submit, reset, button) or 2) render component with <a> tag so you can access events even if disable or 3) Use 'href' prop and specify 'type' as a media tag",
|
||||
"default": "'button'",
|
||||
"examples": [
|
||||
"'a'", "'submit'", "'button'", "'reset'",
|
||||
"'image/png'",
|
||||
"# href=\"https://quasar.dev\" target=\"_blank\""
|
||||
],
|
||||
"category": "general"
|
||||
},
|
||||
|
||||
"to": {
|
||||
"type": [ "String", "Object" ],
|
||||
"desc": "Equivalent to Vue Router <router-link> 'to' property; Superseded by 'href' prop if used",
|
||||
"examples": [
|
||||
"'/home/dashboard'",
|
||||
"{ name: 'my-route-name' }"
|
||||
],
|
||||
"category": "navigation"
|
||||
},
|
||||
|
||||
"replace": {
|
||||
"type": "Boolean",
|
||||
"desc": "Equivalent to Vue Router <router-link> 'replace' property; Superseded by 'href' prop if used",
|
||||
"category": "navigation"
|
||||
},
|
||||
|
||||
"href": {
|
||||
"type": "String",
|
||||
"desc": "Native <a> link href attribute; Has priority over the 'to' and 'replace' props",
|
||||
"examples": [ "'https://quasar.dev'", "# href=\"https://quasar.dev\" target=\"_blank\"" ],
|
||||
"category": "navigation",
|
||||
"addedIn": "v2.4"
|
||||
},
|
||||
|
||||
"target": {
|
||||
"type": "String",
|
||||
"desc": "Native <a> link target attribute; Use it only with 'to' or 'href' props",
|
||||
"examples": [ "'_blank'", "'_self'", "'_parent'", "'_top'" ],
|
||||
"category": "navigation",
|
||||
"addedIn": "v2.4"
|
||||
},
|
||||
|
||||
"label":{
|
||||
"type": [ "String", "Number" ],
|
||||
"desc": "The text that will be shown on the button",
|
||||
"examples": [ "'Button Label'" ],
|
||||
"category": "content"
|
||||
},
|
||||
|
||||
"icon": {
|
||||
"extends": "icon"
|
||||
},
|
||||
|
||||
"icon-right": {
|
||||
"extends": "icon"
|
||||
},
|
||||
|
||||
"outline": {
|
||||
"type": "Boolean",
|
||||
"desc": "Use 'outline' design",
|
||||
"category": "style"
|
||||
},
|
||||
|
||||
"flat": {
|
||||
"type": "Boolean",
|
||||
"desc": "Use 'flat' design",
|
||||
"category": "style"
|
||||
},
|
||||
|
||||
"unelevated": {
|
||||
"type": "Boolean",
|
||||
"desc": "Remove shadow",
|
||||
"category": "style"
|
||||
},
|
||||
|
||||
"rounded": {
|
||||
"type": "Boolean",
|
||||
"desc": "Applies a more prominent border-radius for a squared shape button",
|
||||
"category": "style"
|
||||
},
|
||||
|
||||
"push": {
|
||||
"type": "Boolean",
|
||||
"desc": "Use 'push' design",
|
||||
"category": "style"
|
||||
},
|
||||
|
||||
"square": {
|
||||
"extends": "square",
|
||||
"addedIn": "v2.7.6"
|
||||
},
|
||||
|
||||
"glossy": {
|
||||
"type": "Boolean",
|
||||
"desc": "Applies a glossy effect",
|
||||
"category": "style"
|
||||
},
|
||||
|
||||
"fab": {
|
||||
"type": "Boolean",
|
||||
"desc": "Makes button size and shape to fit a Floating Action Button",
|
||||
"category": "style"
|
||||
},
|
||||
|
||||
"fab-mini": {
|
||||
"type": "Boolean",
|
||||
"desc": "Makes button size and shape to fit a small Floating Action Button",
|
||||
"category": "style"
|
||||
},
|
||||
|
||||
"padding": {
|
||||
"type": "String",
|
||||
"desc": "Apply custom padding (vertical [horizontal]); Size in CSS units, including unit name or standard size name (none|xs|sm|md|lg|xl); Also removes the min width and height when set",
|
||||
"examples": [ "'16px'", "'10px 5px'", "'2rem'", "'xs'", "'md lg'", "'2px 2px 5px 7px'" ],
|
||||
"category": "style"
|
||||
},
|
||||
|
||||
"color": {
|
||||
"extends": "color"
|
||||
},
|
||||
|
||||
"text-color": {
|
||||
"extends": "text-color"
|
||||
},
|
||||
|
||||
"no-caps": {
|
||||
"type": "Boolean",
|
||||
"desc": "Avoid turning label text into caps (which happens by default)",
|
||||
"category": "content"
|
||||
},
|
||||
|
||||
"no-wrap": {
|
||||
"type": "Boolean",
|
||||
"desc": "Avoid label text wrapping",
|
||||
"category": "content"
|
||||
},
|
||||
|
||||
"dense": {
|
||||
"extends": "dense"
|
||||
},
|
||||
|
||||
"ripple": {
|
||||
"extends": "ripple"
|
||||
},
|
||||
|
||||
"tabindex": {
|
||||
"extends": "tabindex"
|
||||
},
|
||||
|
||||
"align": {
|
||||
"type": "String",
|
||||
"desc": "Label or content alignment",
|
||||
"default": "'center'",
|
||||
"values": [ "'left'", "'right'", "'center'", "'around'", "'between'", "'evenly'" ],
|
||||
"category": "content"
|
||||
},
|
||||
|
||||
"stack": {
|
||||
"type": "Boolean",
|
||||
"desc": "Stack icon and label vertically instead of on same line (like it is by default)",
|
||||
"category": "content"
|
||||
},
|
||||
|
||||
"stretch": {
|
||||
"type": "Boolean",
|
||||
"desc": "When used on flexbox parent, button will stretch to parent's height",
|
||||
"category": "content"
|
||||
},
|
||||
|
||||
"loading": {
|
||||
"type": [ "Boolean", "null" ],
|
||||
"default": "null",
|
||||
"desc": "Put button into loading state (displays a QSpinner -- can be overridden by using a 'loading' slot)",
|
||||
"category": "behavior|state"
|
||||
},
|
||||
|
||||
"disable": {
|
||||
"extends": "disable"
|
||||
}
|
||||
}
|
||||
}
|
||||
142
Frontend-Learner/node_modules/quasar/src/components/btn/use-btn.test.js
generated
vendored
Normal file
142
Frontend-Learner/node_modules/quasar/src/components/btn/use-btn.test.js
generated
vendored
Normal file
|
|
@ -0,0 +1,142 @@
|
|||
import { mount } from '@vue/test-utils'
|
||||
import { describe, test, expect } from 'vitest'
|
||||
import { defineComponent } from 'vue'
|
||||
|
||||
import useBtn, {
|
||||
btnPadding, defaultSizes, btnDesignOptions,
|
||||
useBtnProps, nonRoundBtnProps,
|
||||
getBtnDesign, getBtnDesignAttr
|
||||
} from './use-btn.js'
|
||||
|
||||
describe('[useBtn API]', () => {
|
||||
describe('[Variables]', () => {
|
||||
describe('[(variable)btnPadding]', () => {
|
||||
test('is defined correctly', () => {
|
||||
expect(btnPadding).toBeTypeOf('object')
|
||||
expect(Object.keys(btnPadding)).not.toHaveLength(0)
|
||||
})
|
||||
})
|
||||
|
||||
describe('[(variable)defaultSizes]', () => {
|
||||
test('is defined correctly', () => {
|
||||
expect(defaultSizes).toBeTypeOf('object')
|
||||
expect(Object.keys(defaultSizes)).not.toHaveLength(0)
|
||||
})
|
||||
})
|
||||
|
||||
describe('[(variable)btnDesignOptions]', () => {
|
||||
test('is defined correctly', () => {
|
||||
expect(btnDesignOptions).toBeTypeOf('object')
|
||||
expect(Object.keys(btnDesignOptions)).not.toHaveLength(0)
|
||||
})
|
||||
})
|
||||
|
||||
describe('[(variable)useBtnProps]', () => {
|
||||
test('is defined correctly', () => {
|
||||
expect(useBtnProps).toBeTypeOf('object')
|
||||
expect(Object.keys(useBtnProps)).not.toHaveLength(0)
|
||||
})
|
||||
})
|
||||
|
||||
describe('[(variable)nonRoundBtnProps]', () => {
|
||||
test('is defined correctly', () => {
|
||||
expect(nonRoundBtnProps).toBeTypeOf('object')
|
||||
expect(Object.keys(nonRoundBtnProps)).not.toHaveLength(0)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('[Functions]', () => {
|
||||
describe('[(function)default]', () => {
|
||||
test('has correct return value', () => {
|
||||
const wrapper = mount(
|
||||
defineComponent({
|
||||
template: '<div />',
|
||||
setup () {
|
||||
const result = useBtn({})
|
||||
return { result }
|
||||
}
|
||||
})
|
||||
)
|
||||
|
||||
expect(
|
||||
wrapper.vm.result
|
||||
).toStrictEqual({
|
||||
classes: expect.$ref(expect.any(String)),
|
||||
style: expect.$ref(expect.any(Object)),
|
||||
innerClasses: expect.$ref(expect.any(String)),
|
||||
attributes: expect.$ref(expect.any(Object)),
|
||||
hasLink: expect.$ref(expect.any(Boolean)),
|
||||
linkTag: expect.$ref(expect.any(String)),
|
||||
navigateOnClick: expect.any(Function),
|
||||
isActionable: expect.$ref(expect.any(Boolean))
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('[(function)getBtnDesign]', () => {
|
||||
test('returns correctly with single value', () => {
|
||||
for (const prop of btnDesignOptions) {
|
||||
expect(
|
||||
getBtnDesign({ [ prop ]: true })
|
||||
).toBe(prop)
|
||||
|
||||
expect(
|
||||
getBtnDesign({ [ prop ]: true }, 'default')
|
||||
).toBe(prop)
|
||||
|
||||
expect(
|
||||
getBtnDesign({ [ prop ]: false })
|
||||
).toBeUndefined()
|
||||
|
||||
expect(
|
||||
getBtnDesign({ [ prop ]: false }, 'default')
|
||||
).toBe('default')
|
||||
}
|
||||
})
|
||||
|
||||
test('returns correctly with multiple values', () => {
|
||||
for (const prop of btnDesignOptions) {
|
||||
const propMap = btnDesignOptions
|
||||
.reduce((acc, val) => {
|
||||
if (val !== prop) {
|
||||
acc[ val ] = true
|
||||
}
|
||||
|
||||
return acc
|
||||
}, {})
|
||||
|
||||
expect(
|
||||
getBtnDesign(propMap)
|
||||
).not.toBe(prop)
|
||||
|
||||
expect(
|
||||
getBtnDesign(propMap, 'default')
|
||||
).not.toBe('default')
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
describe('[(function)getBtnDesignAttr]', () => {
|
||||
test('has correct return value', () => {
|
||||
expect(
|
||||
getBtnDesignAttr({})
|
||||
).toStrictEqual({})
|
||||
|
||||
expect(
|
||||
getBtnDesignAttr({ something: true })
|
||||
).toStrictEqual({})
|
||||
|
||||
for (const prop of btnDesignOptions) {
|
||||
expect(
|
||||
getBtnDesignAttr({ [ prop ]: true })
|
||||
).toStrictEqual({ [ prop ]: true })
|
||||
|
||||
expect(
|
||||
getBtnDesignAttr({ [ prop ]: false })
|
||||
).toStrictEqual({})
|
||||
}
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
Loading…
Add table
Add a link
Reference in a new issue