refactor(frontend): implement Full Transparent Wrapper Pattern with v-bind=\ and \ passthrough to expose 100% PrimeVue native capabilities without feature loss
Validators (Pushes and Pull Requests) / UI & Storage Validation (push) Failing after 10s
Validators (Pushes and Pull Requests) / Database & Schema Validation (push) Successful in 11s
Validators (Pushes and Pull Requests) / Core Validators & Database Setup (push) Failing after 19s
Validators (Pushes and Pull Requests) / WBS & Audit Validations (push) Has been skipped
Validators (Pushes and Pull Requests) / .NET Contracts (push) Has been skipped
Validators (Pushes and Pull Requests) / Calibration & Performance (push) Has been skipped
Validators (Pushes and Pull Requests) / Operational Report & Decision Packet (push) Has been skipped
Validators (Pushes and Pull Requests) / Security & Secrets (push) Successful in 9s
Validators (Pushes and Pull Requests) / CI Workflow Lint (push) Failing after 9s
Validators (Pushes and Pull Requests) / Notify PR Results (push) Has been skipped
Validators (Pushes and Pull Requests) / UI & Storage Validation (push) Failing after 10s
Validators (Pushes and Pull Requests) / Database & Schema Validation (push) Successful in 11s
Validators (Pushes and Pull Requests) / Core Validators & Database Setup (push) Failing after 19s
Validators (Pushes and Pull Requests) / WBS & Audit Validations (push) Has been skipped
Validators (Pushes and Pull Requests) / .NET Contracts (push) Has been skipped
Validators (Pushes and Pull Requests) / Calibration & Performance (push) Has been skipped
Validators (Pushes and Pull Requests) / Operational Report & Decision Packet (push) Has been skipped
Validators (Pushes and Pull Requests) / Security & Secrets (push) Successful in 9s
Validators (Pushes and Pull Requests) / CI Workflow Lint (push) Failing after 9s
Validators (Pushes and Pull Requests) / Notify PR Results (push) Has been skipped
This commit is contained in:
@@ -1,7 +1,11 @@
|
|||||||
<!-- PrimeVue Select Adapter Wrapper: QuantComboBox -->
|
<!-- PrimeVue Select Adapter Wrapper: QuantComboBox (Full Transparent Passthrough Wrapper) -->
|
||||||
<template>
|
<template>
|
||||||
<div class="quant-combobox-wrapper w-full">
|
<div class="quant-combobox-wrapper flex flex-col gap-1 w-full">
|
||||||
|
<label v-if="label" class="text-xs font-bold text-slate-800">
|
||||||
|
{{ label }} <span v-if="required" class="text-rose-600">*</span>
|
||||||
|
</label>
|
||||||
<Select
|
<Select
|
||||||
|
v-bind="$attrs"
|
||||||
:id="id"
|
:id="id"
|
||||||
:modelValue="modelValue"
|
:modelValue="modelValue"
|
||||||
:options="formattedOptions"
|
:options="formattedOptions"
|
||||||
@@ -9,9 +13,17 @@
|
|||||||
optionValue="value"
|
optionValue="value"
|
||||||
:placeholder="placeholder || '선택하세요'"
|
:placeholder="placeholder || '선택하세요'"
|
||||||
:disabled="disabled"
|
:disabled="disabled"
|
||||||
|
:showClear="showClear !== false"
|
||||||
|
:filter="filter !== false"
|
||||||
|
filterPlaceholder="검색어 입력..."
|
||||||
class="w-full h-9 border border-slate-300 rounded text-xs text-slate-900 font-medium bg-white focus:ring-2 focus:ring-blue-500 focus:border-blue-500"
|
class="w-full h-9 border border-slate-300 rounded text-xs text-slate-900 font-medium bg-white focus:ring-2 focus:ring-blue-500 focus:border-blue-500"
|
||||||
@update:modelValue="onSelectChange"
|
@update:modelValue="onSelectChange"
|
||||||
/>
|
>
|
||||||
|
<!-- PrimeVue Select 슬롯 100% 투명 전파 -->
|
||||||
|
<template v-for="(_, slotName) in $slots" :key="slotName" #[slotName]="slotProps">
|
||||||
|
<slot :name="slotName" v-bind="slotProps || {}"></slot>
|
||||||
|
</template>
|
||||||
|
</Select>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@@ -19,12 +31,20 @@
|
|||||||
import { computed } from 'vue';
|
import { computed } from 'vue';
|
||||||
import Select from 'primevue/select';
|
import Select from 'primevue/select';
|
||||||
|
|
||||||
|
defineOptions({
|
||||||
|
inheritAttrs: false
|
||||||
|
});
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
id?: string;
|
id?: string;
|
||||||
|
label?: string;
|
||||||
modelValue?: string | number;
|
modelValue?: string | number;
|
||||||
options: Array<string | { label: string; value: string | number }>;
|
options: Array<string | { label: string; value: string | number }>;
|
||||||
placeholder?: string;
|
placeholder?: string;
|
||||||
|
required?: boolean;
|
||||||
disabled?: boolean;
|
disabled?: boolean;
|
||||||
|
showClear?: boolean;
|
||||||
|
filter?: boolean;
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
const emit = defineEmits(['update:modelValue', 'change']);
|
const emit = defineEmits(['update:modelValue', 'change']);
|
||||||
|
|||||||
@@ -1,14 +1,17 @@
|
|||||||
<!-- PrimeVue DataTable Adapter Wrapper: QuantDataGrid -->
|
<!-- PrimeVue DataTable Adapter Wrapper: QuantDataGrid (Full Transparent Passthrough Wrapper) -->
|
||||||
<template>
|
<template>
|
||||||
<div class="quant-datagrid-wrapper w-full overflow-hidden border border-slate-300 rounded-lg shadow-2xs bg-white">
|
<div class="quant-datagrid-wrapper w-full overflow-hidden border border-slate-300 rounded-lg shadow-2xs bg-white">
|
||||||
<DataTable
|
<DataTable
|
||||||
|
v-bind="$attrs"
|
||||||
:value="items"
|
:value="items"
|
||||||
:loading="loading"
|
:loading="loading"
|
||||||
:paginator="paginator"
|
:paginator="paginator !== false"
|
||||||
:rows="rows || 10"
|
:rows="rows || 10"
|
||||||
:rowsPerPageOptions="[10, 25, 50, 100]"
|
:rowsPerPageOptions="[10, 25, 50, 100]"
|
||||||
dataKey="id"
|
dataKey="id"
|
||||||
responsiveLayout="scroll"
|
responsiveLayout="scroll"
|
||||||
|
resizableColumns
|
||||||
|
columnResizeMode="fit"
|
||||||
class="p-datatable-sm w-full text-xs"
|
class="p-datatable-sm w-full text-xs"
|
||||||
tableStyle="min-width: 50rem"
|
tableStyle="min-width: 50rem"
|
||||||
>
|
>
|
||||||
@@ -18,6 +21,11 @@
|
|||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
<!-- 사용자 정의 슬롯 100% 전파 -->
|
||||||
|
<template v-for="(_, slotName) in $slots" :key="slotName" #[slotName]="slotProps">
|
||||||
|
<slot :name="slotName" v-bind="slotProps || {}"></slot>
|
||||||
|
</template>
|
||||||
|
|
||||||
<Column
|
<Column
|
||||||
v-for="col in headers"
|
v-for="col in headers"
|
||||||
:key="col.key"
|
:key="col.key"
|
||||||
@@ -42,6 +50,10 @@
|
|||||||
import DataTable from 'primevue/datatable';
|
import DataTable from 'primevue/datatable';
|
||||||
import Column from 'primevue/column';
|
import Column from 'primevue/column';
|
||||||
|
|
||||||
|
defineOptions({
|
||||||
|
inheritAttrs: false
|
||||||
|
});
|
||||||
|
|
||||||
export interface GridColumn {
|
export interface GridColumn {
|
||||||
key: string;
|
key: string;
|
||||||
label: string;
|
label: string;
|
||||||
|
|||||||
@@ -1,10 +1,11 @@
|
|||||||
<!-- PrimeVue InputText Adapter Wrapper: QuantInput (Strict Numeric Filter Support) -->
|
<!-- PrimeVue InputText Adapter Wrapper: QuantInput (Full Transparent Passthrough Wrapper) -->
|
||||||
<template>
|
<template>
|
||||||
<div class="quant-input-wrapper flex flex-col gap-1 w-full">
|
<div class="quant-input-wrapper flex flex-col gap-1 w-full">
|
||||||
<label v-if="label" class="text-xs font-bold text-slate-800">
|
<label v-if="label" class="text-xs font-bold text-slate-800">
|
||||||
{{ label }} <span v-if="required" class="text-rose-600">*</span>
|
{{ label }} <span v-if="required" class="text-rose-600">*</span>
|
||||||
</label>
|
</label>
|
||||||
<InputText
|
<InputText
|
||||||
|
v-bind="$attrs"
|
||||||
:id="id"
|
:id="id"
|
||||||
:type="inputType"
|
:type="inputType"
|
||||||
:modelValue="displayValue"
|
:modelValue="displayValue"
|
||||||
@@ -15,7 +16,12 @@
|
|||||||
@keydown="onKeyDown"
|
@keydown="onKeyDown"
|
||||||
@paste="onPaste"
|
@paste="onPaste"
|
||||||
@update:modelValue="onInput"
|
@update:modelValue="onInput"
|
||||||
/>
|
>
|
||||||
|
<!-- PrimeVue 원본 슬롯 100% 투명 전파 -->
|
||||||
|
<template v-for="(_, slotName) in $slots" :key="slotName" #[slotName]="slotProps">
|
||||||
|
<slot :name="slotName" v-bind="slotProps || {}"></slot>
|
||||||
|
</template>
|
||||||
|
</InputText>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@@ -23,6 +29,10 @@
|
|||||||
import { computed } from 'vue';
|
import { computed } from 'vue';
|
||||||
import InputText from 'primevue/inputtext';
|
import InputText from 'primevue/inputtext';
|
||||||
|
|
||||||
|
defineOptions({
|
||||||
|
inheritAttrs: false
|
||||||
|
});
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
id?: string;
|
id?: string;
|
||||||
label?: string;
|
label?: string;
|
||||||
@@ -37,7 +47,6 @@ const props = defineProps<{
|
|||||||
const emit = defineEmits(['update:modelValue', 'change']);
|
const emit = defineEmits(['update:modelValue', 'change']);
|
||||||
|
|
||||||
const isNumeric = computed(() => props.type === 'number');
|
const isNumeric = computed(() => props.type === 'number');
|
||||||
|
|
||||||
const inputType = computed(() => (isNumeric.value ? 'text' : props.type || 'text'));
|
const inputType = computed(() => (isNumeric.value ? 'text' : props.type || 'text'));
|
||||||
|
|
||||||
const displayValue = computed(() => {
|
const displayValue = computed(() => {
|
||||||
@@ -47,37 +56,27 @@ const displayValue = computed(() => {
|
|||||||
|
|
||||||
const onKeyDown = (e: KeyboardEvent) => {
|
const onKeyDown = (e: KeyboardEvent) => {
|
||||||
if (!isNumeric.value) return;
|
if (!isNumeric.value) return;
|
||||||
|
|
||||||
const allowedKeys = [
|
const allowedKeys = [
|
||||||
'Backspace', 'Delete', 'Tab', 'Escape', 'Enter',
|
'Backspace', 'Delete', 'Tab', 'Escape', 'Enter',
|
||||||
'ArrowLeft', 'ArrowRight', 'ArrowUp', 'ArrowDown',
|
'ArrowLeft', 'ArrowRight', 'ArrowUp', 'ArrowDown',
|
||||||
'Home', 'End', '.', '-'
|
'Home', 'End', '.', '-'
|
||||||
];
|
];
|
||||||
|
if (allowedKeys.includes(e.key) || e.ctrlKey || e.metaKey) return;
|
||||||
if (allowedKeys.includes(e.key) || e.ctrlKey || e.metaKey) {
|
if (!/^[0-9]$/.test(e.key)) e.preventDefault();
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!/^[0-9]$/.test(e.key)) {
|
|
||||||
e.preventDefault();
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const onPaste = (e: ClipboardEvent) => {
|
const onPaste = (e: ClipboardEvent) => {
|
||||||
if (!isNumeric.value) return;
|
if (!isNumeric.value) return;
|
||||||
|
|
||||||
const pasteData = e.clipboardData?.getData('text') || '';
|
const pasteData = e.clipboardData?.getData('text') || '';
|
||||||
if (/[^0-9.-]/g.test(pasteData)) {
|
if (/[^0-9.-]/g.test(pasteData)) {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
const sanitized = pasteData.replace(/[^0-9.-]/g, '');
|
onInput(pasteData.replace(/[^0-9.-]/g, ''));
|
||||||
onInput(sanitized);
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const onInput = (val: string | undefined) => {
|
const onInput = (val: string | undefined) => {
|
||||||
let finalVal = val ?? '';
|
let finalVal = val ?? '';
|
||||||
if (isNumeric.value) {
|
if (isNumeric.value) {
|
||||||
// 숫자가 아닌 모든 문자 원천 정제
|
|
||||||
finalVal = finalVal.replace(/[^0-9.-]/g, '');
|
finalVal = finalVal.replace(/[^0-9.-]/g, '');
|
||||||
}
|
}
|
||||||
emit('update:modelValue', finalVal);
|
emit('update:modelValue', finalVal);
|
||||||
|
|||||||
@@ -1,10 +1,13 @@
|
|||||||
<!-- PrimeVue DataTable Adapter Wrapper: QuantMasterGrid -->
|
<!-- PrimeVue DataTable Adapter Wrapper: QuantMasterGrid (Full Transparent Passthrough Wrapper) -->
|
||||||
<template>
|
<template>
|
||||||
<div class="quant-master-grid-wrapper w-full overflow-hidden border border-slate-300 rounded shadow-2xs bg-white">
|
<div class="quant-master-grid-wrapper w-full overflow-hidden border border-slate-300 rounded shadow-2xs bg-white">
|
||||||
<DataTable
|
<DataTable
|
||||||
|
v-bind="$attrs"
|
||||||
:value="items || []"
|
:value="items || []"
|
||||||
dataKey="id"
|
dataKey="id"
|
||||||
responsiveLayout="scroll"
|
responsiveLayout="scroll"
|
||||||
|
resizableColumns
|
||||||
|
columnResizeMode="fit"
|
||||||
class="p-datatable-sm w-full text-xs"
|
class="p-datatable-sm w-full text-xs"
|
||||||
:style="{ maxHeight: height ? height + 'px' : '300px' }"
|
:style="{ maxHeight: height ? height + 'px' : '300px' }"
|
||||||
>
|
>
|
||||||
@@ -14,12 +17,18 @@
|
|||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
<!-- 사용자 정의 슬롯 100% 전파 -->
|
||||||
|
<template v-for="(_, slotName) in $slots" :key="slotName" #[slotName]="slotProps">
|
||||||
|
<slot :name="slotName" v-bind="slotProps || {}"></slot>
|
||||||
|
</template>
|
||||||
|
|
||||||
<Column
|
<Column
|
||||||
v-for="col in gridHeaders"
|
v-for="col in gridHeaders"
|
||||||
:key="col.key"
|
:key="col.key"
|
||||||
:field="col.key"
|
:field="col.key"
|
||||||
:header="col.label"
|
:header="col.label"
|
||||||
:style="{ width: col.width || 'auto', textAlign: col.align || 'left' }"
|
:style="{ width: col.width || 'auto', textAlign: col.align || 'left' }"
|
||||||
|
sortable
|
||||||
>
|
>
|
||||||
<template #body="slotProps">
|
<template #body="slotProps">
|
||||||
<slot :name="col.key" :item="slotProps.data" :value="slotProps.data[col.key]">
|
<slot :name="col.key" :item="slotProps.data" :value="slotProps.data[col.key]">
|
||||||
@@ -38,6 +47,10 @@ import { computed } from 'vue';
|
|||||||
import DataTable from 'primevue/datatable';
|
import DataTable from 'primevue/datatable';
|
||||||
import Column from 'primevue/column';
|
import Column from 'primevue/column';
|
||||||
|
|
||||||
|
defineOptions({
|
||||||
|
inheritAttrs: false
|
||||||
|
});
|
||||||
|
|
||||||
export interface GridHeader {
|
export interface GridHeader {
|
||||||
key: string;
|
key: string;
|
||||||
label: string;
|
label: string;
|
||||||
@@ -53,7 +66,6 @@ const props = defineProps<{
|
|||||||
|
|
||||||
const gridHeaders = computed(() => {
|
const gridHeaders = computed(() => {
|
||||||
if (props.headers && props.headers.length > 0) return props.headers;
|
if (props.headers && props.headers.length > 0) return props.headers;
|
||||||
// Fallback defaults if not supplied
|
|
||||||
return [
|
return [
|
||||||
{ key: 'code', label: '코드', width: '120px' },
|
{ key: 'code', label: '코드', width: '120px' },
|
||||||
{ key: 'name', label: '명칭' },
|
{ key: 'name', label: '명칭' },
|
||||||
|
|||||||
Reference in New Issue
Block a user