intelligent-system-design-language 0.3.21 → 0.3.23
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/.claude/agents/langium-language-designer.md +38 -38
- package/.claude/agents/typescript-vscode-expert.md +29 -29
- package/.claude/agents/ui-ux-designer.md +36 -36
- package/.claude/settings.local.json +33 -33
- package/.idea/inspectionProfiles/Project_Default.xml +6 -6
- package/.idea/isdl.iml +13 -13
- package/.idea/modules.xml +8 -8
- package/.idea/vcs.xml +6 -6
- package/.idea/watcherTasks.xml +3 -3
- package/.vscodeignore +18 -18
- package/LICENSE +673 -673
- package/README.md +86 -86
- package/bin/cli.js +4 -4
- package/bin/lsp.js +8 -8
- package/out/_backgrounds.scss +91 -91
- package/out/_handlebars.scss +497 -497
- package/out/_isdlStyles.scss +1444 -1381
- package/out/_vuetifyOverrides.scss +425 -425
- package/out/_vuetifyStyles.scss +31957 -31957
- package/out/cli/components/_backgrounds.scss +91 -91
- package/out/cli/components/_handlebars.scss +497 -497
- package/out/cli/components/_isdlStyles.scss +1444 -1381
- package/out/cli/components/_vuetifyOverrides.scss +425 -425
- package/out/cli/components/_vuetifyStyles.scss +31957 -31957
- package/out/cli/components/active-effect-sheet-generator.js +453 -453
- package/out/cli/components/chat-card-generator.js +654 -651
- package/out/cli/components/chat-card-generator.js.map +1 -1
- package/out/cli/components/css-generator.js +4 -4
- package/out/cli/components/damage-roll-generator.js +160 -160
- package/out/cli/components/datamodel-generator.js +264 -257
- package/out/cli/components/datamodel-generator.js.map +1 -1
- package/out/cli/components/derived-data-generator.js +923 -923
- package/out/cli/components/hotbar-drop-hook-generator.js +82 -82
- package/out/cli/components/init-hook-generator.js +495 -495
- package/out/cli/components/language-generator.js +1 -1
- package/out/cli/components/language-generator.js.map +1 -1
- package/out/cli/components/measured-template-preview.js +221 -221
- package/out/cli/components/method-generator.js +979 -887
- package/out/cli/components/method-generator.js.map +1 -1
- package/out/cli/components/ready-hook-generator.js +404 -404
- package/out/cli/components/token-generator.js +116 -116
- package/out/cli/components/vue/base-components/vue-attribute.js +138 -138
- package/out/cli/components/vue/base-components/vue-boolean.js +64 -64
- package/out/cli/components/vue/base-components/vue-calculator.js +93 -93
- package/out/cli/components/vue/base-components/vue-damage-application.js +356 -356
- package/out/cli/components/vue/base-components/vue-damage-bonuses.js +165 -165
- package/out/cli/components/vue/base-components/vue-damage-resistances.js +196 -196
- package/out/cli/components/vue/base-components/vue-damage-track.js +121 -121
- package/out/cli/components/vue/base-components/vue-date-time.js +42 -42
- package/out/cli/components/vue/base-components/vue-dice.js +98 -98
- package/out/cli/components/vue/base-components/vue-die.js +73 -73
- package/out/cli/components/vue/base-components/vue-document-choice.js +149 -149
- package/out/cli/components/vue/base-components/vue-document-choices.js +179 -179
- package/out/cli/components/vue/base-components/vue-document-link.js +60 -60
- package/out/cli/components/vue/base-components/vue-extended-choice.js +88 -88
- package/out/cli/components/vue/base-components/vue-inventory.js +519 -519
- package/out/cli/components/vue/base-components/vue-macro-choice.js +138 -138
- package/out/cli/components/vue/base-components/vue-measured-template.js +530 -530
- package/out/cli/components/vue/base-components/vue-money.js +483 -483
- package/out/cli/components/vue/base-components/vue-number.js +174 -174
- package/out/cli/components/vue/base-components/vue-paperdoll.js +43 -43
- package/out/cli/components/vue/base-components/vue-parent-property-reference.js +76 -76
- package/out/cli/components/vue/base-components/vue-prosemirror.js +18 -18
- package/out/cli/components/vue/base-components/vue-resource.js +136 -136
- package/out/cli/components/vue/base-components/vue-roll-visualizer.js +286 -109
- package/out/cli/components/vue/base-components/vue-roll-visualizer.js.map +1 -1
- package/out/cli/components/vue/base-components/vue-self-property-reference.js +62 -62
- package/out/cli/components/vue/base-components/vue-string-choice.js +98 -98
- package/out/cli/components/vue/base-components/vue-string-choices.js +203 -203
- package/out/cli/components/vue/base-components/vue-string.js +60 -60
- package/out/cli/components/vue/base-components/vue-text-field.js +53 -53
- package/out/cli/components/vue/base-components/vue-tracker.js +431 -431
- package/out/cli/components/vue/vue-action-component-generator.js +64 -64
- package/out/cli/components/vue/vue-active-effect-sheet-generator.js +856 -856
- package/out/cli/components/vue/vue-datatable-sheet-class-generator.js +292 -292
- package/out/cli/components/vue/vue-datatable2-component-generator.js +824 -824
- package/out/cli/components/vue/vue-document-creation-app.js +121 -121
- package/out/cli/components/vue/vue-document-creation-sheet.js +94 -94
- package/out/cli/components/vue/vue-generator.js +40 -40
- package/out/cli/components/vue/vue-mixin.js +296 -296
- package/out/cli/components/vue/vue-pinned-datatable-component-generator.js +260 -260
- package/out/cli/components/vue/vue-prompt-generator.js +91 -76
- package/out/cli/components/vue/vue-prompt-generator.js.map +1 -1
- package/out/cli/components/vue/vue-prompt-sheet-class-generator.js +317 -317
- package/out/cli/components/vue/vue-sheet-application-generator.js +1177 -1167
- package/out/cli/components/vue/vue-sheet-application-generator.js.map +1 -1
- package/out/cli/components/vue/vue-sheet-class-generator.js +510 -510
- package/out/cli/generator.js +438 -433
- package/out/cli/generator.js.map +1 -1
- package/out/extension/github/githubAuthProvider.js +71 -29
- package/out/extension/github/githubAuthProvider.js.map +1 -1
- package/out/extension/github/githubGistManager.js +4 -3
- package/out/extension/github/githubGistManager.js.map +1 -1
- package/out/extension/github/githubManager.js +40 -38
- package/out/extension/github/githubManager.js.map +1 -1
- package/out/extension/github/githubQuickActions.js +120 -120
- package/out/extension/github/system-workflow.yml +47 -47
- package/out/extension/main.cjs +909 -532
- package/out/extension/main.cjs.map +3 -3
- package/out/extension/package.json +419 -419
- package/out/language/generated/ast.js +51 -2
- package/out/language/generated/ast.js.map +1 -1
- package/out/language/generated/grammar.js +14240 -13991
- package/out/language/generated/grammar.js.map +1 -1
- package/out/language/intelligent-system-design-language-validator.js +32 -2
- package/out/language/intelligent-system-design-language-validator.js.map +1 -1
- package/out/language/isdl-scope-provider.js +14 -1
- package/out/language/isdl-scope-provider.js.map +1 -1
- package/out/language/main.cjs +913 -569
- package/out/language/main.cjs.map +3 -3
- package/out/package.json +419 -419
- package/out/progressbar.min.js +6 -6
- package/out/styles.scss +762 -747
- package/out/test/validating/diagnostics.test.js +40 -0
- package/out/test/validating/diagnostics.test.js.map +1 -1
- package/package.json +419 -419
|
@@ -46,8 +46,8 @@ export function generateVuetifyDatatableComponent(entry, id, document, pageName,
|
|
|
46
46
|
function generateDataTableHeader(refDoc, property) {
|
|
47
47
|
var _a;
|
|
48
48
|
if (isLayout(property)) {
|
|
49
|
-
return expandToNode `
|
|
50
|
-
${joinToNode(property.body, p => generateDataTableHeader(refDoc, p), { appendNewLineIfNotEmpty: true })}
|
|
49
|
+
return expandToNode `
|
|
50
|
+
${joinToNode(property.body, p => generateDataTableHeader(refDoc, p), { appendNewLineIfNotEmpty: true })}
|
|
51
51
|
`;
|
|
52
52
|
}
|
|
53
53
|
if (isHtmlExp(property) || isInitiativeProperty(property) || isPaperDollElement(property) || isHookHandler(property))
|
|
@@ -70,8 +70,8 @@ export function generateVuetifyDatatableComponent(entry, id, document, pageName,
|
|
|
70
70
|
// Set appropriate minimum widths based on field type
|
|
71
71
|
if (isStringExp(property)) {
|
|
72
72
|
minWidth = '140px'; // Text fields need more space for content
|
|
73
|
-
return expandToNode `
|
|
74
|
-
{ title: game.i18n.localize("${localizeName}"), key: '${systemPath}', sortable: ${sortable}, minWidth: '${minWidth}', visibility: '${visibility}' },
|
|
73
|
+
return expandToNode `
|
|
74
|
+
{ title: game.i18n.localize("${localizeName}"), key: '${systemPath}', sortable: ${sortable}, minWidth: '${minWidth}', visibility: '${visibility}' },
|
|
75
75
|
`;
|
|
76
76
|
}
|
|
77
77
|
if (isDocumentChoiceExp(property) || isStringChoiceField(property) || isDamageTypeChoiceField(property)) {
|
|
@@ -93,16 +93,16 @@ export function generateVuetifyDatatableComponent(entry, id, document, pageName,
|
|
|
93
93
|
if (isMeasuredTemplateField(property)) {
|
|
94
94
|
minWidth = '130px'; // Template fields have longer text
|
|
95
95
|
}
|
|
96
|
-
return expandToNode `
|
|
97
|
-
{ title: game.i18n.localize("${localizeName}"), key: '${systemPath}', sortable: ${sortable}, minWidth: '${minWidth}', type: '${property.$type}', visibility: '${visibility}' },
|
|
96
|
+
return expandToNode `
|
|
97
|
+
{ title: game.i18n.localize("${localizeName}"), key: '${systemPath}', sortable: ${sortable}, minWidth: '${minWidth}', type: '${property.$type}', visibility: '${visibility}' },
|
|
98
98
|
`;
|
|
99
99
|
}
|
|
100
100
|
return undefined;
|
|
101
101
|
}
|
|
102
102
|
function generateSlotTemplate(refDoc, property) {
|
|
103
103
|
if (isLayout(property)) {
|
|
104
|
-
return expandToNode `
|
|
105
|
-
${joinToNode(property.body, p => generateSlotTemplate(refDoc, p), { appendNewLineIfNotEmpty: true })}
|
|
104
|
+
return expandToNode `
|
|
105
|
+
${joinToNode(property.body, p => generateSlotTemplate(refDoc, p), { appendNewLineIfNotEmpty: true })}
|
|
106
106
|
`;
|
|
107
107
|
}
|
|
108
108
|
if (isHtmlExp(property) || isInitiativeProperty(property) || isPaperDollElement(property) || isHookHandler(property))
|
|
@@ -115,117 +115,117 @@ export function generateVuetifyDatatableComponent(entry, id, document, pageName,
|
|
|
115
115
|
let systemPath = getSystemPath(property, [], undefined, false);
|
|
116
116
|
const slotName = systemPath;
|
|
117
117
|
if (isBooleanExp(property)) {
|
|
118
|
-
return expandToNode `
|
|
119
|
-
<template v-if="isColumnVisible('${systemPath}')" v-slot:item.${slotName}="{ item }">
|
|
120
|
-
<v-chip
|
|
121
|
-
:color="getNestedValue(item, '${systemPath}') ? props.primaryColor : props.secondaryColor"
|
|
122
|
-
size="x-small"
|
|
123
|
-
variant="elevated"
|
|
124
|
-
class="text-caption"
|
|
125
|
-
label
|
|
126
|
-
>
|
|
127
|
-
<v-icon v-if="getNestedValue(item, '${systemPath}')">fa-solid fa-check</v-icon>
|
|
128
|
-
<v-icon v-else>fa-solid fa-times</v-icon>
|
|
129
|
-
</v-chip>
|
|
130
|
-
</template>
|
|
118
|
+
return expandToNode `
|
|
119
|
+
<template v-if="isColumnVisible('${systemPath}')" v-slot:item.${slotName}="{ item }">
|
|
120
|
+
<v-chip
|
|
121
|
+
:color="getNestedValue(item, '${systemPath}') ? props.primaryColor : props.secondaryColor"
|
|
122
|
+
size="x-small"
|
|
123
|
+
variant="elevated"
|
|
124
|
+
class="text-caption"
|
|
125
|
+
label
|
|
126
|
+
>
|
|
127
|
+
<v-icon v-if="getNestedValue(item, '${systemPath}')">fa-solid fa-check</v-icon>
|
|
128
|
+
<v-icon v-else>fa-solid fa-times</v-icon>
|
|
129
|
+
</v-chip>
|
|
130
|
+
</template>
|
|
131
131
|
`;
|
|
132
132
|
}
|
|
133
133
|
if (isStringExp(property)) {
|
|
134
|
-
return expandToNode `
|
|
135
|
-
<template v-if="isColumnVisible('${systemPath}')" v-slot:item.${slotName}="{ item }">
|
|
136
|
-
<div class="text-caption text-truncate" :data-tooltip="getNestedValue(item, '${systemPath}')" style="max-width: 300px;">
|
|
137
|
-
{{ getNestedValue(item, '${systemPath}') }}
|
|
138
|
-
</div>
|
|
139
|
-
</template>
|
|
134
|
+
return expandToNode `
|
|
135
|
+
<template v-if="isColumnVisible('${systemPath}')" v-slot:item.${slotName}="{ item }">
|
|
136
|
+
<div class="text-caption text-truncate" :data-tooltip="getNestedValue(item, '${systemPath}')" style="max-width: 300px;">
|
|
137
|
+
{{ getNestedValue(item, '${systemPath}') }}
|
|
138
|
+
</div>
|
|
139
|
+
</template>
|
|
140
140
|
`;
|
|
141
141
|
}
|
|
142
142
|
if (isStringChoiceField(property)) {
|
|
143
143
|
systemPath = systemPath.replace(/\.(value)$/, '');
|
|
144
144
|
const parentDocument = AstUtils.getContainerOfType(property, isDocument);
|
|
145
|
-
return expandToNode `
|
|
146
|
-
<template v-if="isColumnVisible('${systemPath}')" v-slot:item.${slotName}="{ item }">
|
|
147
|
-
<v-chip label size="x-small" variant="elevated" class="text-caption" :color="getNestedValue(item, '${systemPath}.color')" :prepend-icon="getNestedValue(item, '${systemPath}.icon')" :data-tooltip="getExtendedChoiceTooltip(item, '${systemPath}')">
|
|
148
|
-
{{ game.i18n.localize('${parentDocument === null || parentDocument === void 0 ? void 0 : parentDocument.name}.${property.name}.' + getNestedValue(item, '${systemPath}.value')) }}
|
|
149
|
-
</v-chip>
|
|
150
|
-
</template>
|
|
145
|
+
return expandToNode `
|
|
146
|
+
<template v-if="isColumnVisible('${systemPath}')" v-slot:item.${slotName}="{ item }">
|
|
147
|
+
<v-chip label size="x-small" variant="elevated" class="text-caption" :color="getNestedValue(item, '${systemPath}.color')" :prepend-icon="getNestedValue(item, '${systemPath}.icon')" :data-tooltip="getExtendedChoiceTooltip(item, '${systemPath}')">
|
|
148
|
+
{{ game.i18n.localize('${parentDocument === null || parentDocument === void 0 ? void 0 : parentDocument.name}.${property.name}.' + getNestedValue(item, '${systemPath}.value')) }}
|
|
149
|
+
</v-chip>
|
|
150
|
+
</template>
|
|
151
151
|
`;
|
|
152
152
|
}
|
|
153
153
|
if (isParentPropertyRefExp(property)) {
|
|
154
|
-
return expandToNode `
|
|
155
|
-
<template v-if="isColumnVisible('${systemPath}')" v-slot:item.${slotName}="{ item }">
|
|
156
|
-
<span class="text-caption">{{ humanize(getNestedValue(item, '${systemPath}')) }}</span>
|
|
157
|
-
</template>
|
|
154
|
+
return expandToNode `
|
|
155
|
+
<template v-if="isColumnVisible('${systemPath}')" v-slot:item.${slotName}="{ item }">
|
|
156
|
+
<span class="text-caption">{{ humanize(getNestedValue(item, '${systemPath}')) }}</span>
|
|
157
|
+
</template>
|
|
158
158
|
`;
|
|
159
159
|
}
|
|
160
160
|
if (isResourceExp(property) || isTrackerExp(property)) {
|
|
161
161
|
systemPath = systemPath.replace(/\.(value|max)$/, '');
|
|
162
|
-
return expandToNode `
|
|
163
|
-
<template v-if="isColumnVisible('${systemPath}')" v-slot:item.${slotName.replace(".value", "")}="{ item }">
|
|
164
|
-
<v-chip
|
|
165
|
-
label
|
|
166
|
-
:color="getResourceColor(getNestedValue(item, '${systemPath}'))"
|
|
167
|
-
size="x-small"
|
|
168
|
-
variant="elevated"
|
|
169
|
-
class="text-caption"
|
|
170
|
-
>
|
|
171
|
-
{{ getNestedValue(item, '${systemPath}.value') }}/{{ getNestedValue(item, '${systemPath}.max') }}
|
|
172
|
-
</v-chip>
|
|
173
|
-
</template>
|
|
162
|
+
return expandToNode `
|
|
163
|
+
<template v-if="isColumnVisible('${systemPath}')" v-slot:item.${slotName.replace(".value", "")}="{ item }">
|
|
164
|
+
<v-chip
|
|
165
|
+
label
|
|
166
|
+
:color="getResourceColor(getNestedValue(item, '${systemPath}'))"
|
|
167
|
+
size="x-small"
|
|
168
|
+
variant="elevated"
|
|
169
|
+
class="text-caption"
|
|
170
|
+
>
|
|
171
|
+
{{ getNestedValue(item, '${systemPath}.value') }}/{{ getNestedValue(item, '${systemPath}.max') }}
|
|
172
|
+
</v-chip>
|
|
173
|
+
</template>
|
|
174
174
|
`;
|
|
175
175
|
}
|
|
176
176
|
if (isDieField(property)) {
|
|
177
|
-
return expandToNode `
|
|
178
|
-
<template v-if="isColumnVisible('${systemPath}')" v-slot:item.${slotName}="{ item }">
|
|
179
|
-
<v-chip
|
|
180
|
-
label
|
|
181
|
-
color="primary"
|
|
182
|
-
size="x-small"
|
|
183
|
-
variant="elevated"
|
|
184
|
-
prepend-icon="fa-solid fa-dice"
|
|
185
|
-
class="text-caption"
|
|
186
|
-
>
|
|
187
|
-
{{ getNestedValue(item, '${systemPath}') }}
|
|
188
|
-
</v-chip>
|
|
189
|
-
</template>
|
|
177
|
+
return expandToNode `
|
|
178
|
+
<template v-if="isColumnVisible('${systemPath}')" v-slot:item.${slotName}="{ item }">
|
|
179
|
+
<v-chip
|
|
180
|
+
label
|
|
181
|
+
color="primary"
|
|
182
|
+
size="x-small"
|
|
183
|
+
variant="elevated"
|
|
184
|
+
prepend-icon="fa-solid fa-dice"
|
|
185
|
+
class="text-caption"
|
|
186
|
+
>
|
|
187
|
+
{{ getNestedValue(item, '${systemPath}') }}
|
|
188
|
+
</v-chip>
|
|
189
|
+
</template>
|
|
190
190
|
`;
|
|
191
191
|
}
|
|
192
192
|
if (isDiceField(property)) {
|
|
193
|
-
return expandToNode `
|
|
194
|
-
<template v-if="isColumnVisible('${systemPath}')" v-slot:item.${slotName}="{ item }">
|
|
195
|
-
<v-chip
|
|
196
|
-
label
|
|
197
|
-
color="primary"
|
|
198
|
-
size="x-small"
|
|
199
|
-
variant="elevated"
|
|
200
|
-
prepend-icon="fa-solid fa-dice"
|
|
201
|
-
class="text-caption"
|
|
202
|
-
>
|
|
203
|
-
{{ getNestedValue(item, '${systemPath}').number }}{{ getNestedValue(item, '${systemPath}').die }}
|
|
204
|
-
</v-chip>
|
|
205
|
-
</template>
|
|
193
|
+
return expandToNode `
|
|
194
|
+
<template v-if="isColumnVisible('${systemPath}')" v-slot:item.${slotName}="{ item }">
|
|
195
|
+
<v-chip
|
|
196
|
+
label
|
|
197
|
+
color="primary"
|
|
198
|
+
size="x-small"
|
|
199
|
+
variant="elevated"
|
|
200
|
+
prepend-icon="fa-solid fa-dice"
|
|
201
|
+
class="text-caption"
|
|
202
|
+
>
|
|
203
|
+
{{ getNestedValue(item, '${systemPath}').number }}{{ getNestedValue(item, '${systemPath}').die }}
|
|
204
|
+
</v-chip>
|
|
205
|
+
</template>
|
|
206
206
|
`;
|
|
207
207
|
}
|
|
208
208
|
if (isMeasuredTemplateField(property)) {
|
|
209
|
-
return expandToNode `
|
|
210
|
-
<template v-if="isColumnVisible('${systemPath}')" v-slot:item.${slotName}="{ item }">
|
|
211
|
-
<v-chip
|
|
212
|
-
color="secondary"
|
|
213
|
-
size="x-small"
|
|
214
|
-
variant="elevated"
|
|
215
|
-
prepend-icon="fa-solid fa-ruler-combined"
|
|
216
|
-
class="text-caption"
|
|
217
|
-
label
|
|
218
|
-
>
|
|
219
|
-
{{ getNestedValue(item, '${systemPath}.summary') }}
|
|
220
|
-
</v-chip>
|
|
221
|
-
</template>
|
|
209
|
+
return expandToNode `
|
|
210
|
+
<template v-if="isColumnVisible('${systemPath}')" v-slot:item.${slotName}="{ item }">
|
|
211
|
+
<v-chip
|
|
212
|
+
color="secondary"
|
|
213
|
+
size="x-small"
|
|
214
|
+
variant="elevated"
|
|
215
|
+
prepend-icon="fa-solid fa-ruler-combined"
|
|
216
|
+
class="text-caption"
|
|
217
|
+
label
|
|
218
|
+
>
|
|
219
|
+
{{ getNestedValue(item, '${systemPath}.summary') }}
|
|
220
|
+
</v-chip>
|
|
221
|
+
</template>
|
|
222
222
|
`;
|
|
223
223
|
}
|
|
224
224
|
if (isTimeExp(property) || isDateExp(property) || isDateTimeExp(property)) {
|
|
225
|
-
return expandToNode `
|
|
226
|
-
<template v-if="isColumnVisible('${systemPath}')" v-slot:item.${slotName}="{ item }">
|
|
227
|
-
<span class="text-caption">{{ formatDate(getNestedValue(item, '${systemPath}')) }}</span>
|
|
228
|
-
</template>
|
|
225
|
+
return expandToNode `
|
|
226
|
+
<template v-if="isColumnVisible('${systemPath}')" v-slot:item.${slotName}="{ item }">
|
|
227
|
+
<span class="text-caption">{{ formatDate(getNestedValue(item, '${systemPath}')) }}</span>
|
|
228
|
+
</template>
|
|
229
229
|
`;
|
|
230
230
|
}
|
|
231
231
|
}
|
|
@@ -244,717 +244,717 @@ export function generateVuetifyDatatableComponent(entry, id, document, pageName,
|
|
|
244
244
|
const secondaryActions = actions.filter(action => action.isSecondary);
|
|
245
245
|
// Check if any PRIMARY actions have chat cards (exclude secondary actions)
|
|
246
246
|
const hasActionWithChat = primaryActions.some(action => action.method.body.some(expr => isChatCard(expr)));
|
|
247
|
-
const fileNode = expandToNode `
|
|
248
|
-
<script setup>
|
|
249
|
-
import { ref, computed, inject, onMounted, onUnmounted, watch, nextTick } from "vue";
|
|
250
|
-
|
|
251
|
-
const props = defineProps({
|
|
252
|
-
systemPath: String,
|
|
253
|
-
context: Object,
|
|
254
|
-
primaryColor: String,
|
|
255
|
-
secondaryColor: String,
|
|
256
|
-
tertiaryColor: String
|
|
257
|
-
});
|
|
258
|
-
|
|
259
|
-
const document = inject('rawDocument');
|
|
260
|
-
const search = ref('');
|
|
261
|
-
const loading = ref(false);
|
|
262
|
-
const showColumnDialog = ref(false);
|
|
263
|
-
|
|
264
|
-
const columnVisibility = ref({});
|
|
265
|
-
const columnOrder = ref([]);
|
|
266
|
-
|
|
267
|
-
const data = computed(() => {
|
|
268
|
-
// Table fields represent embedded items. Map over context.object.items (plain objects
|
|
269
|
-
// from toObject()) to keep the reactive dependency and avoid Vue's reactive proxy
|
|
270
|
-
// traversing Foundry's EmbeddedCollection. toObject() omits derived/computed values
|
|
271
|
-
// (their getters are non-enumerable), so we transiently read the live item and overlay
|
|
272
|
-
// each schema field's resolved value -- the live item is never stored in reactive state.
|
|
273
|
-
const allItems = props.context?.object?.items ?? [];
|
|
274
|
-
return allItems.filter(i => i.type === '${(_o = (_m = table.document.ref) === null || _m === void 0 ? void 0 : _m.name.toLowerCase()) !== null && _o !== void 0 ? _o : ''}').map(plain => {
|
|
275
|
-
const live = document.items.get(plain._id);
|
|
276
|
-
if (live) {
|
|
277
|
-
const fields = live.system.schema?.fields ?? live.system.constructor?.schema?.fields ?? {};
|
|
278
|
-
for (const key of Object.keys(fields)) {
|
|
279
|
-
plain.system[key] = foundry.utils.deepClone(live.system[key]);
|
|
280
|
-
}
|
|
281
|
-
}
|
|
282
|
-
return plain;
|
|
283
|
-
})${whereClause ? expandToNode `.filter(item => {
|
|
284
|
-
return ${whereClause};
|
|
285
|
-
})` : ''};
|
|
286
|
-
});
|
|
287
|
-
|
|
288
|
-
// Create a map of item _id to item for drag operations
|
|
289
|
-
const itemMap = computed(() => {
|
|
290
|
-
const map = new Map();
|
|
291
|
-
data.value.forEach(item => {
|
|
292
|
-
map.set(item._id, item);
|
|
293
|
-
});
|
|
294
|
-
return map;
|
|
295
|
-
});
|
|
296
|
-
|
|
297
|
-
// Expose itemMap globally for drag handlers to access
|
|
298
|
-
if (!window.isdlItemMaps) window.isdlItemMaps = new Map();
|
|
299
|
-
window.isdlItemMaps.set(document._id, itemMap);
|
|
300
|
-
|
|
301
|
-
const customHeaders = [
|
|
302
|
-
// Image and Name are configurable columns like any other. Image is ordered first by default.
|
|
303
|
-
{ title: game.i18n.localize("Image"), key: 'img', sortable: false, width: '50px', maxWidth: '50px' },
|
|
304
|
-
{ title: game.i18n.localize("Name"), key: 'name', sortable: true, minWidth: '120px', locked: true },
|
|
305
|
-
${joinToNode(table.document.ref.body, p => generateDataTableHeader(table.document, p), { appendNewLineIfNotEmpty: true })}
|
|
306
|
-
];
|
|
307
|
-
|
|
308
|
-
const orderedHeaders = computed(() => {
|
|
309
|
-
if (columnOrder.value.length === 0) {
|
|
310
|
-
return customHeaders;
|
|
311
|
-
}
|
|
312
|
-
|
|
313
|
-
// Create a map for quick lookup
|
|
314
|
-
const headerMap = new Map();
|
|
315
|
-
customHeaders.forEach(header => {
|
|
316
|
-
headerMap.set(header.key, header);
|
|
317
|
-
});
|
|
318
|
-
|
|
319
|
-
// Build ordered array based on columnOrder, then add any missing headers
|
|
320
|
-
const ordered = [];
|
|
321
|
-
columnOrder.value.forEach(key => {
|
|
322
|
-
if (headerMap.has(key)) {
|
|
323
|
-
ordered.push(headerMap.get(key));
|
|
324
|
-
headerMap.delete(key);
|
|
325
|
-
}
|
|
326
|
-
});
|
|
327
|
-
|
|
328
|
-
// Add any remaining headers that weren't in the order
|
|
329
|
-
headerMap.forEach(header => {
|
|
330
|
-
ordered.push(header);
|
|
331
|
-
});
|
|
332
|
-
|
|
333
|
-
return ordered;
|
|
334
|
-
});
|
|
335
|
-
|
|
336
|
-
// Per-viewer column gating from the referenced field's visibility.
|
|
337
|
-
// 'hidden' columns are already dropped at build time; method-block visibility can't be
|
|
338
|
-
// resolved here, so such columns fall through as visible.
|
|
339
|
-
const passesVisibility = (header) => {
|
|
340
|
-
const v = header?.visibility;
|
|
341
|
-
if (v === 'gmOnly') return game.user.isGM;
|
|
342
|
-
if (v === 'secret') {
|
|
343
|
-
return game.user.isGM || document.getUserLevel(game.user) === CONST.DOCUMENT_OWNERSHIP_LEVELS.OWNER;
|
|
344
|
-
}
|
|
345
|
-
return true;
|
|
346
|
-
};
|
|
347
|
-
|
|
348
|
-
const visibleHeaders = computed(() => {
|
|
349
|
-
const baseHeaders = [
|
|
350
|
-
{
|
|
351
|
-
title: "",
|
|
352
|
-
key: 'system.pinned',
|
|
353
|
-
sortable: false,
|
|
354
|
-
width: '40px',
|
|
355
|
-
maxWidth: '40px',
|
|
356
|
-
align: 'center'
|
|
357
|
-
}
|
|
358
|
-
];
|
|
359
|
-
|
|
360
|
-
let customHeadersToShow = orderedHeaders.value.filter(header => header && isColumnVisible(header.key) && passesVisibility(header));
|
|
361
|
-
|
|
362
|
-
const actionHeaders = [
|
|
363
|
-
{
|
|
364
|
-
title: game.i18n.localize("Actions"),
|
|
365
|
-
key: 'actions',
|
|
366
|
-
sortable: false,
|
|
367
|
-
width: '150px',
|
|
368
|
-
align: 'center'
|
|
369
|
-
}
|
|
370
|
-
];
|
|
371
|
-
|
|
372
|
-
return [...baseHeaders, ...customHeadersToShow, ...actionHeaders];
|
|
373
|
-
});
|
|
374
|
-
|
|
375
|
-
// Column configuration
|
|
376
|
-
const settingKey = 'documentTableColumns';
|
|
377
|
-
|
|
378
|
-
const defaultVisibileColumns = [
|
|
379
|
-
${joinToNode(defaultVisibleFields, p => expandToNode `'system.${p.toLowerCase()}'`, { separator: ",", appendNewLineIfNotEmpty: true })}
|
|
380
|
-
];
|
|
381
|
-
|
|
382
|
-
// Default visibility per column. Image defaults to the image table param (default true),
|
|
383
|
-
// Name is always visible (locked), and the rest follow the table's fields defaults.
|
|
384
|
-
const columnDefaultVisible = (key) => {
|
|
385
|
-
if (key === 'img') return ${imageDefaultVisible};
|
|
386
|
-
if (key === 'name') return true;
|
|
387
|
-
return defaultVisibileColumns.includes(key);
|
|
388
|
-
};
|
|
389
|
-
|
|
390
|
-
const buildDefaultVisibility = (tableSettings) => {
|
|
391
|
-
const defaultVisibility = {};
|
|
392
|
-
customHeaders.forEach(col => {
|
|
393
|
-
defaultVisibility[col.key] = columnDefaultVisible(col.key);
|
|
394
|
-
if (tableSettings && tableSettings.visibility && tableSettings.visibility[col.key] !== undefined) {
|
|
395
|
-
defaultVisibility[col.key] = tableSettings.visibility[col.key];
|
|
396
|
-
}
|
|
397
|
-
});
|
|
398
|
-
// Name can never be hidden.
|
|
399
|
-
defaultVisibility['name'] = true;
|
|
400
|
-
return defaultVisibility;
|
|
401
|
-
};
|
|
402
|
-
|
|
403
|
-
const initializeColumnSettings = async () => {
|
|
404
|
-
try {
|
|
405
|
-
const savedSettings = game.settings.get("${id}", settingKey) || {};
|
|
406
|
-
const documentTables = savedSettings[document._id] || {};
|
|
407
|
-
const tableSettings = documentTables['${pageName}${table.name}'] || {};
|
|
408
|
-
|
|
409
|
-
columnVisibility.value = buildDefaultVisibility(tableSettings);
|
|
410
|
-
|
|
411
|
-
// Initialize order
|
|
412
|
-
if (tableSettings.order && Array.isArray(tableSettings.order)) {
|
|
413
|
-
// Start from the saved order, then splice in any columns the save predates
|
|
414
|
-
// (e.g. img/name, or fields added to the document later) at their default index.
|
|
415
|
-
const order = [...tableSettings.order];
|
|
416
|
-
customHeaders.map(h => h.key).forEach((key, idx) => {
|
|
417
|
-
if (!order.includes(key)) {
|
|
418
|
-
order.splice(Math.min(idx, order.length), 0, key);
|
|
419
|
-
}
|
|
420
|
-
});
|
|
421
|
-
columnOrder.value = order;
|
|
422
|
-
} else {
|
|
423
|
-
// Default order is the order they appear in customHeaders
|
|
424
|
-
columnOrder.value = customHeaders.map(h => h.key);
|
|
425
|
-
}
|
|
426
|
-
} catch (error) {
|
|
427
|
-
console.warn("Failed to load column settings, using defaults:", error);
|
|
428
|
-
// Use defaults if setting doesn't exist yet
|
|
429
|
-
columnVisibility.value = buildDefaultVisibility(null);
|
|
430
|
-
columnOrder.value = customHeaders.map(h => h.key);
|
|
431
|
-
}
|
|
432
|
-
};
|
|
433
|
-
|
|
434
|
-
const saveColumnSettings = async () => {
|
|
435
|
-
try {
|
|
436
|
-
const savedSettings = game.settings.get("${id}", settingKey) || {};
|
|
437
|
-
const documentTables = savedSettings[document._id] || {};
|
|
438
|
-
savedSettings[document._id] = documentTables;
|
|
439
|
-
|
|
440
|
-
const tableSettings = documentTables['${pageName}${table.name}'] || {};
|
|
441
|
-
|
|
442
|
-
// Save visibility
|
|
443
|
-
const visibilitySettings = {};
|
|
444
|
-
customHeaders.forEach(col => {
|
|
445
|
-
visibilitySettings[col.key] = columnVisibility.value[col.key];
|
|
446
|
-
});
|
|
447
|
-
|
|
448
|
-
tableSettings.visibility = visibilitySettings;
|
|
449
|
-
tableSettings.order = [...columnOrder.value];
|
|
450
|
-
|
|
451
|
-
savedSettings[document._id]['${pageName}${table.name}'] = tableSettings;
|
|
452
|
-
await game.settings.set("${id}", settingKey, savedSettings);
|
|
453
|
-
} catch (error) {
|
|
454
|
-
console.error("Failed to save column settings:", error);
|
|
455
|
-
ui.notifications.error("Failed to save column settings");
|
|
456
|
-
}
|
|
457
|
-
};
|
|
458
|
-
|
|
459
|
-
const isColumnVisible = (columnKey) => {
|
|
460
|
-
return columnVisibility.value[columnKey] !== false;
|
|
461
|
-
};
|
|
462
|
-
|
|
463
|
-
const toggleColumn = async (columnKey) => {
|
|
464
|
-
// Name is the row identity column and can't be hidden.
|
|
465
|
-
if (columnKey === 'name') return;
|
|
466
|
-
columnVisibility.value[columnKey] = !columnVisibility.value[columnKey];
|
|
467
|
-
await saveColumnSettings();
|
|
468
|
-
};
|
|
469
|
-
|
|
470
|
-
const resetColumns = async () => {
|
|
471
|
-
columnVisibility.value = buildDefaultVisibility(null);
|
|
472
|
-
columnOrder.value = customHeaders.map(h => h.key);
|
|
473
|
-
await saveColumnSettings();
|
|
474
|
-
};
|
|
475
|
-
|
|
476
|
-
const moveColumn = async (fromIndex, toIndex) => {
|
|
477
|
-
const newOrder = [...columnOrder.value];
|
|
478
|
-
const [movedItem] = newOrder.splice(fromIndex, 1);
|
|
479
|
-
newOrder.splice(toIndex, 0, movedItem);
|
|
480
|
-
columnOrder.value = newOrder;
|
|
481
|
-
await saveColumnSettings();
|
|
482
|
-
};
|
|
483
|
-
|
|
484
|
-
const onColumnDragStart = (event, index) => {
|
|
485
|
-
event.dataTransfer.effectAllowed = 'move';
|
|
486
|
-
event.dataTransfer.setData('text/plain', index.toString());
|
|
487
|
-
};
|
|
488
|
-
|
|
489
|
-
const onColumnDragOver = (event) => {
|
|
490
|
-
event.preventDefault();
|
|
491
|
-
event.dataTransfer.dropEffect = 'move';
|
|
492
|
-
};
|
|
493
|
-
|
|
494
|
-
const onColumnDrop = async (event, toIndex) => {
|
|
495
|
-
event.preventDefault();
|
|
496
|
-
const fromIndex = parseInt(event.dataTransfer.getData('text/plain'));
|
|
497
|
-
if (fromIndex !== toIndex) {
|
|
498
|
-
await moveColumn(fromIndex, toIndex);
|
|
499
|
-
}
|
|
500
|
-
};
|
|
501
|
-
|
|
502
|
-
onMounted(() => {
|
|
503
|
-
initializeColumnSettings();
|
|
504
|
-
});
|
|
505
|
-
|
|
506
|
-
const humanize = (str) => {
|
|
507
|
-
if (!str) return "";
|
|
508
|
-
let humanized = str.replace(/_/g, " ");
|
|
509
|
-
humanized = humanized.replace("system.", "").replaceAll(".", " ");
|
|
510
|
-
humanized = humanized.charAt(0).toUpperCase() + humanized.slice(1);
|
|
511
|
-
return humanized;
|
|
512
|
-
};
|
|
513
|
-
|
|
514
|
-
const getNestedValue = (obj, path) => {
|
|
515
|
-
const data = foundry.utils.getProperty(obj, path);
|
|
516
|
-
return data;
|
|
517
|
-
};
|
|
518
|
-
|
|
519
|
-
const getResourceColor = (resource) => {
|
|
520
|
-
if (!resource || !resource.max) return 'grey';
|
|
521
|
-
const percentage = (resource.value / resource.max) * 100;
|
|
522
|
-
if (percentage > 75) return 'green';
|
|
523
|
-
if (percentage > 50) return 'orange';
|
|
524
|
-
if (percentage > 25) return 'red';
|
|
525
|
-
return 'red-darken-2';
|
|
526
|
-
};
|
|
527
|
-
|
|
528
|
-
const formatDate = (dateValue) => {
|
|
529
|
-
if (!dateValue) return "";
|
|
530
|
-
return new Date(dateValue).toLocaleDateString();
|
|
531
|
-
};
|
|
532
|
-
|
|
533
|
-
const editItem = (item) => {
|
|
534
|
-
const foundryItem = document.items.get(item._id);
|
|
535
|
-
foundryItem.sheet.render(true);
|
|
536
|
-
};
|
|
537
|
-
|
|
538
|
-
const sendItemToChat = async (item) => {
|
|
539
|
-
const foundryItem = document.items.get(item._id);
|
|
540
|
-
const chatDescription = foundryItem.description ?? foundryItem.system.description;
|
|
541
|
-
const content = await renderTemplate("systems/${id}/system/templates/chat/standard-card.hbs", {
|
|
542
|
-
cssClass: "${id}",
|
|
543
|
-
document: foundryItem,
|
|
544
|
-
hasEffects: foundryItem.effects?.size > 0,
|
|
545
|
-
description: chatDescription,
|
|
546
|
-
hasDescription: chatDescription != ""
|
|
547
|
-
});
|
|
548
|
-
ChatMessage.create({
|
|
549
|
-
content: content,
|
|
550
|
-
speaker: ChatMessage.getSpeaker(),
|
|
551
|
-
style: CONST.CHAT_MESSAGE_STYLES.IC
|
|
552
|
-
});
|
|
553
|
-
};
|
|
554
|
-
|
|
555
|
-
const deleteItem = async (item) => {
|
|
556
|
-
const foundryItem = document.items.get(item._id);
|
|
557
|
-
const shouldDelete = await Dialog.confirm({
|
|
558
|
-
title: "Delete Confirmation",
|
|
559
|
-
content: \`<p>Are you sure you would like to delete the "\${foundryItem.name}" Item?</p>\`,
|
|
560
|
-
defaultYes: false
|
|
561
|
-
});
|
|
562
|
-
if (shouldDelete) foundryItem.delete();
|
|
563
|
-
};
|
|
564
|
-
|
|
565
|
-
const duplicateItem = async (item) => {
|
|
566
|
-
loading.value = true;
|
|
567
|
-
try {
|
|
568
|
-
const foundryItem = document.items.get(item._id);
|
|
569
|
-
const itemData = foundryItem.toObject();
|
|
570
|
-
itemData.name = "Copy of " + itemData.name;
|
|
571
|
-
delete itemData._id;
|
|
572
|
-
|
|
573
|
-
const duplicatedItems = await Item.createDocuments([itemData], {parent: document});
|
|
574
|
-
if (duplicatedItems && duplicatedItems[0]) {
|
|
575
|
-
ui.notifications.info(\`Duplicated "\${foundryItem.name}"\`);
|
|
576
|
-
}
|
|
577
|
-
} catch (error) {
|
|
578
|
-
console.error("Error duplicating item:", error);
|
|
579
|
-
ui.notifications.error("Failed to duplicate item");
|
|
580
|
-
} finally {
|
|
581
|
-
loading.value = false;
|
|
582
|
-
}
|
|
583
|
-
};
|
|
584
|
-
|
|
585
|
-
const customItemAction = async (item, actionName) => {
|
|
586
|
-
const foundryItem = document.items.get(item._id);
|
|
587
|
-
const event = { currentTarget: { dataset: { action: actionName } } };
|
|
588
|
-
foundryItem.sheet._onAction(event);
|
|
589
|
-
};
|
|
590
|
-
|
|
591
|
-
const togglePin = async (item) => {
|
|
592
|
-
const foundryItem = document.items.get(item._id);
|
|
593
|
-
await foundryItem.update({"system.pinned": !foundryItem.system.pinned});
|
|
594
|
-
};
|
|
595
|
-
|
|
596
|
-
const addNewItem = async () => {
|
|
597
|
-
loading.value = true;
|
|
598
|
-
try {
|
|
599
|
-
const type = '${(_p = table.document.ref) === null || _p === void 0 ? void 0 : _p.name.toLowerCase()}';
|
|
600
|
-
const items = await Item.createDocuments([{
|
|
601
|
-
type: type,
|
|
602
|
-
name: "New " + type
|
|
603
|
-
}], {parent: document});
|
|
604
|
-
|
|
605
|
-
if (items && items[0]) {
|
|
606
|
-
items[0].sheet.render(true);
|
|
607
|
-
}
|
|
608
|
-
} catch (error) {
|
|
609
|
-
console.error("Error creating item:", error);
|
|
610
|
-
ui.notifications.error("Failed to create new item");
|
|
611
|
-
} finally {
|
|
612
|
-
loading.value = false;
|
|
613
|
-
}
|
|
614
|
-
};
|
|
615
|
-
|
|
616
|
-
const truncate = (text, maxLength) => {
|
|
617
|
-
if (!text) return '';
|
|
618
|
-
if (text.length > maxLength) {
|
|
619
|
-
return text.substring(0, maxLength) + '...';
|
|
620
|
-
}
|
|
621
|
-
return text;
|
|
622
|
-
}
|
|
623
|
-
|
|
624
|
-
const bindDragDrop = () => {
|
|
625
|
-
try {
|
|
626
|
-
if (document.sheet.element) {
|
|
627
|
-
document.sheet.dragDrop.forEach((d) => d.bind(document.sheet.element));
|
|
628
|
-
}
|
|
629
|
-
} catch (e) {
|
|
630
|
-
console.error(e);
|
|
631
|
-
}
|
|
632
|
-
};
|
|
633
|
-
|
|
634
|
-
const getExtendedChoiceTooltip = (item, systemPath) => {
|
|
635
|
-
const tooltipParts = [];
|
|
636
|
-
const coreKeys = ['value', 'color', 'icon'];
|
|
637
|
-
const base = getNestedValue(item, systemPath);
|
|
638
|
-
for (const key of Object.keys(base)) {
|
|
639
|
-
if (!coreKeys.includes(key)) {
|
|
640
|
-
const value = base[key];
|
|
641
|
-
if (value !== undefined) {
|
|
642
|
-
tooltipParts.push(\`\${key}: \${value}\`);
|
|
643
|
-
}
|
|
644
|
-
}
|
|
645
|
-
}
|
|
646
|
-
return tooltipParts.join('<br>');
|
|
647
|
-
};
|
|
648
|
-
|
|
649
|
-
// Get item props for row attributes (for drag-drop)
|
|
650
|
-
const getItemProps = (item) => {
|
|
651
|
-
// Construct UUID for embedded items: parent.uuid + Item.itemId
|
|
652
|
-
const itemUuid = item.uuid || \`\${document.uuid}.Item.\${item._id}\`;
|
|
653
|
-
return {
|
|
654
|
-
'data-item-id': item._id,
|
|
655
|
-
'data-document-id': document._id,
|
|
656
|
-
'data-uuid': itemUuid
|
|
657
|
-
};
|
|
658
|
-
};
|
|
659
|
-
|
|
660
|
-
// Function to add data attributes to table rows
|
|
661
|
-
const updateTableRowAttributes = () => {
|
|
662
|
-
const tableEl = window.document.querySelector('.custom-datatable table');
|
|
663
|
-
if (!tableEl) {
|
|
664
|
-
console.warn('Table not found for attribute update');
|
|
665
|
-
return;
|
|
666
|
-
}
|
|
667
|
-
|
|
668
|
-
const rows = tableEl.querySelectorAll('tbody tr');
|
|
669
|
-
const items = data.value;
|
|
670
|
-
|
|
671
|
-
rows.forEach((row, index) => {
|
|
672
|
-
if (index < items.length) {
|
|
673
|
-
const item = items[index];
|
|
674
|
-
const itemUuid = item.uuid || \`\${document.uuid}.Item.\${item._id}\`;
|
|
675
|
-
row.setAttribute('data-item-id', item._id);
|
|
676
|
-
row.setAttribute('data-document-id', document._id);
|
|
677
|
-
row.setAttribute('data-uuid', itemUuid);
|
|
678
|
-
}
|
|
679
|
-
});
|
|
680
|
-
};
|
|
681
|
-
|
|
682
|
-
// Watch for data changes and update attributes
|
|
683
|
-
watch(data, () => {
|
|
684
|
-
nextTick(updateTableRowAttributes);
|
|
685
|
-
}, { immediate: false });
|
|
686
|
-
|
|
687
|
-
// Bind drag drop and update attributes after component mount
|
|
688
|
-
onMounted(() => {
|
|
689
|
-
setTimeout(() => {
|
|
690
|
-
updateTableRowAttributes();
|
|
691
|
-
bindDragDrop();
|
|
692
|
-
}, 200);
|
|
693
|
-
});
|
|
694
|
-
|
|
695
|
-
// Clean up item map on unmount
|
|
696
|
-
onUnmounted(() => {
|
|
697
|
-
if (window.isdlItemMaps) {
|
|
698
|
-
window.isdlItemMaps.delete(document._id);
|
|
699
|
-
}
|
|
700
|
-
});
|
|
701
|
-
</script>
|
|
702
|
-
|
|
703
|
-
<template>
|
|
704
|
-
<v-card flat class="isdl-datatable">
|
|
705
|
-
<v-card-title class="d-flex align-center pe-1" style="height: 40px;">
|
|
706
|
-
<v-icon icon="fa-solid ${iconParam ? iconParam.value : 'fa-table'}" size="small" />
|
|
707
|
-
{{ game.i18n.localize("${document.name}.${table.name}") }}
|
|
708
|
-
<v-spacer></v-spacer>
|
|
709
|
-
<v-text-field
|
|
710
|
-
v-model="search"
|
|
711
|
-
density="compact"
|
|
712
|
-
label="Search"
|
|
713
|
-
prepend-inner-icon="fa-solid fa-magnify"
|
|
714
|
-
variant="outlined"
|
|
715
|
-
flat
|
|
716
|
-
hide-details
|
|
717
|
-
single-line
|
|
718
|
-
clearable
|
|
719
|
-
style="margin: 0; margin-right: 8px;"
|
|
720
|
-
></v-text-field>
|
|
721
|
-
<v-btn
|
|
722
|
-
icon="fa-solid fa-columns"
|
|
723
|
-
size="small"
|
|
724
|
-
variant="text"
|
|
725
|
-
@click="showColumnDialog = true"
|
|
726
|
-
style="margin-right: 8px;"
|
|
727
|
-
>
|
|
728
|
-
<v-icon>fa-solid fa-columns</v-icon>
|
|
729
|
-
<v-tooltip activator="parent" location="top">Configure Columns</v-tooltip>
|
|
730
|
-
</v-btn>
|
|
731
|
-
<v-btn
|
|
732
|
-
:color="primaryColor || 'primary'"
|
|
733
|
-
prepend-icon="fa-solid fa-plus"
|
|
734
|
-
rounded="0"
|
|
735
|
-
size="small"
|
|
736
|
-
:loading="loading"
|
|
737
|
-
@click="addNewItem"
|
|
738
|
-
style="max-width: 80px; height: 38px;"
|
|
739
|
-
>
|
|
740
|
-
{{ game.i18n.localize("Add") }}
|
|
741
|
-
</v-btn>
|
|
742
|
-
</v-card-title>
|
|
743
|
-
<v-divider></v-divider>
|
|
744
|
-
|
|
745
|
-
<v-data-table
|
|
746
|
-
v-model:search="search"
|
|
747
|
-
:headers="visibleHeaders"
|
|
748
|
-
:items="data"
|
|
749
|
-
:search="search"
|
|
750
|
-
hover
|
|
751
|
-
density="compact"
|
|
752
|
-
hide-default-footer
|
|
753
|
-
items-per-page=-1
|
|
754
|
-
style="background: none;"
|
|
755
|
-
class="custom-datatable"
|
|
756
|
-
:sort-by="[{ key: 'system.pinned', order: 'desc' }, { key: 'name', order: 'asc' }]"
|
|
757
|
-
:item-props="getItemProps"
|
|
758
|
-
>
|
|
759
|
-
<!-- Image slot -->
|
|
760
|
-
<template v-slot:item.img="{ item }">
|
|
247
|
+
const fileNode = expandToNode `
|
|
248
|
+
<script setup>
|
|
249
|
+
import { ref, computed, inject, onMounted, onUnmounted, watch, nextTick } from "vue";
|
|
250
|
+
|
|
251
|
+
const props = defineProps({
|
|
252
|
+
systemPath: String,
|
|
253
|
+
context: Object,
|
|
254
|
+
primaryColor: String,
|
|
255
|
+
secondaryColor: String,
|
|
256
|
+
tertiaryColor: String
|
|
257
|
+
});
|
|
258
|
+
|
|
259
|
+
const document = inject('rawDocument');
|
|
260
|
+
const search = ref('');
|
|
261
|
+
const loading = ref(false);
|
|
262
|
+
const showColumnDialog = ref(false);
|
|
263
|
+
|
|
264
|
+
const columnVisibility = ref({});
|
|
265
|
+
const columnOrder = ref([]);
|
|
266
|
+
|
|
267
|
+
const data = computed(() => {
|
|
268
|
+
// Table fields represent embedded items. Map over context.object.items (plain objects
|
|
269
|
+
// from toObject()) to keep the reactive dependency and avoid Vue's reactive proxy
|
|
270
|
+
// traversing Foundry's EmbeddedCollection. toObject() omits derived/computed values
|
|
271
|
+
// (their getters are non-enumerable), so we transiently read the live item and overlay
|
|
272
|
+
// each schema field's resolved value -- the live item is never stored in reactive state.
|
|
273
|
+
const allItems = props.context?.object?.items ?? [];
|
|
274
|
+
return allItems.filter(i => i.type === '${(_o = (_m = table.document.ref) === null || _m === void 0 ? void 0 : _m.name.toLowerCase()) !== null && _o !== void 0 ? _o : ''}').map(plain => {
|
|
275
|
+
const live = document.items.get(plain._id);
|
|
276
|
+
if (live) {
|
|
277
|
+
const fields = live.system.schema?.fields ?? live.system.constructor?.schema?.fields ?? {};
|
|
278
|
+
for (const key of Object.keys(fields)) {
|
|
279
|
+
plain.system[key] = foundry.utils.deepClone(live.system[key]);
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
return plain;
|
|
283
|
+
})${whereClause ? expandToNode `.filter(item => {
|
|
284
|
+
return ${whereClause};
|
|
285
|
+
})` : ''};
|
|
286
|
+
});
|
|
287
|
+
|
|
288
|
+
// Create a map of item _id to item for drag operations
|
|
289
|
+
const itemMap = computed(() => {
|
|
290
|
+
const map = new Map();
|
|
291
|
+
data.value.forEach(item => {
|
|
292
|
+
map.set(item._id, item);
|
|
293
|
+
});
|
|
294
|
+
return map;
|
|
295
|
+
});
|
|
296
|
+
|
|
297
|
+
// Expose itemMap globally for drag handlers to access
|
|
298
|
+
if (!window.isdlItemMaps) window.isdlItemMaps = new Map();
|
|
299
|
+
window.isdlItemMaps.set(document._id, itemMap);
|
|
300
|
+
|
|
301
|
+
const customHeaders = [
|
|
302
|
+
// Image and Name are configurable columns like any other. Image is ordered first by default.
|
|
303
|
+
{ title: game.i18n.localize("Image"), key: 'img', sortable: false, width: '50px', maxWidth: '50px' },
|
|
304
|
+
{ title: game.i18n.localize("Name"), key: 'name', sortable: true, minWidth: '120px', locked: true },
|
|
305
|
+
${joinToNode(table.document.ref.body, p => generateDataTableHeader(table.document, p), { appendNewLineIfNotEmpty: true })}
|
|
306
|
+
];
|
|
307
|
+
|
|
308
|
+
const orderedHeaders = computed(() => {
|
|
309
|
+
if (columnOrder.value.length === 0) {
|
|
310
|
+
return customHeaders;
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
// Create a map for quick lookup
|
|
314
|
+
const headerMap = new Map();
|
|
315
|
+
customHeaders.forEach(header => {
|
|
316
|
+
headerMap.set(header.key, header);
|
|
317
|
+
});
|
|
318
|
+
|
|
319
|
+
// Build ordered array based on columnOrder, then add any missing headers
|
|
320
|
+
const ordered = [];
|
|
321
|
+
columnOrder.value.forEach(key => {
|
|
322
|
+
if (headerMap.has(key)) {
|
|
323
|
+
ordered.push(headerMap.get(key));
|
|
324
|
+
headerMap.delete(key);
|
|
325
|
+
}
|
|
326
|
+
});
|
|
327
|
+
|
|
328
|
+
// Add any remaining headers that weren't in the order
|
|
329
|
+
headerMap.forEach(header => {
|
|
330
|
+
ordered.push(header);
|
|
331
|
+
});
|
|
332
|
+
|
|
333
|
+
return ordered;
|
|
334
|
+
});
|
|
335
|
+
|
|
336
|
+
// Per-viewer column gating from the referenced field's visibility.
|
|
337
|
+
// 'hidden' columns are already dropped at build time; method-block visibility can't be
|
|
338
|
+
// resolved here, so such columns fall through as visible.
|
|
339
|
+
const passesVisibility = (header) => {
|
|
340
|
+
const v = header?.visibility;
|
|
341
|
+
if (v === 'gmOnly') return game.user.isGM;
|
|
342
|
+
if (v === 'secret') {
|
|
343
|
+
return game.user.isGM || document.getUserLevel(game.user) === CONST.DOCUMENT_OWNERSHIP_LEVELS.OWNER;
|
|
344
|
+
}
|
|
345
|
+
return true;
|
|
346
|
+
};
|
|
347
|
+
|
|
348
|
+
const visibleHeaders = computed(() => {
|
|
349
|
+
const baseHeaders = [
|
|
350
|
+
{
|
|
351
|
+
title: "",
|
|
352
|
+
key: 'system.pinned',
|
|
353
|
+
sortable: false,
|
|
354
|
+
width: '40px',
|
|
355
|
+
maxWidth: '40px',
|
|
356
|
+
align: 'center'
|
|
357
|
+
}
|
|
358
|
+
];
|
|
359
|
+
|
|
360
|
+
let customHeadersToShow = orderedHeaders.value.filter(header => header && isColumnVisible(header.key) && passesVisibility(header));
|
|
361
|
+
|
|
362
|
+
const actionHeaders = [
|
|
363
|
+
{
|
|
364
|
+
title: game.i18n.localize("Actions"),
|
|
365
|
+
key: 'actions',
|
|
366
|
+
sortable: false,
|
|
367
|
+
width: '150px',
|
|
368
|
+
align: 'center'
|
|
369
|
+
}
|
|
370
|
+
];
|
|
371
|
+
|
|
372
|
+
return [...baseHeaders, ...customHeadersToShow, ...actionHeaders];
|
|
373
|
+
});
|
|
374
|
+
|
|
375
|
+
// Column configuration
|
|
376
|
+
const settingKey = 'documentTableColumns';
|
|
377
|
+
|
|
378
|
+
const defaultVisibileColumns = [
|
|
379
|
+
${joinToNode(defaultVisibleFields, p => expandToNode `'system.${p.toLowerCase()}'`, { separator: ",", appendNewLineIfNotEmpty: true })}
|
|
380
|
+
];
|
|
381
|
+
|
|
382
|
+
// Default visibility per column. Image defaults to the image table param (default true),
|
|
383
|
+
// Name is always visible (locked), and the rest follow the table's fields defaults.
|
|
384
|
+
const columnDefaultVisible = (key) => {
|
|
385
|
+
if (key === 'img') return ${imageDefaultVisible};
|
|
386
|
+
if (key === 'name') return true;
|
|
387
|
+
return defaultVisibileColumns.includes(key);
|
|
388
|
+
};
|
|
389
|
+
|
|
390
|
+
const buildDefaultVisibility = (tableSettings) => {
|
|
391
|
+
const defaultVisibility = {};
|
|
392
|
+
customHeaders.forEach(col => {
|
|
393
|
+
defaultVisibility[col.key] = columnDefaultVisible(col.key);
|
|
394
|
+
if (tableSettings && tableSettings.visibility && tableSettings.visibility[col.key] !== undefined) {
|
|
395
|
+
defaultVisibility[col.key] = tableSettings.visibility[col.key];
|
|
396
|
+
}
|
|
397
|
+
});
|
|
398
|
+
// Name can never be hidden.
|
|
399
|
+
defaultVisibility['name'] = true;
|
|
400
|
+
return defaultVisibility;
|
|
401
|
+
};
|
|
402
|
+
|
|
403
|
+
const initializeColumnSettings = async () => {
|
|
404
|
+
try {
|
|
405
|
+
const savedSettings = game.settings.get("${id}", settingKey) || {};
|
|
406
|
+
const documentTables = savedSettings[document._id] || {};
|
|
407
|
+
const tableSettings = documentTables['${pageName}${table.name}'] || {};
|
|
408
|
+
|
|
409
|
+
columnVisibility.value = buildDefaultVisibility(tableSettings);
|
|
410
|
+
|
|
411
|
+
// Initialize order
|
|
412
|
+
if (tableSettings.order && Array.isArray(tableSettings.order)) {
|
|
413
|
+
// Start from the saved order, then splice in any columns the save predates
|
|
414
|
+
// (e.g. img/name, or fields added to the document later) at their default index.
|
|
415
|
+
const order = [...tableSettings.order];
|
|
416
|
+
customHeaders.map(h => h.key).forEach((key, idx) => {
|
|
417
|
+
if (!order.includes(key)) {
|
|
418
|
+
order.splice(Math.min(idx, order.length), 0, key);
|
|
419
|
+
}
|
|
420
|
+
});
|
|
421
|
+
columnOrder.value = order;
|
|
422
|
+
} else {
|
|
423
|
+
// Default order is the order they appear in customHeaders
|
|
424
|
+
columnOrder.value = customHeaders.map(h => h.key);
|
|
425
|
+
}
|
|
426
|
+
} catch (error) {
|
|
427
|
+
console.warn("Failed to load column settings, using defaults:", error);
|
|
428
|
+
// Use defaults if setting doesn't exist yet
|
|
429
|
+
columnVisibility.value = buildDefaultVisibility(null);
|
|
430
|
+
columnOrder.value = customHeaders.map(h => h.key);
|
|
431
|
+
}
|
|
432
|
+
};
|
|
433
|
+
|
|
434
|
+
const saveColumnSettings = async () => {
|
|
435
|
+
try {
|
|
436
|
+
const savedSettings = game.settings.get("${id}", settingKey) || {};
|
|
437
|
+
const documentTables = savedSettings[document._id] || {};
|
|
438
|
+
savedSettings[document._id] = documentTables;
|
|
439
|
+
|
|
440
|
+
const tableSettings = documentTables['${pageName}${table.name}'] || {};
|
|
441
|
+
|
|
442
|
+
// Save visibility
|
|
443
|
+
const visibilitySettings = {};
|
|
444
|
+
customHeaders.forEach(col => {
|
|
445
|
+
visibilitySettings[col.key] = columnVisibility.value[col.key];
|
|
446
|
+
});
|
|
447
|
+
|
|
448
|
+
tableSettings.visibility = visibilitySettings;
|
|
449
|
+
tableSettings.order = [...columnOrder.value];
|
|
450
|
+
|
|
451
|
+
savedSettings[document._id]['${pageName}${table.name}'] = tableSettings;
|
|
452
|
+
await game.settings.set("${id}", settingKey, savedSettings);
|
|
453
|
+
} catch (error) {
|
|
454
|
+
console.error("Failed to save column settings:", error);
|
|
455
|
+
ui.notifications.error("Failed to save column settings");
|
|
456
|
+
}
|
|
457
|
+
};
|
|
458
|
+
|
|
459
|
+
const isColumnVisible = (columnKey) => {
|
|
460
|
+
return columnVisibility.value[columnKey] !== false;
|
|
461
|
+
};
|
|
462
|
+
|
|
463
|
+
const toggleColumn = async (columnKey) => {
|
|
464
|
+
// Name is the row identity column and can't be hidden.
|
|
465
|
+
if (columnKey === 'name') return;
|
|
466
|
+
columnVisibility.value[columnKey] = !columnVisibility.value[columnKey];
|
|
467
|
+
await saveColumnSettings();
|
|
468
|
+
};
|
|
469
|
+
|
|
470
|
+
const resetColumns = async () => {
|
|
471
|
+
columnVisibility.value = buildDefaultVisibility(null);
|
|
472
|
+
columnOrder.value = customHeaders.map(h => h.key);
|
|
473
|
+
await saveColumnSettings();
|
|
474
|
+
};
|
|
475
|
+
|
|
476
|
+
const moveColumn = async (fromIndex, toIndex) => {
|
|
477
|
+
const newOrder = [...columnOrder.value];
|
|
478
|
+
const [movedItem] = newOrder.splice(fromIndex, 1);
|
|
479
|
+
newOrder.splice(toIndex, 0, movedItem);
|
|
480
|
+
columnOrder.value = newOrder;
|
|
481
|
+
await saveColumnSettings();
|
|
482
|
+
};
|
|
483
|
+
|
|
484
|
+
const onColumnDragStart = (event, index) => {
|
|
485
|
+
event.dataTransfer.effectAllowed = 'move';
|
|
486
|
+
event.dataTransfer.setData('text/plain', index.toString());
|
|
487
|
+
};
|
|
488
|
+
|
|
489
|
+
const onColumnDragOver = (event) => {
|
|
490
|
+
event.preventDefault();
|
|
491
|
+
event.dataTransfer.dropEffect = 'move';
|
|
492
|
+
};
|
|
493
|
+
|
|
494
|
+
const onColumnDrop = async (event, toIndex) => {
|
|
495
|
+
event.preventDefault();
|
|
496
|
+
const fromIndex = parseInt(event.dataTransfer.getData('text/plain'));
|
|
497
|
+
if (fromIndex !== toIndex) {
|
|
498
|
+
await moveColumn(fromIndex, toIndex);
|
|
499
|
+
}
|
|
500
|
+
};
|
|
501
|
+
|
|
502
|
+
onMounted(() => {
|
|
503
|
+
initializeColumnSettings();
|
|
504
|
+
});
|
|
505
|
+
|
|
506
|
+
const humanize = (str) => {
|
|
507
|
+
if (!str) return "";
|
|
508
|
+
let humanized = str.replace(/_/g, " ");
|
|
509
|
+
humanized = humanized.replace("system.", "").replaceAll(".", " ");
|
|
510
|
+
humanized = humanized.charAt(0).toUpperCase() + humanized.slice(1);
|
|
511
|
+
return humanized;
|
|
512
|
+
};
|
|
513
|
+
|
|
514
|
+
const getNestedValue = (obj, path) => {
|
|
515
|
+
const data = foundry.utils.getProperty(obj, path);
|
|
516
|
+
return data;
|
|
517
|
+
};
|
|
518
|
+
|
|
519
|
+
const getResourceColor = (resource) => {
|
|
520
|
+
if (!resource || !resource.max) return 'grey';
|
|
521
|
+
const percentage = (resource.value / resource.max) * 100;
|
|
522
|
+
if (percentage > 75) return 'green';
|
|
523
|
+
if (percentage > 50) return 'orange';
|
|
524
|
+
if (percentage > 25) return 'red';
|
|
525
|
+
return 'red-darken-2';
|
|
526
|
+
};
|
|
527
|
+
|
|
528
|
+
const formatDate = (dateValue) => {
|
|
529
|
+
if (!dateValue) return "";
|
|
530
|
+
return new Date(dateValue).toLocaleDateString();
|
|
531
|
+
};
|
|
532
|
+
|
|
533
|
+
const editItem = (item) => {
|
|
534
|
+
const foundryItem = document.items.get(item._id);
|
|
535
|
+
foundryItem.sheet.render(true);
|
|
536
|
+
};
|
|
537
|
+
|
|
538
|
+
const sendItemToChat = async (item) => {
|
|
539
|
+
const foundryItem = document.items.get(item._id);
|
|
540
|
+
const chatDescription = foundryItem.description ?? foundryItem.system.description;
|
|
541
|
+
const content = await renderTemplate("systems/${id}/system/templates/chat/standard-card.hbs", {
|
|
542
|
+
cssClass: "${id}",
|
|
543
|
+
document: foundryItem,
|
|
544
|
+
hasEffects: foundryItem.effects?.size > 0,
|
|
545
|
+
description: chatDescription,
|
|
546
|
+
hasDescription: chatDescription != ""
|
|
547
|
+
});
|
|
548
|
+
ChatMessage.create({
|
|
549
|
+
content: content,
|
|
550
|
+
speaker: ChatMessage.getSpeaker(),
|
|
551
|
+
style: CONST.CHAT_MESSAGE_STYLES.IC
|
|
552
|
+
});
|
|
553
|
+
};
|
|
554
|
+
|
|
555
|
+
const deleteItem = async (item) => {
|
|
556
|
+
const foundryItem = document.items.get(item._id);
|
|
557
|
+
const shouldDelete = await Dialog.confirm({
|
|
558
|
+
title: "Delete Confirmation",
|
|
559
|
+
content: \`<p>Are you sure you would like to delete the "\${foundryItem.name}" Item?</p>\`,
|
|
560
|
+
defaultYes: false
|
|
561
|
+
});
|
|
562
|
+
if (shouldDelete) foundryItem.delete();
|
|
563
|
+
};
|
|
564
|
+
|
|
565
|
+
const duplicateItem = async (item) => {
|
|
566
|
+
loading.value = true;
|
|
567
|
+
try {
|
|
568
|
+
const foundryItem = document.items.get(item._id);
|
|
569
|
+
const itemData = foundryItem.toObject();
|
|
570
|
+
itemData.name = "Copy of " + itemData.name;
|
|
571
|
+
delete itemData._id;
|
|
572
|
+
|
|
573
|
+
const duplicatedItems = await Item.createDocuments([itemData], {parent: document});
|
|
574
|
+
if (duplicatedItems && duplicatedItems[0]) {
|
|
575
|
+
ui.notifications.info(\`Duplicated "\${foundryItem.name}"\`);
|
|
576
|
+
}
|
|
577
|
+
} catch (error) {
|
|
578
|
+
console.error("Error duplicating item:", error);
|
|
579
|
+
ui.notifications.error("Failed to duplicate item");
|
|
580
|
+
} finally {
|
|
581
|
+
loading.value = false;
|
|
582
|
+
}
|
|
583
|
+
};
|
|
584
|
+
|
|
585
|
+
const customItemAction = async (item, actionName) => {
|
|
586
|
+
const foundryItem = document.items.get(item._id);
|
|
587
|
+
const event = { currentTarget: { dataset: { action: actionName } } };
|
|
588
|
+
foundryItem.sheet._onAction(event);
|
|
589
|
+
};
|
|
590
|
+
|
|
591
|
+
const togglePin = async (item) => {
|
|
592
|
+
const foundryItem = document.items.get(item._id);
|
|
593
|
+
await foundryItem.update({"system.pinned": !foundryItem.system.pinned});
|
|
594
|
+
};
|
|
595
|
+
|
|
596
|
+
const addNewItem = async () => {
|
|
597
|
+
loading.value = true;
|
|
598
|
+
try {
|
|
599
|
+
const type = '${(_p = table.document.ref) === null || _p === void 0 ? void 0 : _p.name.toLowerCase()}';
|
|
600
|
+
const items = await Item.createDocuments([{
|
|
601
|
+
type: type,
|
|
602
|
+
name: "New " + type
|
|
603
|
+
}], {parent: document});
|
|
604
|
+
|
|
605
|
+
if (items && items[0]) {
|
|
606
|
+
items[0].sheet.render(true);
|
|
607
|
+
}
|
|
608
|
+
} catch (error) {
|
|
609
|
+
console.error("Error creating item:", error);
|
|
610
|
+
ui.notifications.error("Failed to create new item");
|
|
611
|
+
} finally {
|
|
612
|
+
loading.value = false;
|
|
613
|
+
}
|
|
614
|
+
};
|
|
615
|
+
|
|
616
|
+
const truncate = (text, maxLength) => {
|
|
617
|
+
if (!text) return '';
|
|
618
|
+
if (text.length > maxLength) {
|
|
619
|
+
return text.substring(0, maxLength) + '...';
|
|
620
|
+
}
|
|
621
|
+
return text;
|
|
622
|
+
}
|
|
623
|
+
|
|
624
|
+
const bindDragDrop = () => {
|
|
625
|
+
try {
|
|
626
|
+
if (document.sheet.element) {
|
|
627
|
+
document.sheet.dragDrop.forEach((d) => d.bind(document.sheet.element));
|
|
628
|
+
}
|
|
629
|
+
} catch (e) {
|
|
630
|
+
console.error(e);
|
|
631
|
+
}
|
|
632
|
+
};
|
|
633
|
+
|
|
634
|
+
const getExtendedChoiceTooltip = (item, systemPath) => {
|
|
635
|
+
const tooltipParts = [];
|
|
636
|
+
const coreKeys = ['value', 'color', 'icon'];
|
|
637
|
+
const base = getNestedValue(item, systemPath);
|
|
638
|
+
for (const key of Object.keys(base)) {
|
|
639
|
+
if (!coreKeys.includes(key)) {
|
|
640
|
+
const value = base[key];
|
|
641
|
+
if (value !== undefined) {
|
|
642
|
+
tooltipParts.push(\`\${key}: \${value}\`);
|
|
643
|
+
}
|
|
644
|
+
}
|
|
645
|
+
}
|
|
646
|
+
return tooltipParts.join('<br>');
|
|
647
|
+
};
|
|
648
|
+
|
|
649
|
+
// Get item props for row attributes (for drag-drop)
|
|
650
|
+
const getItemProps = (item) => {
|
|
651
|
+
// Construct UUID for embedded items: parent.uuid + Item.itemId
|
|
652
|
+
const itemUuid = item.uuid || \`\${document.uuid}.Item.\${item._id}\`;
|
|
653
|
+
return {
|
|
654
|
+
'data-item-id': item._id,
|
|
655
|
+
'data-document-id': document._id,
|
|
656
|
+
'data-uuid': itemUuid
|
|
657
|
+
};
|
|
658
|
+
};
|
|
659
|
+
|
|
660
|
+
// Function to add data attributes to table rows
|
|
661
|
+
const updateTableRowAttributes = () => {
|
|
662
|
+
const tableEl = window.document.querySelector('.custom-datatable table');
|
|
663
|
+
if (!tableEl) {
|
|
664
|
+
console.warn('Table not found for attribute update');
|
|
665
|
+
return;
|
|
666
|
+
}
|
|
667
|
+
|
|
668
|
+
const rows = tableEl.querySelectorAll('tbody tr');
|
|
669
|
+
const items = data.value;
|
|
670
|
+
|
|
671
|
+
rows.forEach((row, index) => {
|
|
672
|
+
if (index < items.length) {
|
|
673
|
+
const item = items[index];
|
|
674
|
+
const itemUuid = item.uuid || \`\${document.uuid}.Item.\${item._id}\`;
|
|
675
|
+
row.setAttribute('data-item-id', item._id);
|
|
676
|
+
row.setAttribute('data-document-id', document._id);
|
|
677
|
+
row.setAttribute('data-uuid', itemUuid);
|
|
678
|
+
}
|
|
679
|
+
});
|
|
680
|
+
};
|
|
681
|
+
|
|
682
|
+
// Watch for data changes and update attributes
|
|
683
|
+
watch(data, () => {
|
|
684
|
+
nextTick(updateTableRowAttributes);
|
|
685
|
+
}, { immediate: false });
|
|
686
|
+
|
|
687
|
+
// Bind drag drop and update attributes after component mount
|
|
688
|
+
onMounted(() => {
|
|
689
|
+
setTimeout(() => {
|
|
690
|
+
updateTableRowAttributes();
|
|
691
|
+
bindDragDrop();
|
|
692
|
+
}, 200);
|
|
693
|
+
});
|
|
694
|
+
|
|
695
|
+
// Clean up item map on unmount
|
|
696
|
+
onUnmounted(() => {
|
|
697
|
+
if (window.isdlItemMaps) {
|
|
698
|
+
window.isdlItemMaps.delete(document._id);
|
|
699
|
+
}
|
|
700
|
+
});
|
|
701
|
+
</script>
|
|
702
|
+
|
|
703
|
+
<template>
|
|
704
|
+
<v-card flat class="isdl-datatable">
|
|
705
|
+
<v-card-title class="d-flex align-center pe-1" style="height: 40px;">
|
|
706
|
+
<v-icon icon="fa-solid ${iconParam ? iconParam.value : 'fa-table'}" size="small" />
|
|
707
|
+
{{ game.i18n.localize("${document.name}.${table.name}") }}
|
|
708
|
+
<v-spacer></v-spacer>
|
|
709
|
+
<v-text-field
|
|
710
|
+
v-model="search"
|
|
711
|
+
density="compact"
|
|
712
|
+
label="Search"
|
|
713
|
+
prepend-inner-icon="fa-solid fa-magnify"
|
|
714
|
+
variant="outlined"
|
|
715
|
+
flat
|
|
716
|
+
hide-details
|
|
717
|
+
single-line
|
|
718
|
+
clearable
|
|
719
|
+
style="margin: 0; margin-right: 8px;"
|
|
720
|
+
></v-text-field>
|
|
721
|
+
<v-btn
|
|
722
|
+
icon="fa-solid fa-columns"
|
|
723
|
+
size="small"
|
|
724
|
+
variant="text"
|
|
725
|
+
@click="showColumnDialog = true"
|
|
726
|
+
style="margin-right: 8px;"
|
|
727
|
+
>
|
|
728
|
+
<v-icon>fa-solid fa-columns</v-icon>
|
|
729
|
+
<v-tooltip activator="parent" location="top">Configure Columns</v-tooltip>
|
|
730
|
+
</v-btn>
|
|
731
|
+
<v-btn
|
|
732
|
+
:color="primaryColor || 'primary'"
|
|
733
|
+
prepend-icon="fa-solid fa-plus"
|
|
734
|
+
rounded="0"
|
|
735
|
+
size="small"
|
|
736
|
+
:loading="loading"
|
|
737
|
+
@click="addNewItem"
|
|
738
|
+
style="max-width: 80px; height: 38px;"
|
|
739
|
+
>
|
|
740
|
+
{{ game.i18n.localize("Add") }}
|
|
741
|
+
</v-btn>
|
|
742
|
+
</v-card-title>
|
|
743
|
+
<v-divider></v-divider>
|
|
744
|
+
|
|
745
|
+
<v-data-table
|
|
746
|
+
v-model:search="search"
|
|
747
|
+
:headers="visibleHeaders"
|
|
748
|
+
:items="data"
|
|
749
|
+
:search="search"
|
|
750
|
+
hover
|
|
751
|
+
density="compact"
|
|
752
|
+
hide-default-footer
|
|
753
|
+
items-per-page=-1
|
|
754
|
+
style="background: none;"
|
|
755
|
+
class="custom-datatable"
|
|
756
|
+
:sort-by="[{ key: 'system.pinned', order: 'desc' }, { key: 'name', order: 'asc' }]"
|
|
757
|
+
:item-props="getItemProps"
|
|
758
|
+
>
|
|
759
|
+
<!-- Image slot -->
|
|
760
|
+
<template v-slot:item.img="{ item }">
|
|
761
761
|
${imageActionName
|
|
762
|
-
? expandToNode `<div class="isdl-image-action" @click.stop="customItemAction(item, '${imageActionName}')" :data-tooltip="game.i18n.localize('${(_q = table.document.ref) === null || _q === void 0 ? void 0 : _q.name}.${imageActionParam.action.ref.name}')">
|
|
763
|
-
<v-avatar size="40" rounded="0">
|
|
764
|
-
<v-img :src="item.img" :alt="item.name" cover></v-img>
|
|
765
|
-
</v-avatar>
|
|
766
|
-
<div class="isdl-image-action-overlay">
|
|
767
|
-
<v-btn icon size="x-small" color="${imageActionColor}" variant="elevated" elevation="4" class="isdl-image-action-btn">
|
|
768
|
-
<v-icon size="small">${imageActionIcon}</v-icon>
|
|
769
|
-
</v-btn>
|
|
770
|
-
</div>
|
|
762
|
+
? expandToNode `<div class="isdl-image-action" @click.stop="customItemAction(item, '${imageActionName}')" :data-tooltip="game.i18n.localize('${(_q = table.document.ref) === null || _q === void 0 ? void 0 : _q.name}.${imageActionParam.action.ref.name}')">
|
|
763
|
+
<v-avatar size="40" rounded="0">
|
|
764
|
+
<v-img :src="item.img" :alt="item.name" cover></v-img>
|
|
765
|
+
</v-avatar>
|
|
766
|
+
<div class="isdl-image-action-overlay">
|
|
767
|
+
<v-btn icon size="x-small" color="${imageActionColor}" variant="elevated" elevation="4" class="isdl-image-action-btn">
|
|
768
|
+
<v-icon size="small">${imageActionIcon}</v-icon>
|
|
769
|
+
</v-btn>
|
|
770
|
+
</div>
|
|
771
771
|
</div>`
|
|
772
|
-
: expandToNode `<v-avatar size="40" rounded="0">
|
|
773
|
-
<v-img :src="item.img" :alt="item.name" cover></v-img>
|
|
774
|
-
</v-avatar>`}
|
|
775
|
-
</template>
|
|
776
|
-
|
|
777
|
-
<!-- Name slot with description tooltip -->
|
|
778
|
-
<template v-slot:item.name="{ item }">
|
|
779
|
-
<div class="d-flex align-center" :data-tooltip="item.system.description">
|
|
780
|
-
<div class="font-weight-medium text-truncate" style="min-width: 120px; max-width: 200px;">{{ item.name }}</div>
|
|
781
|
-
</div>
|
|
782
|
-
</template>
|
|
783
|
-
|
|
784
|
-
<!-- Pinned slot -->
|
|
785
|
-
<template v-slot:item.system.pinned="{ item }">
|
|
786
|
-
<div class="d-flex justify-center">
|
|
787
|
-
<v-btn
|
|
788
|
-
icon
|
|
789
|
-
size="small"
|
|
790
|
-
variant="text"
|
|
791
|
-
@click="togglePin(item)"
|
|
792
|
-
:data-tooltip="item.system.pinned ? 'Unpin' : 'Pin'"
|
|
793
|
-
>
|
|
794
|
-
<v-icon
|
|
795
|
-
:icon="item.system.pinned ? 'fa-solid fa-thumbtack' : 'fa-regular fa-thumbtack'"
|
|
796
|
-
:color="item.system.pinned ? primaryColor : 'grey'"
|
|
797
|
-
size="small"
|
|
798
|
-
></v-icon>
|
|
799
|
-
</v-btn>
|
|
800
|
-
</div>
|
|
801
|
-
</template>
|
|
802
|
-
|
|
803
|
-
<!-- Custom field slots -->
|
|
804
|
-
${joinToNode(table.document.ref.body, p => generateSlotTemplate(table.document, p), { appendNewLineIfNotEmpty: true })}
|
|
805
|
-
|
|
806
|
-
<!-- Actions slot -->
|
|
807
|
-
<template v-slot:item.actions="{ item }">
|
|
808
|
-
<div class="d-flex align-center justify-center ga-1">
|
|
809
|
-
${joinToNode(primaryActions, generateActionButton, { appendNewLineIfNotEmpty: true })}
|
|
810
|
-
<v-tooltip text="Edit">
|
|
811
|
-
<template v-slot:activator="{ props }">
|
|
812
|
-
<v-btn
|
|
813
|
-
v-bind="props"
|
|
814
|
-
icon="fa-solid fa-edit"
|
|
815
|
-
size="x-small"
|
|
816
|
-
variant="text"
|
|
817
|
-
@click="editItem(item)"
|
|
818
|
-
></v-btn>
|
|
819
|
-
</template>
|
|
820
|
-
</v-tooltip>
|
|
821
|
-
${!hasActionWithChat ? expandToNode `
|
|
822
|
-
<v-tooltip text="Send to Chat">
|
|
823
|
-
<template v-slot:activator="{ props }">
|
|
824
|
-
<v-btn
|
|
825
|
-
v-bind="props"
|
|
826
|
-
icon="fa-solid fa-message"
|
|
827
|
-
size="x-small"
|
|
828
|
-
variant="text"
|
|
829
|
-
@click="sendItemToChat(item)"
|
|
830
|
-
></v-btn>
|
|
831
|
-
</template>
|
|
832
|
-
</v-tooltip>
|
|
833
|
-
` : ''}
|
|
834
|
-
<v-menu>
|
|
835
|
-
<template v-slot:activator="{ props }">
|
|
836
|
-
<v-btn
|
|
837
|
-
v-bind="props"
|
|
838
|
-
icon="fa-solid fa-ellipsis-vertical"
|
|
839
|
-
size="x-small"
|
|
840
|
-
variant="text"
|
|
841
|
-
></v-btn>
|
|
842
|
-
</template>
|
|
843
|
-
<v-list density="compact" class="pa-0" min-width="120">
|
|
844
|
-
${hasActionWithChat ? expandToNode `
|
|
845
|
-
<v-list-item
|
|
846
|
-
@click="sendItemToChat(item)"
|
|
847
|
-
title="Send to Chat"
|
|
848
|
-
min-height="32"
|
|
849
|
-
>
|
|
850
|
-
<template v-slot:prepend>
|
|
851
|
-
<v-icon icon="fa-solid fa-message" size="15"></v-icon>
|
|
852
|
-
</template>
|
|
853
|
-
</v-list-item>
|
|
854
|
-
` : ''}
|
|
855
|
-
${joinToNode(secondaryActions, generateSecondaryActionMenuItem, { appendNewLineIfNotEmpty: true })}
|
|
856
|
-
<v-list-item
|
|
857
|
-
@click="duplicateItem(item)"
|
|
858
|
-
title="Duplicate"
|
|
859
|
-
min-height="32"
|
|
860
|
-
>
|
|
861
|
-
<template v-slot:prepend>
|
|
862
|
-
<v-icon icon="fa-solid fa-copy" size="15"></v-icon>
|
|
863
|
-
</template>
|
|
864
|
-
</v-list-item>
|
|
865
|
-
<v-list-item
|
|
866
|
-
@click="deleteItem(item)"
|
|
867
|
-
title="Delete"
|
|
868
|
-
class="text-error"
|
|
869
|
-
min-height="32"
|
|
870
|
-
>
|
|
871
|
-
<template v-slot:prepend>
|
|
872
|
-
<v-icon icon="fa-solid fa-trash" size="15"></v-icon>
|
|
873
|
-
</template>
|
|
874
|
-
</v-list-item>
|
|
875
|
-
</v-list>
|
|
876
|
-
</v-menu>
|
|
877
|
-
</div>
|
|
878
|
-
</template>
|
|
879
|
-
|
|
880
|
-
<!-- No data slot -->
|
|
881
|
-
<template v-slot:no-data>
|
|
882
|
-
<div class="text-center pa-4">
|
|
883
|
-
<v-icon size="48" color="grey-lighten-1">fa-solid fa-inbox</v-icon>
|
|
884
|
-
<div class="text-h6 mt-2">No items found</div>
|
|
885
|
-
<div class="text-body-2 text-medium-emphasis">
|
|
886
|
-
Add your first {{ game.i18n.localize("${(_r = table.document.ref) === null || _r === void 0 ? void 0 : _r.name}").toLowerCase() }} to get started
|
|
887
|
-
</div>
|
|
888
|
-
</div>
|
|
889
|
-
</template>
|
|
890
|
-
</v-data-table>
|
|
891
|
-
</v-card>
|
|
892
|
-
|
|
893
|
-
<!-- Column Configuration Dialog -->
|
|
894
|
-
<v-dialog v-model="showColumnDialog" max-width="600px">
|
|
895
|
-
<v-card>
|
|
896
|
-
<v-card-title class="d-flex align-center">
|
|
897
|
-
<v-icon class="me-2">fa-solid fa-columns</v-icon>
|
|
898
|
-
Configure Columns
|
|
899
|
-
</v-card-title>
|
|
900
|
-
<v-divider></v-divider>
|
|
901
|
-
<v-card-text>
|
|
902
|
-
<div class="text-body-2 mb-4 text-medium-emphasis">
|
|
903
|
-
Drag to reorder columns, check/uncheck to show/hide columns
|
|
904
|
-
</div>
|
|
905
|
-
<v-list density="compact" class="column-config-list">
|
|
906
|
-
<div
|
|
907
|
-
v-for="(columnKey, index) in columnOrder"
|
|
908
|
-
:key="columnKey"
|
|
909
|
-
v-if="passesVisibility(customHeaders.find(h => h.key === columnKey))"
|
|
910
|
-
class="column-config-item"
|
|
911
|
-
draggable="true"
|
|
912
|
-
@dragstart="onColumnDragStart($event, index)"
|
|
913
|
-
@dragover="onColumnDragOver"
|
|
914
|
-
@drop="onColumnDrop($event, index)"
|
|
915
|
-
>
|
|
916
|
-
<v-list-item class="px-2">
|
|
917
|
-
<template v-slot:prepend>
|
|
918
|
-
<v-icon
|
|
919
|
-
icon="fa-solid fa-grip-vertical"
|
|
920
|
-
class="drag-handle me-2"
|
|
921
|
-
size="small"
|
|
922
|
-
style="cursor: grab;"
|
|
923
|
-
></v-icon>
|
|
924
|
-
<v-checkbox-btn
|
|
925
|
-
:model-value="columnKey === 'name' ? true : columnVisibility[columnKey]"
|
|
926
|
-
:disabled="columnKey === 'name'"
|
|
927
|
-
@update:model-value="toggleColumn(columnKey)"
|
|
928
|
-
class="me-2"
|
|
929
|
-
></v-checkbox-btn>
|
|
930
|
-
</template>
|
|
931
|
-
<v-list-item-title>
|
|
932
|
-
{{ customHeaders.find(h => h.key === columnKey)?.title || columnKey }}
|
|
933
|
-
</v-list-item-title>
|
|
934
|
-
</v-list-item>
|
|
935
|
-
</div>
|
|
936
|
-
</v-list>
|
|
937
|
-
</v-card-text>
|
|
938
|
-
<v-card-actions class="flexrow">
|
|
939
|
-
<v-btn
|
|
940
|
-
variant="elevated"
|
|
941
|
-
@click="showColumnDialog = false"
|
|
942
|
-
:color="primaryColor"
|
|
943
|
-
>
|
|
944
|
-
Close
|
|
945
|
-
</v-btn>
|
|
946
|
-
<v-btn
|
|
947
|
-
variant="elevated"
|
|
948
|
-
@click="resetColumns"
|
|
949
|
-
prepend-icon="fa-solid fa-undo"
|
|
950
|
-
:color="secondaryColor"
|
|
951
|
-
>
|
|
952
|
-
Reset to Default
|
|
953
|
-
</v-btn>
|
|
954
|
-
</v-card-actions>
|
|
955
|
-
</v-card>
|
|
956
|
-
</v-dialog>
|
|
957
|
-
</template>
|
|
772
|
+
: expandToNode `<v-avatar size="40" rounded="0">
|
|
773
|
+
<v-img :src="item.img" :alt="item.name" cover></v-img>
|
|
774
|
+
</v-avatar>`}
|
|
775
|
+
</template>
|
|
776
|
+
|
|
777
|
+
<!-- Name slot with description tooltip -->
|
|
778
|
+
<template v-slot:item.name="{ item }">
|
|
779
|
+
<div class="d-flex align-center" :data-tooltip="item.system.description">
|
|
780
|
+
<div class="font-weight-medium text-truncate" style="min-width: 120px; max-width: 200px;">{{ item.name }}</div>
|
|
781
|
+
</div>
|
|
782
|
+
</template>
|
|
783
|
+
|
|
784
|
+
<!-- Pinned slot -->
|
|
785
|
+
<template v-slot:item.system.pinned="{ item }">
|
|
786
|
+
<div class="d-flex justify-center">
|
|
787
|
+
<v-btn
|
|
788
|
+
icon
|
|
789
|
+
size="small"
|
|
790
|
+
variant="text"
|
|
791
|
+
@click="togglePin(item)"
|
|
792
|
+
:data-tooltip="item.system.pinned ? 'Unpin' : 'Pin'"
|
|
793
|
+
>
|
|
794
|
+
<v-icon
|
|
795
|
+
:icon="item.system.pinned ? 'fa-solid fa-thumbtack' : 'fa-regular fa-thumbtack'"
|
|
796
|
+
:color="item.system.pinned ? primaryColor : 'grey'"
|
|
797
|
+
size="small"
|
|
798
|
+
></v-icon>
|
|
799
|
+
</v-btn>
|
|
800
|
+
</div>
|
|
801
|
+
</template>
|
|
802
|
+
|
|
803
|
+
<!-- Custom field slots -->
|
|
804
|
+
${joinToNode(table.document.ref.body, p => generateSlotTemplate(table.document, p), { appendNewLineIfNotEmpty: true })}
|
|
805
|
+
|
|
806
|
+
<!-- Actions slot -->
|
|
807
|
+
<template v-slot:item.actions="{ item }">
|
|
808
|
+
<div class="d-flex align-center justify-center ga-1">
|
|
809
|
+
${joinToNode(primaryActions, generateActionButton, { appendNewLineIfNotEmpty: true })}
|
|
810
|
+
<v-tooltip text="Edit">
|
|
811
|
+
<template v-slot:activator="{ props }">
|
|
812
|
+
<v-btn
|
|
813
|
+
v-bind="props"
|
|
814
|
+
icon="fa-solid fa-edit"
|
|
815
|
+
size="x-small"
|
|
816
|
+
variant="text"
|
|
817
|
+
@click="editItem(item)"
|
|
818
|
+
></v-btn>
|
|
819
|
+
</template>
|
|
820
|
+
</v-tooltip>
|
|
821
|
+
${!hasActionWithChat ? expandToNode `
|
|
822
|
+
<v-tooltip text="Send to Chat">
|
|
823
|
+
<template v-slot:activator="{ props }">
|
|
824
|
+
<v-btn
|
|
825
|
+
v-bind="props"
|
|
826
|
+
icon="fa-solid fa-message"
|
|
827
|
+
size="x-small"
|
|
828
|
+
variant="text"
|
|
829
|
+
@click="sendItemToChat(item)"
|
|
830
|
+
></v-btn>
|
|
831
|
+
</template>
|
|
832
|
+
</v-tooltip>
|
|
833
|
+
` : ''}
|
|
834
|
+
<v-menu>
|
|
835
|
+
<template v-slot:activator="{ props }">
|
|
836
|
+
<v-btn
|
|
837
|
+
v-bind="props"
|
|
838
|
+
icon="fa-solid fa-ellipsis-vertical"
|
|
839
|
+
size="x-small"
|
|
840
|
+
variant="text"
|
|
841
|
+
></v-btn>
|
|
842
|
+
</template>
|
|
843
|
+
<v-list density="compact" class="pa-0" min-width="120">
|
|
844
|
+
${hasActionWithChat ? expandToNode `
|
|
845
|
+
<v-list-item
|
|
846
|
+
@click="sendItemToChat(item)"
|
|
847
|
+
title="Send to Chat"
|
|
848
|
+
min-height="32"
|
|
849
|
+
>
|
|
850
|
+
<template v-slot:prepend>
|
|
851
|
+
<v-icon icon="fa-solid fa-message" size="15"></v-icon>
|
|
852
|
+
</template>
|
|
853
|
+
</v-list-item>
|
|
854
|
+
` : ''}
|
|
855
|
+
${joinToNode(secondaryActions, generateSecondaryActionMenuItem, { appendNewLineIfNotEmpty: true })}
|
|
856
|
+
<v-list-item
|
|
857
|
+
@click="duplicateItem(item)"
|
|
858
|
+
title="Duplicate"
|
|
859
|
+
min-height="32"
|
|
860
|
+
>
|
|
861
|
+
<template v-slot:prepend>
|
|
862
|
+
<v-icon icon="fa-solid fa-copy" size="15"></v-icon>
|
|
863
|
+
</template>
|
|
864
|
+
</v-list-item>
|
|
865
|
+
<v-list-item
|
|
866
|
+
@click="deleteItem(item)"
|
|
867
|
+
title="Delete"
|
|
868
|
+
class="text-error"
|
|
869
|
+
min-height="32"
|
|
870
|
+
>
|
|
871
|
+
<template v-slot:prepend>
|
|
872
|
+
<v-icon icon="fa-solid fa-trash" size="15"></v-icon>
|
|
873
|
+
</template>
|
|
874
|
+
</v-list-item>
|
|
875
|
+
</v-list>
|
|
876
|
+
</v-menu>
|
|
877
|
+
</div>
|
|
878
|
+
</template>
|
|
879
|
+
|
|
880
|
+
<!-- No data slot -->
|
|
881
|
+
<template v-slot:no-data>
|
|
882
|
+
<div class="text-center pa-4">
|
|
883
|
+
<v-icon size="48" color="grey-lighten-1">fa-solid fa-inbox</v-icon>
|
|
884
|
+
<div class="text-h6 mt-2">No items found</div>
|
|
885
|
+
<div class="text-body-2 text-medium-emphasis">
|
|
886
|
+
Add your first {{ game.i18n.localize("${(_r = table.document.ref) === null || _r === void 0 ? void 0 : _r.name}").toLowerCase() }} to get started
|
|
887
|
+
</div>
|
|
888
|
+
</div>
|
|
889
|
+
</template>
|
|
890
|
+
</v-data-table>
|
|
891
|
+
</v-card>
|
|
892
|
+
|
|
893
|
+
<!-- Column Configuration Dialog -->
|
|
894
|
+
<v-dialog v-model="showColumnDialog" max-width="600px">
|
|
895
|
+
<v-card>
|
|
896
|
+
<v-card-title class="d-flex align-center">
|
|
897
|
+
<v-icon class="me-2">fa-solid fa-columns</v-icon>
|
|
898
|
+
Configure Columns
|
|
899
|
+
</v-card-title>
|
|
900
|
+
<v-divider></v-divider>
|
|
901
|
+
<v-card-text>
|
|
902
|
+
<div class="text-body-2 mb-4 text-medium-emphasis">
|
|
903
|
+
Drag to reorder columns, check/uncheck to show/hide columns
|
|
904
|
+
</div>
|
|
905
|
+
<v-list density="compact" class="column-config-list">
|
|
906
|
+
<div
|
|
907
|
+
v-for="(columnKey, index) in columnOrder"
|
|
908
|
+
:key="columnKey"
|
|
909
|
+
v-if="passesVisibility(customHeaders.find(h => h.key === columnKey))"
|
|
910
|
+
class="column-config-item"
|
|
911
|
+
draggable="true"
|
|
912
|
+
@dragstart="onColumnDragStart($event, index)"
|
|
913
|
+
@dragover="onColumnDragOver"
|
|
914
|
+
@drop="onColumnDrop($event, index)"
|
|
915
|
+
>
|
|
916
|
+
<v-list-item class="px-2">
|
|
917
|
+
<template v-slot:prepend>
|
|
918
|
+
<v-icon
|
|
919
|
+
icon="fa-solid fa-grip-vertical"
|
|
920
|
+
class="drag-handle me-2"
|
|
921
|
+
size="small"
|
|
922
|
+
style="cursor: grab;"
|
|
923
|
+
></v-icon>
|
|
924
|
+
<v-checkbox-btn
|
|
925
|
+
:model-value="columnKey === 'name' ? true : columnVisibility[columnKey]"
|
|
926
|
+
:disabled="columnKey === 'name'"
|
|
927
|
+
@update:model-value="toggleColumn(columnKey)"
|
|
928
|
+
class="me-2"
|
|
929
|
+
></v-checkbox-btn>
|
|
930
|
+
</template>
|
|
931
|
+
<v-list-item-title>
|
|
932
|
+
{{ customHeaders.find(h => h.key === columnKey)?.title || columnKey }}
|
|
933
|
+
</v-list-item-title>
|
|
934
|
+
</v-list-item>
|
|
935
|
+
</div>
|
|
936
|
+
</v-list>
|
|
937
|
+
</v-card-text>
|
|
938
|
+
<v-card-actions class="flexrow">
|
|
939
|
+
<v-btn
|
|
940
|
+
variant="elevated"
|
|
941
|
+
@click="showColumnDialog = false"
|
|
942
|
+
:color="primaryColor"
|
|
943
|
+
>
|
|
944
|
+
Close
|
|
945
|
+
</v-btn>
|
|
946
|
+
<v-btn
|
|
947
|
+
variant="elevated"
|
|
948
|
+
@click="resetColumns"
|
|
949
|
+
prepend-icon="fa-solid fa-undo"
|
|
950
|
+
:color="secondaryColor"
|
|
951
|
+
>
|
|
952
|
+
Reset to Default
|
|
953
|
+
</v-btn>
|
|
954
|
+
</v-card-actions>
|
|
955
|
+
</v-card>
|
|
956
|
+
</v-dialog>
|
|
957
|
+
</template>
|
|
958
958
|
`;
|
|
959
959
|
fs.writeFileSync(generatedFilePath, toString(fileNode));
|
|
960
960
|
function generateActionButton(action) {
|
|
@@ -963,19 +963,19 @@ export function generateVuetifyDatatableComponent(entry, id, document, pageName,
|
|
|
963
963
|
const icon = (_b = (_a = standardParams.find(x => isIconParam(x))) === null || _a === void 0 ? void 0 : _a.value) !== null && _b !== void 0 ? _b : "fa-solid fa-bolt";
|
|
964
964
|
const color = (_d = (_c = standardParams.find(x => isColorParam(x))) === null || _c === void 0 ? void 0 : _c.value) !== null && _d !== void 0 ? _d : "primary";
|
|
965
965
|
const parentDocument = AstUtils.getContainerOfType(action, isDocument);
|
|
966
|
-
return expandToNode `
|
|
967
|
-
<v-tooltip :text="game.i18n.localize('${parentDocument === null || parentDocument === void 0 ? void 0 : parentDocument.name}.${action.name}')">
|
|
968
|
-
<template v-slot:activator="{ props }">
|
|
969
|
-
<v-btn
|
|
970
|
-
v-bind="props"
|
|
971
|
-
icon="${icon}"
|
|
972
|
-
size="x-small"
|
|
973
|
-
variant="text"
|
|
974
|
-
color="${color}"
|
|
975
|
-
@click="customItemAction(item, '${action.name.toLowerCase()}')"
|
|
976
|
-
></v-btn>
|
|
977
|
-
</template>
|
|
978
|
-
</v-tooltip>
|
|
966
|
+
return expandToNode `
|
|
967
|
+
<v-tooltip :text="game.i18n.localize('${parentDocument === null || parentDocument === void 0 ? void 0 : parentDocument.name}.${action.name}')">
|
|
968
|
+
<template v-slot:activator="{ props }">
|
|
969
|
+
<v-btn
|
|
970
|
+
v-bind="props"
|
|
971
|
+
icon="${icon}"
|
|
972
|
+
size="x-small"
|
|
973
|
+
variant="text"
|
|
974
|
+
color="${color}"
|
|
975
|
+
@click="customItemAction(item, '${action.name.toLowerCase()}')"
|
|
976
|
+
></v-btn>
|
|
977
|
+
</template>
|
|
978
|
+
</v-tooltip>
|
|
979
979
|
`;
|
|
980
980
|
}
|
|
981
981
|
function generateSecondaryActionMenuItem(action) {
|
|
@@ -983,16 +983,16 @@ export function generateVuetifyDatatableComponent(entry, id, document, pageName,
|
|
|
983
983
|
const standardParams = action.params;
|
|
984
984
|
const icon = (_b = (_a = standardParams.find(x => isIconParam(x))) === null || _a === void 0 ? void 0 : _a.value) !== null && _b !== void 0 ? _b : "fa-solid fa-bolt";
|
|
985
985
|
const parentDocument = AstUtils.getContainerOfType(action, isDocument);
|
|
986
|
-
return expandToNode `
|
|
987
|
-
<v-list-item
|
|
988
|
-
@click="customItemAction(item, '${action.name.toLowerCase()}')"
|
|
989
|
-
:title="game.i18n.localize('${parentDocument === null || parentDocument === void 0 ? void 0 : parentDocument.name}.${action.name}')"
|
|
990
|
-
min-height="32"
|
|
991
|
-
>
|
|
992
|
-
<template v-slot:prepend>
|
|
993
|
-
<v-icon icon="${icon}" size="15"></v-icon>
|
|
994
|
-
</template>
|
|
995
|
-
</v-list-item>
|
|
986
|
+
return expandToNode `
|
|
987
|
+
<v-list-item
|
|
988
|
+
@click="customItemAction(item, '${action.name.toLowerCase()}')"
|
|
989
|
+
:title="game.i18n.localize('${parentDocument === null || parentDocument === void 0 ? void 0 : parentDocument.name}.${action.name}')"
|
|
990
|
+
min-height="32"
|
|
991
|
+
>
|
|
992
|
+
<template v-slot:prepend>
|
|
993
|
+
<v-icon icon="${icon}" size="15"></v-icon>
|
|
994
|
+
</template>
|
|
995
|
+
</v-list-item>
|
|
996
996
|
`;
|
|
997
997
|
}
|
|
998
998
|
}
|