Website Structure

This commit is contained in:
supalerk-ar66 2026-01-13 10:46:40 +07:00
parent 62812f2090
commit 71f0676a62
22365 changed files with 4265753 additions and 791 deletions

View file

@ -0,0 +1,8 @@
import DialogPlugin from './component/DialogPluginComponent.js'
import globalDialog from '../../utils/private.dialog/create-dialog.js'
export default {
install ({ $q, parentApp }) {
$q.dialog = this.create = globalDialog(DialogPlugin, true, parentApp)
}
}

View file

@ -0,0 +1,287 @@
{
"mixins": [ "utils/private.dialog/create-dialog" ],
"meta": {
"docsUrl": "https://v2.quasar.dev/quasar-plugins/dialog"
},
"injection": "$q.dialog",
"methods": {
"create": {
"tsInjectionPoint": true,
"params": {
"opts": {
"desc": "Dialog options",
"tsType": "QDialogOptions",
"autoDefineTsType": true,
"definition": {
"title": {
"type": "String",
"desc": "A text for the heading title of the dialog",
"examples": [ "'Continue?'" ]
},
"message": {
"type": "String",
"desc": "A text with more information about what needs to be input, selected or confirmed.",
"examples": [ "'Are you certain you want to continue?'" ]
},
"html": {
"type": "Boolean",
"desc": "Render title and message as HTML; This can lead to XSS attacks, so make sure that you sanitize the message first"
},
"position": {
"type": "String",
"desc": "Position of the Dialog on screen. Standard is centered.",
"values": [ "'top'", "'right'", "'bottom'", "'left'", "'standard'" ],
"default": "'standard'"
},
"prompt": {
"type": "Object",
"tsType": "QDialogInputPrompt",
"desc": "An object definition of the input field for the prompting question.",
"examples": [ "{ model: 'initial-value', type: 'number' }" ],
"definition": {
"model": {
"type": "String",
"required": true,
"desc": "The initial value of the input"
},
"type": {
"type": "String",
"desc": "Optional property to determine the input field type",
"default": "'text'",
"examples": [ "'text'", "'number'", "'textarea'" ]
},
"isValid": {
"type": "Function",
"desc": "Is typed content valid?",
"params": {
"val": {
"type": "String",
"required": true,
"desc": "The value of the input"
}
},
"returns": {
"type": "Boolean",
"desc": "The text passed validation or not"
}
},
"...QInputProps": {
"type": "Any",
"desc": "Any QInput props, like color, label, stackLabel, filled, outlined, rounded, prefix etc",
"examples": [
"label: 'My Label'",
"standout: true",
"counter: true",
"maxlength: 12"
]
},
"...nativeAttributes": {
"type": "Object",
"desc": "Any native attributes to pass to the prompt control",
"examples": [ "# autocomplete: 'off'" ]
}
}
},
"options": {
"type": "Object",
"tsType": "QDialogSelectionPrompt",
"desc": "An object definition for creating the selection form content",
"examples": [ "{ model: null, type: 'radio', items: [ /* ...listOfItems */ ] }" ],
"definition": {
"model": {
"type": [ "String", "Array" ],
"required": true,
"desc": "The value of the selection (String if it's of type radio or Array otherwise)",
"examples": [ "[]" ]
},
"type": {
"type": "String",
"desc": "The type of selection",
"default": "'radio'",
"values": [ "'radio'", "'checkbox'", "'toggle'" ]
},
"items": {
"type": "Array",
"desc": "The list of options to interact with; Equivalent to options prop of the QOptionGroup component",
"examples": [
"[ { label: 'Option 1', value: 'op1' }, { label: 'Option 2', value: 'op2' }, { label: 'Option 3', value: 'op3' } ]"
]
},
"isValid": {
"type": "Function",
"desc": "Is the model valid?",
"params": {
"model": {
"type": [ "String", "Array" ],
"required": true,
"desc": "The current model (String if it's of type radio or Array otherwise)",
"examples": [
"'opt2'",
"[ 'opt1' ]",
"[]",
"[ 'opt1', 'opt3' ]"
]
}
},
"returns": {
"type": "Boolean",
"desc": "The selection passed validation or not"
}
},
"...QOptionGroupProps": {
"type": "Any",
"desc": "Any QOptionGroup props",
"examples": [
"color: 'deep-purple-4'",
"inline: true",
"dense: true",
"leftLabel: true"
]
},
"...nativeAttributes": {
"type": "Object",
"desc": "Any native attributes to pass to the inner QOptionGroup"
}
}
},
"progress": {
"type": [ "Boolean", "Object" ],
"desc": "Display a Quasar spinner (if value is true, then the defaults are used); Useful for conveying the idea that something is happening behind the covers; Tip: use along with persistent, ok: false and update() method",
"definition": {
"spinner": {
"type": "Component",
"desc": "One of the QSpinners"
},
"color": {
"extends": "color"
}
}
},
"ok": {
"type": [ "String", "Object", "Boolean" ],
"desc": "Props for an 'OK' button",
"definition": {
"...props": {
"type": "Any",
"desc": "See QBtn for available props"
}
}
},
"cancel": {
"type": [ "String", "Object", "Boolean" ],
"desc": "Props for a 'CANCEL' button",
"definition": {
"...props": {
"type": "Any",
"desc": "See QBtn for available props"
}
}
},
"focus": {
"type": "String",
"desc": "What button to focus, unless you also have 'prompt' or 'options'",
"values": [ "'ok'", "'cancel'", "'none'" ],
"default": "'ok'"
},
"stackButtons": {
"type": "Boolean",
"desc": "Makes buttons be stacked instead of vertically aligned"
},
"color": {
"extends": "color"
},
"dark": {
"extends": "dark",
"desc": "Apply dark mode"
},
"persistent": {
"type": "Boolean",
"desc": "User cannot dismiss Dialog if clicking outside of it or hitting ESC key; Also, an app route change won't dismiss it"
},
"noEscDismiss": {
"type": "Boolean",
"desc": "User cannot dismiss Dialog by hitting ESC key; No need to set it if 'persistent' prop is also set"
},
"noBackdropDismiss": {
"type": "Boolean",
"desc": "User cannot dismiss Dialog by clicking outside of it; No need to set it if 'persistent' prop is also set"
},
"noRouteDismiss": {
"type": "Boolean",
"desc": "Changing route app won't dismiss Dialog; No need to set it if 'persistent' prop is also set"
},
"seamless": {
"type": "Boolean",
"desc": "Put Dialog into seamless mode; Does not use a backdrop so user is able to interact with the rest of the page too"
},
"maximized": {
"type": "Boolean",
"desc": "Put Dialog into maximized mode"
},
"fullWidth": {
"type": "Boolean",
"desc": "Dialog will try to render with same width as the window"
},
"fullHeight": {
"type": "Boolean",
"desc": "Dialog will try to render with same height as the window"
},
"transitionShow": {
"extends": "transition",
"default": "'scale'"
},
"transitionHide": {
"extends": "transition",
"default": "'scale'"
},
"component": {
"type": [ "Component", "String" ],
"desc": "Use custom dialog component; use along with 'componentProps' prop where possible",
"examples": [ "CustomComponent", "'custom-component'" ]
},
"componentProps": {
"type": "Object",
"desc": "User defined props which will be forwarded to underlying custom component if 'component' prop is used; May also include any built-in QDialog option such as 'persistent' or 'seamless'"
}
}
}
}
}
}
}

