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