qdadm 0.13.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +270 -0
- package/LICENSE +21 -0
- package/README.md +166 -0
- package/package.json +48 -0
- package/src/assets/logo.svg +6 -0
- package/src/components/BoolCell.vue +28 -0
- package/src/components/dialogs/BulkStatusDialog.vue +43 -0
- package/src/components/dialogs/MultiStepDialog.vue +321 -0
- package/src/components/dialogs/SimpleDialog.vue +108 -0
- package/src/components/dialogs/UnsavedChangesDialog.vue +87 -0
- package/src/components/display/CardsGrid.vue +155 -0
- package/src/components/display/CopyableId.vue +92 -0
- package/src/components/display/EmptyState.vue +114 -0
- package/src/components/display/IntensityBar.vue +171 -0
- package/src/components/display/RichCardsGrid.vue +220 -0
- package/src/components/editors/JsonEditorFoldable.vue +467 -0
- package/src/components/editors/JsonStructuredField.vue +218 -0
- package/src/components/editors/JsonViewer.vue +91 -0
- package/src/components/editors/KeyValueEditor.vue +314 -0
- package/src/components/editors/LanguageEditor.vue +245 -0
- package/src/components/editors/ScopeEditor.vue +341 -0
- package/src/components/editors/VanillaJsonEditor.vue +185 -0
- package/src/components/forms/FormActions.vue +104 -0
- package/src/components/forms/FormField.vue +64 -0
- package/src/components/forms/FormTab.vue +217 -0
- package/src/components/forms/FormTabs.vue +108 -0
- package/src/components/index.js +44 -0
- package/src/components/layout/AppLayout.vue +430 -0
- package/src/components/layout/Breadcrumb.vue +106 -0
- package/src/components/layout/PageHeader.vue +75 -0
- package/src/components/layout/PageLayout.vue +93 -0
- package/src/components/lists/ActionButtons.vue +41 -0
- package/src/components/lists/ActionColumn.vue +37 -0
- package/src/components/lists/FilterBar.vue +53 -0
- package/src/components/lists/ListPage.vue +319 -0
- package/src/composables/index.js +19 -0
- package/src/composables/useApp.js +43 -0
- package/src/composables/useAuth.js +49 -0
- package/src/composables/useBareForm.js +143 -0
- package/src/composables/useBreadcrumb.js +221 -0
- package/src/composables/useDirtyState.js +103 -0
- package/src/composables/useEntityTitle.js +121 -0
- package/src/composables/useForm.js +254 -0
- package/src/composables/useGuardStore.js +37 -0
- package/src/composables/useJsonSyntax.js +101 -0
- package/src/composables/useListPageBuilder.js +1176 -0
- package/src/composables/useNavigation.js +89 -0
- package/src/composables/usePageBuilder.js +334 -0
- package/src/composables/useStatus.js +146 -0
- package/src/composables/useSubEditor.js +165 -0
- package/src/composables/useTabSync.js +110 -0
- package/src/composables/useUnsavedChangesGuard.js +122 -0
- package/src/entity/EntityManager.js +540 -0
- package/src/entity/index.js +11 -0
- package/src/entity/storage/ApiStorage.js +146 -0
- package/src/entity/storage/LocalStorage.js +220 -0
- package/src/entity/storage/MemoryStorage.js +201 -0
- package/src/entity/storage/index.js +10 -0
- package/src/index.js +29 -0
- package/src/kernel/Kernel.js +234 -0
- package/src/kernel/index.js +7 -0
- package/src/module/index.js +16 -0
- package/src/module/moduleRegistry.js +222 -0
- package/src/orchestrator/Orchestrator.js +141 -0
- package/src/orchestrator/index.js +8 -0
- package/src/orchestrator/useOrchestrator.js +61 -0
- package/src/plugin.js +142 -0
- package/src/styles/_alerts.css +48 -0
- package/src/styles/_code.css +33 -0
- package/src/styles/_dialogs.css +17 -0
- package/src/styles/_markdown.css +82 -0
- package/src/styles/_show-pages.css +84 -0
- package/src/styles/index.css +16 -0
- package/src/styles/main.css +845 -0
- package/src/styles/theme/components.css +286 -0
- package/src/styles/theme/index.css +10 -0
- package/src/styles/theme/tokens.css +125 -0
- package/src/styles/theme/utilities.css +172 -0
- package/src/utils/debugInjector.js +261 -0
- package/src/utils/formatters.js +165 -0
- package/src/utils/index.js +35 -0
- package/src/utils/transformers.js +105 -0
|
@@ -0,0 +1,217 @@
|
|
|
1
|
+
<script setup>
|
|
2
|
+
/**
|
|
3
|
+
* FormTab - Normalized tab header component
|
|
4
|
+
*
|
|
5
|
+
* Provides consistent tab headers with optional icon, count badge, and visibility control.
|
|
6
|
+
*
|
|
7
|
+
* Props:
|
|
8
|
+
* - value: Tab identifier (required)
|
|
9
|
+
* - label: Display text (required)
|
|
10
|
+
* - icon: PrimeIcon class (e.g., 'pi-cog')
|
|
11
|
+
* - count: Number to display as badge
|
|
12
|
+
* - badge: Custom badge text/value
|
|
13
|
+
* - badgeSeverity: Badge color ('secondary', 'info', 'success', 'warn', 'danger')
|
|
14
|
+
* - visible: Show/hide tab (default: true)
|
|
15
|
+
* - disabled: Disable tab interaction
|
|
16
|
+
*
|
|
17
|
+
* Usage:
|
|
18
|
+
* <FormTab value="general" label="General" icon="pi-cog" />
|
|
19
|
+
* <FormTab value="items" label="Items" :count="5" />
|
|
20
|
+
* <FormTab value="errors" label="Errors" :count="errors.length" badge-severity="danger" />
|
|
21
|
+
* <FormTab value="advanced" label="Advanced" :visible="isEdit" />
|
|
22
|
+
*/
|
|
23
|
+
|
|
24
|
+
import Tab from 'primevue/tab'
|
|
25
|
+
import Tag from 'primevue/tag'
|
|
26
|
+
|
|
27
|
+
const props = defineProps({
|
|
28
|
+
/**
|
|
29
|
+
* Tab identifier (used for v-model matching)
|
|
30
|
+
*/
|
|
31
|
+
value: {
|
|
32
|
+
type: String,
|
|
33
|
+
required: true
|
|
34
|
+
},
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Display label
|
|
38
|
+
*/
|
|
39
|
+
label: {
|
|
40
|
+
type: String,
|
|
41
|
+
required: true
|
|
42
|
+
},
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* PrimeIcon class (without 'pi' prefix or with full class)
|
|
46
|
+
* Examples: 'pi-cog', 'cog', 'pi pi-cog'
|
|
47
|
+
*/
|
|
48
|
+
icon: {
|
|
49
|
+
type: String,
|
|
50
|
+
default: null
|
|
51
|
+
},
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Count to display as badge (shows only if > 0)
|
|
55
|
+
*/
|
|
56
|
+
count: {
|
|
57
|
+
type: Number,
|
|
58
|
+
default: null
|
|
59
|
+
},
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Custom badge text (alternative to count)
|
|
63
|
+
*/
|
|
64
|
+
badge: {
|
|
65
|
+
type: [String, Number],
|
|
66
|
+
default: null
|
|
67
|
+
},
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Badge severity/color
|
|
71
|
+
*/
|
|
72
|
+
badgeSeverity: {
|
|
73
|
+
type: String,
|
|
74
|
+
default: 'secondary',
|
|
75
|
+
validator: (v) => ['secondary', 'info', 'success', 'warn', 'danger', 'contrast'].includes(v)
|
|
76
|
+
},
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* Show/hide the tab
|
|
80
|
+
*/
|
|
81
|
+
visible: {
|
|
82
|
+
type: Boolean,
|
|
83
|
+
default: true
|
|
84
|
+
},
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* Disable tab interaction
|
|
88
|
+
*/
|
|
89
|
+
disabled: {
|
|
90
|
+
type: Boolean,
|
|
91
|
+
default: false
|
|
92
|
+
}
|
|
93
|
+
})
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* Normalize icon class
|
|
97
|
+
*/
|
|
98
|
+
function getIconClass() {
|
|
99
|
+
if (!props.icon) return null
|
|
100
|
+
// Already has 'pi' prefix
|
|
101
|
+
if (props.icon.startsWith('pi')) {
|
|
102
|
+
return props.icon.includes(' ') ? props.icon : `pi ${props.icon}`
|
|
103
|
+
}
|
|
104
|
+
// Just icon name
|
|
105
|
+
return `pi pi-${props.icon}`
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* Get count value to display
|
|
110
|
+
*/
|
|
111
|
+
function getCountValue() {
|
|
112
|
+
if (props.count !== null && props.count > 0) return props.count
|
|
113
|
+
return null
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
/**
|
|
117
|
+
* Get badge value to display (separate from count)
|
|
118
|
+
*/
|
|
119
|
+
function getBadgeValue() {
|
|
120
|
+
if (props.badge !== null) return props.badge
|
|
121
|
+
// If no badge but count provided, show count as badge (legacy behavior)
|
|
122
|
+
if (props.count !== null && props.count > 0 && props.badge === null) return props.count
|
|
123
|
+
return null
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
/**
|
|
127
|
+
* Check if we should show count and badge separately
|
|
128
|
+
*/
|
|
129
|
+
function showBothCountAndBadge() {
|
|
130
|
+
return props.count !== null && props.count > 0 && props.badge !== null
|
|
131
|
+
}
|
|
132
|
+
</script>
|
|
133
|
+
|
|
134
|
+
<template>
|
|
135
|
+
<Tab
|
|
136
|
+
v-if="visible"
|
|
137
|
+
:value="value"
|
|
138
|
+
:disabled="disabled"
|
|
139
|
+
>
|
|
140
|
+
<i v-if="icon" :class="getIconClass()" class="tab-icon" ></i>
|
|
141
|
+
<span class="tab-label">{{ label }}</span>
|
|
142
|
+
<!-- When both count and badge are provided, show count first then badge indicator -->
|
|
143
|
+
<template v-if="showBothCountAndBadge()">
|
|
144
|
+
<Tag
|
|
145
|
+
:value="String(getCountValue())"
|
|
146
|
+
severity="secondary"
|
|
147
|
+
class="tab-badge"
|
|
148
|
+
/>
|
|
149
|
+
<span class="tab-alert-dot" :class="`alert-${badgeSeverity}`"></span>
|
|
150
|
+
</template>
|
|
151
|
+
<!-- Otherwise show single badge (legacy behavior) -->
|
|
152
|
+
<Tag
|
|
153
|
+
v-else-if="getBadgeValue() !== null"
|
|
154
|
+
:value="String(getBadgeValue())"
|
|
155
|
+
:severity="badgeSeverity"
|
|
156
|
+
class="tab-badge"
|
|
157
|
+
/>
|
|
158
|
+
</Tab>
|
|
159
|
+
</template>
|
|
160
|
+
|
|
161
|
+
<style scoped>
|
|
162
|
+
.tab-icon {
|
|
163
|
+
margin-right: 0.5rem;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
.tab-label {
|
|
167
|
+
/* Ensure consistent text rendering */
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
.tab-badge {
|
|
171
|
+
margin-left: 0.5rem;
|
|
172
|
+
font-size: 0.7rem;
|
|
173
|
+
padding: 0.15rem 0.4rem;
|
|
174
|
+
min-width: 1.25rem;
|
|
175
|
+
text-align: center;
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
.tab-alert-dot {
|
|
179
|
+
display: inline-block;
|
|
180
|
+
width: 8px !important;
|
|
181
|
+
height: 8px !important;
|
|
182
|
+
min-width: 8px;
|
|
183
|
+
max-width: 8px;
|
|
184
|
+
border-radius: 50%;
|
|
185
|
+
margin-left: 0.35rem;
|
|
186
|
+
animation: pulse 1.5s infinite;
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
.alert-warn {
|
|
190
|
+
background-color: var(--p-orange-500);
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
.alert-danger {
|
|
194
|
+
background-color: var(--p-red-500);
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
.alert-info {
|
|
198
|
+
background-color: var(--p-blue-500);
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
.alert-success {
|
|
202
|
+
background-color: var(--p-green-500);
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
.alert-secondary {
|
|
206
|
+
background-color: var(--p-gray-500);
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
.alert-contrast {
|
|
210
|
+
background-color: var(--p-primary-500);
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
@keyframes pulse {
|
|
214
|
+
0%, 100% { opacity: 1; }
|
|
215
|
+
50% { opacity: 0.5; }
|
|
216
|
+
}
|
|
217
|
+
</style>
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
<script setup>
|
|
2
|
+
/**
|
|
3
|
+
* FormTabs - Normalized tab container for forms
|
|
4
|
+
*
|
|
5
|
+
* Provides consistent styling and behavior for form tabs.
|
|
6
|
+
* Works with FormTab component for individual tabs.
|
|
7
|
+
*
|
|
8
|
+
* Usage:
|
|
9
|
+
* <FormTabs v-model="activeTab" @update:modelValue="onTabChange">
|
|
10
|
+
* <template #tabs>
|
|
11
|
+
* <FormTab value="general" label="General" icon="pi-cog" />
|
|
12
|
+
* <FormTab value="items" label="Items" icon="pi-list" :count="items.length" />
|
|
13
|
+
* <FormTab value="advanced" label="Advanced" :visible="isEdit" />
|
|
14
|
+
* </template>
|
|
15
|
+
* <template #panels>
|
|
16
|
+
* <TabPanel value="general">...</TabPanel>
|
|
17
|
+
* <TabPanel value="items">...</TabPanel>
|
|
18
|
+
* <TabPanel value="advanced">...</TabPanel>
|
|
19
|
+
* </template>
|
|
20
|
+
* </FormTabs>
|
|
21
|
+
*/
|
|
22
|
+
|
|
23
|
+
import Tabs from 'primevue/tabs'
|
|
24
|
+
import TabList from 'primevue/tablist'
|
|
25
|
+
import TabPanels from 'primevue/tabpanels'
|
|
26
|
+
|
|
27
|
+
defineProps({
|
|
28
|
+
/**
|
|
29
|
+
* Active tab value (v-model)
|
|
30
|
+
*/
|
|
31
|
+
modelValue: {
|
|
32
|
+
type: String,
|
|
33
|
+
required: true
|
|
34
|
+
}
|
|
35
|
+
})
|
|
36
|
+
|
|
37
|
+
const emit = defineEmits(['update:modelValue'])
|
|
38
|
+
|
|
39
|
+
function onTabChange(value) {
|
|
40
|
+
emit('update:modelValue', value)
|
|
41
|
+
}
|
|
42
|
+
</script>
|
|
43
|
+
|
|
44
|
+
<template>
|
|
45
|
+
<div class="form-tabs">
|
|
46
|
+
<Tabs :value="modelValue" @update:value="onTabChange">
|
|
47
|
+
<TabList class="form-tabs__list">
|
|
48
|
+
<slot name="tabs" ></slot>
|
|
49
|
+
</TabList>
|
|
50
|
+
<TabPanels class="form-tabs__panels">
|
|
51
|
+
<slot name="panels" ></slot>
|
|
52
|
+
</TabPanels>
|
|
53
|
+
</Tabs>
|
|
54
|
+
</div>
|
|
55
|
+
</template>
|
|
56
|
+
|
|
57
|
+
<style scoped>
|
|
58
|
+
.form-tabs {
|
|
59
|
+
margin-top: 1rem;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
.form-tabs :deep(.form-tabs__list) {
|
|
63
|
+
border-bottom: 1px solid var(--p-surface-200);
|
|
64
|
+
gap: 0;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
.form-tabs :deep(.form-tabs__panels) {
|
|
68
|
+
padding: 1.5rem 0;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/* Tab styling */
|
|
72
|
+
.form-tabs :deep(.p-tab) {
|
|
73
|
+
padding: 0.75rem 1.25rem;
|
|
74
|
+
border-radius: 0;
|
|
75
|
+
border-bottom: 2px solid transparent;
|
|
76
|
+
margin-bottom: -1px;
|
|
77
|
+
transition: all 0.2s ease;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
.form-tabs :deep(.p-tab:hover) {
|
|
81
|
+
background: var(--p-surface-50);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
.form-tabs :deep(.p-tab[data-p-active="true"]) {
|
|
85
|
+
border-bottom-color: var(--p-primary-500);
|
|
86
|
+
color: var(--p-primary-500);
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/* Tab icon alignment */
|
|
90
|
+
.form-tabs :deep(.p-tab .pi) {
|
|
91
|
+
margin-right: 0.5rem;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/* Tab badge/count styling */
|
|
95
|
+
.form-tabs :deep(.tab-badge) {
|
|
96
|
+
margin-left: 0.5rem;
|
|
97
|
+
font-size: 0.7rem;
|
|
98
|
+
padding: 0.15rem 0.4rem;
|
|
99
|
+
min-width: 1.25rem;
|
|
100
|
+
text-align: center;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/* Disabled tab */
|
|
104
|
+
.form-tabs :deep(.p-tab[data-p-disabled="true"]) {
|
|
105
|
+
opacity: 0.5;
|
|
106
|
+
cursor: not-allowed;
|
|
107
|
+
}
|
|
108
|
+
</style>
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* qdadm - Components exports
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
// Layout
|
|
6
|
+
export { default as AppLayout } from './layout/AppLayout.vue'
|
|
7
|
+
export { default as PageLayout } from './layout/PageLayout.vue'
|
|
8
|
+
export { default as PageHeader } from './layout/PageHeader.vue'
|
|
9
|
+
export { default as Breadcrumb } from './layout/Breadcrumb.vue'
|
|
10
|
+
|
|
11
|
+
// Forms
|
|
12
|
+
export { default as FormField } from './forms/FormField.vue'
|
|
13
|
+
export { default as FormActions } from './forms/FormActions.vue'
|
|
14
|
+
export { default as FormTabs } from './forms/FormTabs.vue'
|
|
15
|
+
export { default as FormTab } from './forms/FormTab.vue'
|
|
16
|
+
|
|
17
|
+
// Lists
|
|
18
|
+
export { default as ListPage } from './lists/ListPage.vue'
|
|
19
|
+
export { default as ActionButtons } from './lists/ActionButtons.vue'
|
|
20
|
+
export { default as ActionColumn } from './lists/ActionColumn.vue'
|
|
21
|
+
export { default as FilterBar } from './lists/FilterBar.vue'
|
|
22
|
+
|
|
23
|
+
// Editors
|
|
24
|
+
export { default as KeyValueEditor } from './editors/KeyValueEditor.vue'
|
|
25
|
+
export { default as LanguageEditor } from './editors/LanguageEditor.vue'
|
|
26
|
+
export { default as ScopeEditor } from './editors/ScopeEditor.vue'
|
|
27
|
+
export { default as VanillaJsonEditor } from './editors/VanillaJsonEditor.vue'
|
|
28
|
+
export { default as JsonEditorFoldable } from './editors/JsonEditorFoldable.vue'
|
|
29
|
+
export { default as JsonStructuredField } from './editors/JsonStructuredField.vue'
|
|
30
|
+
export { default as JsonViewer } from './editors/JsonViewer.vue'
|
|
31
|
+
|
|
32
|
+
// Dialogs
|
|
33
|
+
export { default as SimpleDialog } from './dialogs/SimpleDialog.vue'
|
|
34
|
+
export { default as MultiStepDialog } from './dialogs/MultiStepDialog.vue'
|
|
35
|
+
export { default as BulkStatusDialog } from './dialogs/BulkStatusDialog.vue'
|
|
36
|
+
export { default as UnsavedChangesDialog } from './dialogs/UnsavedChangesDialog.vue'
|
|
37
|
+
|
|
38
|
+
// Display
|
|
39
|
+
export { default as CardsGrid } from './display/CardsGrid.vue'
|
|
40
|
+
export { default as RichCardsGrid } from './display/RichCardsGrid.vue'
|
|
41
|
+
export { default as CopyableId } from './display/CopyableId.vue'
|
|
42
|
+
export { default as EmptyState } from './display/EmptyState.vue'
|
|
43
|
+
export { default as IntensityBar } from './display/IntensityBar.vue'
|
|
44
|
+
export { default as BoolCell } from './BoolCell.vue'
|