View file

@ -0,0 +1,325 @@
import { h, ref, computed, watch, toRaw, getCurrentInstance } from 'vue'
import QDialog from '../../../components/dialog/QDialog.js'
import QBtn from '../../../components/btn/QBtn.js'
import QCard from '../../../components/card/QCard.js'
import QCardSection from '../../../components/card/QCardSection.js'
import QCardActions from '../../../components/card/QCardActions.js'
import QSeparator from '../../../components/separator/QSeparator.js'
import QInput from '../../../components/input/QInput.js'
import QOptionGroup from '../../../components/option-group/QOptionGroup.js'
import QSpinner from '../../../components/spinner/QSpinner.js'
import { createComponent } from '../../../utils/private.create/create.js'
import useDark, { useDarkProps } from '../../../composables/private.use-dark/use-dark.js'
import { isKeyCode } from '../../../utils/private.keyboard/key-composition.js'
import { isObject } from '../../../utils/is/is.js'
export default createComponent({
name: 'DialogPluginComponent',
props: {
...useDarkProps,
title: String,
message: String,
prompt: Object,
options: Object,
progress: [ Boolean, Object ],
html: Boolean,
ok: {
type: [ String, Object, Boolean ],
default: true
},
cancel: [ String, Object, Boolean ],
focus: {
type: String,
default: 'ok',
validator: v => [ 'ok', 'cancel', 'none' ].includes(v)
},
stackButtons: Boolean,
color: String,
cardClass: [ String, Array, Object ],
cardStyle: [ String, Array, Object ]
},
emits: [ 'ok', 'hide' ],
setup (props, { emit }) {
const { proxy } = getCurrentInstance()
const { $q } = proxy
const isDark = useDark(props, $q)
const dialogRef = ref(null)
const model = ref(
props.prompt !== void 0
? props.prompt.model
: (props.options !== void 0 ? props.options.model : void 0)
)
const classes = computed(() =>
'q-dialog-plugin'
+ (isDark.value === true ? ' q-dialog-plugin--dark q-dark' : '')
+ (props.progress !== false ? ' q-dialog-plugin--progress' : '')
)
const vmColor = computed(() =>
props.color || (isDark.value === true ? 'amber' : 'primary')
)
const spinner = computed(() => (
props.progress === false
? null
: (
isObject(props.progress) === true
? {
component: props.progress.spinner || QSpinner,
props: { color: props.progress.color || vmColor.value }
}
: {
component: QSpinner,
props: { color: vmColor.value }
}
)
))
const hasForm = computed(() =>
props.prompt !== void 0 || props.options !== void 0
)
const formProps = computed(() => {
if (hasForm.value !== true) {
return {}
}
const { model, isValid, items, ...formProps } = props.prompt !== void 0
? props.prompt
: props.options
return formProps
})
const okLabel = computed(() => (
isObject(props.ok) === true
? $q.lang.label.ok
: (
props.ok === true
? $q.lang.label.ok
: props.ok
)
))
const cancelLabel = computed(() => (
isObject(props.cancel) === true
? $q.lang.label.cancel
: (
props.cancel === true
? $q.lang.label.cancel
: props.cancel
)
))
const okDisabled = computed(() => {
if (props.prompt !== void 0) {
return props.prompt.isValid !== void 0
&& props.prompt.isValid(model.value) !== true
}
if (props.options !== void 0) {
return props.options.isValid !== void 0
&& props.options.isValid(model.value) !== true
}
return false
})
const okProps = computed(() => ({
color: vmColor.value,
label: okLabel.value,
ripple: false,
disable: okDisabled.value,
...(isObject(props.ok) === true ? props.ok : { flat: true }),
'data-autofocus': (props.focus === 'ok' && hasForm.value !== true) || void 0,
onClick: onOk
}))
const cancelProps = computed(() => ({
color: vmColor.value,
label: cancelLabel.value,
ripple: false,
...(isObject(props.cancel) === true ? props.cancel : { flat: true }),
'data-autofocus': (props.focus === 'cancel' && hasForm.value !== true) || void 0,
onClick: onCancel
}))
watch(() => props.prompt && props.prompt.model, onUpdateModel)
watch(() => props.options && props.options.model, onUpdateModel)
function show () {
dialogRef.value.show()
}
function hide () {
dialogRef.value.hide()
}
function onOk () {
emit('ok', toRaw(model.value))
hide()
}
function onCancel () {
hide()
}
function onDialogHide () {
emit('hide')
}
function onUpdateModel (val) {
model.value = val
}
function onInputKeyup (evt) {
// if ENTER key
if (
okDisabled.value !== true
&& props.prompt.type !== 'textarea'
&& isKeyCode(evt, 13) === true
) {
onOk()
}
}
function getSection (classes, text) {
return props.html === true
? h(QCardSection, {
class: classes,
innerHTML: text
})
: h(QCardSection, { class: classes }, () => text)
}
function getPrompt () {
return [
h(QInput, {
color: vmColor.value,
dense: true,
autofocus: true,
dark: isDark.value,
...formProps.value,
modelValue: model.value,
'onUpdate:modelValue': onUpdateModel,
onKeyup: onInputKeyup
})
]
}
function getOptions () {
return [
h(QOptionGroup, {
color: vmColor.value,
options: props.options.items,
dark: isDark.value,
...formProps.value,
modelValue: model.value,
'onUpdate:modelValue': onUpdateModel
})
]
}
function getButtons () {
const child = []
props.cancel && child.push(
h(QBtn, cancelProps.value)
)
props.ok && child.push(
h(QBtn, okProps.value)
)
return h(QCardActions, {
class: props.stackButtons === true ? 'items-end' : '',
vertical: props.stackButtons,
align: 'right'
}, () => child)
}
function getCardContent () {
const child = []
props.title && child.push(
getSection('q-dialog__title', props.title)
)
props.progress !== false && child.push(
h(
QCardSection,
{ class: 'q-dialog__progress' },
() => h(spinner.value.component, spinner.value.props)
)
)
props.message && child.push(
getSection('q-dialog__message', props.message)
)
if (props.prompt !== void 0) {
child.push(
h(
QCardSection,
{ class: 'scroll q-dialog-plugin__form' },
getPrompt
)
)
}
else if (props.options !== void 0) {
child.push(
h(QSeparator, { dark: isDark.value }),
h(
QCardSection,
{ class: 'scroll q-dialog-plugin__form' },
getOptions
),
h(QSeparator, { dark: isDark.value })
)
}
if (props.ok || props.cancel) {
child.push(getButtons())
}
return child
}
function getContent () {
return [
h(QCard, {
class: [
classes.value,
props.cardClass
],
style: props.cardStyle,
dark: isDark.value
}, getCardContent)
]
}
// expose public methods
Object.assign(proxy, { show, hide })
return () => h(QDialog, {
ref: dialogRef,
onHide: onDialogHide
}, getContent)
}
})

View file

@ -0,0 +1,11 @@
.q-dialog-plugin
width: 400px
&__form
max-height: 50vh
.q-card__section + .q-card__section
padding-top: 0
&--progress
text-align: center