-
Notifications
You must be signed in to change notification settings - Fork 7
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #288 from qor5/feat-ui-reborn
style: style adjusting of gap
- Loading branch information
Showing
13 changed files
with
980 additions
and
240 deletions.
There are no files selected for viewing
Large diffs are not rendered by default.
Oops, something went wrong.
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -168,6 +168,7 @@ | |
border-collapse: collapse; | ||
margin: 20px 0; | ||
overflow-x: auto; | ||
word-break: unset; | ||
} | ||
|
||
.vp-doc tr { | ||
|
345 changes: 338 additions & 7 deletions
345
ui/vuetifyx/vuetifyxjs/docs/Components/VXDialog/index.md
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,32 +1,251 @@ | ||
<template> | ||
<div class="vx-dialog-wrap"> | ||
<v-dialog :model-value="dialogVisible" v-bind="filteredAttrs"></v-dialog> | ||
<v-dialog | ||
scrollable | ||
width="auto" | ||
:model-value="dialogVisible" | ||
v-bind="filteredAttrs" | ||
:persistent="persistent" | ||
@update:model-value="onUpdateModelValue" | ||
> | ||
<template v-slot:activator="{ isActive, props: activatorProps }"> | ||
<slot name="activator" :props="{ isActive, activatorProps }" /> | ||
</template> | ||
|
||
<template v-slot:default="{ isActive }"> | ||
<v-card :title="title"> | ||
<template v-slot:prepend v-if="prependIcon.icon"> | ||
<v-icon :color="prependIcon.color" size="small" :icon="prependIcon.icon" /> | ||
</template> | ||
|
||
<template v-slot:append v-if="!hideClose"> | ||
<v-icon color="#757575" size="small" icon="mdi-close" @click="onClose(isActive)" /> | ||
</template> | ||
|
||
<v-card-text :style="[contentWidth, contentMaxWidth, contentHeightStyle]"> | ||
<slot | ||
:isActive="isActive" | ||
><span class="dialog-content-text">{{ text }}</span></slot | ||
> | ||
</v-card-text> | ||
<v-card-actions :class="props.size" v-if="!hideCancel || !hideOk"> | ||
<slot :isActive="isActive" name="action-btn"> | ||
<v-btn | ||
v-if="!hideCancel" | ||
color="grey-darken-3" | ||
:size="props.size === 'default' ? 'small' : 'default'" | ||
variant="tonal" | ||
@click="onCancel(isActive)" | ||
>{{ cancelText }}</v-btn | ||
> | ||
<v-btn | ||
v-if="!hideOk" | ||
color="primary" | ||
:size="props.size === 'default' ? 'small' : 'default'" | ||
:loading="isOkBtnLoading" | ||
variant="flat" | ||
@click="onOk(isActive)" | ||
>{{ okText }}</v-btn | ||
> | ||
</slot> | ||
</v-card-actions> | ||
</v-card> | ||
</template> | ||
</v-dialog> | ||
</div> | ||
</template> | ||
|
||
<script setup lang="ts"> | ||
import { defineEmits, ref, watch } from 'vue' | ||
import { | ||
defineEmits, | ||
ref, | ||
watch, | ||
defineProps, | ||
computed, | ||
PropType, | ||
Ref, | ||
getCurrentInstance | ||
} from 'vue' | ||
import { useFilteredAttrs } from '@/lib/composables/useFilteredAttrs' | ||
import { useHasEventListener } from '@/lib/composables/useEventListener' | ||
const { filteredAttrs } = useFilteredAttrs() | ||
const emit = defineEmits(['update:modelValue']) | ||
const { hasEventListener } = useHasEventListener() | ||
const emit = defineEmits(['update:modelValue', 'click:ok', 'click:cancel', 'click:close']) | ||
const props = defineProps({ | ||
modelValue: Boolean | ||
modelValue: Boolean, | ||
type: { | ||
type: String as PropType<'default' | 'warn' | 'error' | 'info'>, | ||
default: 'default' | ||
}, | ||
size: { | ||
type: String, | ||
default: 'default' | ||
}, | ||
text: String, | ||
hideOk: { | ||
type: Boolean, | ||
default: false | ||
}, | ||
hideCancel: { | ||
type: Boolean, | ||
default: false | ||
}, | ||
hideClose: { | ||
type: Boolean, | ||
default: false | ||
}, | ||
okText: { | ||
type: String, | ||
default: 'OK' | ||
}, | ||
cancelText: { | ||
type: String, | ||
default: 'Cancel' | ||
}, | ||
persistent: Boolean, | ||
contentHeight: { | ||
type: [Number, String], | ||
default: 'auto' | ||
}, | ||
width: { | ||
type: [Number, String], | ||
default: '' | ||
}, | ||
maxWidth: { | ||
type: [Number, String], | ||
default: 665 | ||
}, | ||
title: String | ||
}) | ||
const dialogVisible = ref(props.modelValue) | ||
watch( | ||
() => props.modelValue, | ||
(newValue) => { | ||
dialogVisible.value = newValue | ||
} | ||
) | ||
const isOkBtnLoading = ref(false) | ||
const dialogVisible = ref(props.modelValue) | ||
const contentMaxWidth = computed(() => { | ||
return `max-width:${Math.max(+props.width, +props.maxWidth)}px` | ||
}) | ||
const contentWidth = computed(() => { | ||
let contentWidthStyle | ||
if (props.size === 'default' && props.width === '') { | ||
contentWidthStyle = 'width:461px' | ||
} else { | ||
contentWidthStyle = `width:${props.width}px` | ||
} | ||
return contentWidthStyle | ||
}) | ||
const contentHeightStyle = computed(() => `height:${props.contentHeight}px`) | ||
const prependIcon = computed(() => { | ||
const vCardTitleIconMap = { | ||
default: { | ||
icon: '', | ||
color: '' | ||
}, | ||
info: { | ||
color: 'primary', | ||
icon: 'mdi-alert-circle' | ||
}, | ||
warn: { | ||
color: 'warning', | ||
icon: 'mdi-alert-circle' | ||
}, | ||
success: { | ||
color: 'success', | ||
icon: 'mdi-check-circle' | ||
}, | ||
error: { | ||
color: 'error', | ||
icon: 'mdi-alert-circle' | ||
} | ||
} | ||
return vCardTitleIconMap[props.type] | ||
}) | ||
function onUpdateModelValue(value: any) { | ||
emit('update:modelValue', value) | ||
dialogVisible.value = value | ||
} | ||
const instance = getCurrentInstance() | ||
// const hasEventListener = (event: Parameters<typeof emit>[0]) => { | ||
// // Convert event name to the format used in vnode.props (e.g., 'click:ok' becomes 'onClick:ok') | ||
// const eventName = 'on' + event.charAt(0).toUpperCase() + event.slice(1); | ||
// debugger | ||
// return !!instance?.vnode.props?.[eventName] | ||
// } | ||
function onOk(isActive: Ref<boolean>) { | ||
if (hasEventListener('click:ok')) { | ||
emit('click:ok', { isActive, isLoading: isOkBtnLoading }) | ||
} else { | ||
isActive.value = false | ||
} | ||
} | ||
function onCancel(isActive: Ref<boolean>) { | ||
if (hasEventListener('click:cancel')) { | ||
emit('click:cancel', { isActive }) | ||
} else { | ||
isActive.value = false | ||
} | ||
} | ||
function onClose(isActive: Ref<boolean>) { | ||
if (hasEventListener('click-close')) { | ||
emit('click:close', isActive) | ||
} else { | ||
isActive.value = false | ||
} | ||
} | ||
</script> | ||
|
||
<style lang="sass" scoped></style> | ||
<style lang="scss" scoped> | ||
.dialog-content-text { | ||
font-size: 14px; | ||
font-weight: 400; | ||
line-height: 20px; | ||
color: rgb(var(--v-theme-grey-darken-2)); | ||
} | ||
.v-card-text { | ||
padding-bottom: 12px !important; | ||
} | ||
.v-card-actions { | ||
padding: 12px 24px 24px; | ||
&.default { | ||
.v-btn.v-btn--size-small { | ||
min-width: initial; | ||
padding: 0 12px; | ||
font-size: 12px; | ||
font-weight: 400; | ||
&:deep(.v-btn__content) { | ||
letter-spacing: 0.04px; | ||
} | ||
} | ||
} | ||
.v-btn.v-btn--size-default { | ||
min-width: initial; | ||
padding: 0 16px; | ||
font-weight: 500; | ||
&:deep(.v-btn__content) { | ||
letter-spacing: 0.25px; | ||
} | ||
} | ||
.v-btn ~ .v-btn:not(.v-btn-toggle .v-btn) { | ||
margin-inline-start: 10px; | ||
} | ||
} | ||
</style> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
39 changes: 39 additions & 0 deletions
39
ui/vuetifyx/vuetifyxjs/src/lib/composables/useEventListener.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,39 @@ | ||
import { getCurrentInstance } from 'vue' | ||
|
||
// return a utils function: hasEventListener | ||
// this function is used to check if some event callback is applied to some components | ||
/** | ||
* hasEventListener('click:ok'); // Should log 'onClick:ok' | ||
* hasEventListener('click-ok'); // Should log 'onClickOk' | ||
* hasEventListener('my-custom:event'); // Should log 'onMyCustom:event' | ||
* hasEventListener('multiple-parts-to-test'); // Should log 'onMultiplePartsToTest' | ||
*/ | ||
export function useHasEventListener() { | ||
const instance = getCurrentInstance() | ||
|
||
const hasEventListener = (event: string): boolean => { | ||
// Match the separator '-' or ':' | ||
const separator = event.match(/[-:]/)?.[0] | ||
|
||
// Split the event name by '-' or ':' | ||
const parts = event.split(/[-:]/) | ||
|
||
let eventName = '' | ||
|
||
if (separator === '-') { | ||
// Capitalize each part and join without a separator for '-' | ||
const capitalizedParts = parts.map((part) => part.charAt(0).toUpperCase() + part.slice(1)) | ||
eventName = 'on' + capitalizedParts.join('') | ||
} else if (separator === ':') { | ||
// Capitalize the first part and keep the second part as is for ':' | ||
eventName = 'on' + parts[0].charAt(0).toUpperCase() + parts[0].slice(1) + ':' + parts[1] | ||
} | ||
|
||
// console.log(eventName) // For testing purpose | ||
|
||
// Check if the event listener exists in vnode.props (assumed instance) | ||
return !!instance?.vnode.props?.[eventName] | ||
} | ||
|
||
return { hasEventListener } | ||
} |
Oops, something went wrong.