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.
Files changed (116) hide show
  1. package/.claude/agents/langium-language-designer.md +38 -38
  2. package/.claude/agents/typescript-vscode-expert.md +29 -29
  3. package/.claude/agents/ui-ux-designer.md +36 -36
  4. package/.claude/settings.local.json +33 -33
  5. package/.idea/inspectionProfiles/Project_Default.xml +6 -6
  6. package/.idea/isdl.iml +13 -13
  7. package/.idea/modules.xml +8 -8
  8. package/.idea/vcs.xml +6 -6
  9. package/.idea/watcherTasks.xml +3 -3
  10. package/.vscodeignore +18 -18
  11. package/LICENSE +673 -673
  12. package/README.md +86 -86
  13. package/bin/cli.js +4 -4
  14. package/bin/lsp.js +8 -8
  15. package/out/_backgrounds.scss +91 -91
  16. package/out/_handlebars.scss +497 -497
  17. package/out/_isdlStyles.scss +1444 -1381
  18. package/out/_vuetifyOverrides.scss +425 -425
  19. package/out/_vuetifyStyles.scss +31957 -31957
  20. package/out/cli/components/_backgrounds.scss +91 -91
  21. package/out/cli/components/_handlebars.scss +497 -497
  22. package/out/cli/components/_isdlStyles.scss +1444 -1381
  23. package/out/cli/components/_vuetifyOverrides.scss +425 -425
  24. package/out/cli/components/_vuetifyStyles.scss +31957 -31957
  25. package/out/cli/components/active-effect-sheet-generator.js +453 -453
  26. package/out/cli/components/chat-card-generator.js +654 -651
  27. package/out/cli/components/chat-card-generator.js.map +1 -1
  28. package/out/cli/components/css-generator.js +4 -4
  29. package/out/cli/components/damage-roll-generator.js +160 -160
  30. package/out/cli/components/datamodel-generator.js +264 -257
  31. package/out/cli/components/datamodel-generator.js.map +1 -1
  32. package/out/cli/components/derived-data-generator.js +923 -923
  33. package/out/cli/components/hotbar-drop-hook-generator.js +82 -82
  34. package/out/cli/components/init-hook-generator.js +495 -495
  35. package/out/cli/components/language-generator.js +1 -1
  36. package/out/cli/components/language-generator.js.map +1 -1
  37. package/out/cli/components/measured-template-preview.js +221 -221
  38. package/out/cli/components/method-generator.js +979 -887
  39. package/out/cli/components/method-generator.js.map +1 -1
  40. package/out/cli/components/ready-hook-generator.js +404 -404
  41. package/out/cli/components/token-generator.js +116 -116
  42. package/out/cli/components/vue/base-components/vue-attribute.js +138 -138
  43. package/out/cli/components/vue/base-components/vue-boolean.js +64 -64
  44. package/out/cli/components/vue/base-components/vue-calculator.js +93 -93
  45. package/out/cli/components/vue/base-components/vue-damage-application.js +356 -356
  46. package/out/cli/components/vue/base-components/vue-damage-bonuses.js +165 -165
  47. package/out/cli/components/vue/base-components/vue-damage-resistances.js +196 -196
  48. package/out/cli/components/vue/base-components/vue-damage-track.js +121 -121
  49. package/out/cli/components/vue/base-components/vue-date-time.js +42 -42
  50. package/out/cli/components/vue/base-components/vue-dice.js +98 -98
  51. package/out/cli/components/vue/base-components/vue-die.js +73 -73
  52. package/out/cli/components/vue/base-components/vue-document-choice.js +149 -149
  53. package/out/cli/components/vue/base-components/vue-document-choices.js +179 -179
  54. package/out/cli/components/vue/base-components/vue-document-link.js +60 -60
  55. package/out/cli/components/vue/base-components/vue-extended-choice.js +88 -88
  56. package/out/cli/components/vue/base-components/vue-inventory.js +519 -519
  57. package/out/cli/components/vue/base-components/vue-macro-choice.js +138 -138
  58. package/out/cli/components/vue/base-components/vue-measured-template.js +530 -530
  59. package/out/cli/components/vue/base-components/vue-money.js +483 -483
  60. package/out/cli/components/vue/base-components/vue-number.js +174 -174
  61. package/out/cli/components/vue/base-components/vue-paperdoll.js +43 -43
  62. package/out/cli/components/vue/base-components/vue-parent-property-reference.js +76 -76
  63. package/out/cli/components/vue/base-components/vue-prosemirror.js +18 -18
  64. package/out/cli/components/vue/base-components/vue-resource.js +136 -136
  65. package/out/cli/components/vue/base-components/vue-roll-visualizer.js +286 -109
  66. package/out/cli/components/vue/base-components/vue-roll-visualizer.js.map +1 -1
  67. package/out/cli/components/vue/base-components/vue-self-property-reference.js +62 -62
  68. package/out/cli/components/vue/base-components/vue-string-choice.js +98 -98
  69. package/out/cli/components/vue/base-components/vue-string-choices.js +203 -203
  70. package/out/cli/components/vue/base-components/vue-string.js +60 -60
  71. package/out/cli/components/vue/base-components/vue-text-field.js +53 -53
  72. package/out/cli/components/vue/base-components/vue-tracker.js +431 -431
  73. package/out/cli/components/vue/vue-action-component-generator.js +64 -64
  74. package/out/cli/components/vue/vue-active-effect-sheet-generator.js +856 -856
  75. package/out/cli/components/vue/vue-datatable-sheet-class-generator.js +292 -292
  76. package/out/cli/components/vue/vue-datatable2-component-generator.js +824 -824
  77. package/out/cli/components/vue/vue-document-creation-app.js +121 -121
  78. package/out/cli/components/vue/vue-document-creation-sheet.js +94 -94
  79. package/out/cli/components/vue/vue-generator.js +40 -40
  80. package/out/cli/components/vue/vue-mixin.js +296 -296
  81. package/out/cli/components/vue/vue-pinned-datatable-component-generator.js +260 -260
  82. package/out/cli/components/vue/vue-prompt-generator.js +91 -76
  83. package/out/cli/components/vue/vue-prompt-generator.js.map +1 -1
  84. package/out/cli/components/vue/vue-prompt-sheet-class-generator.js +317 -317
  85. package/out/cli/components/vue/vue-sheet-application-generator.js +1177 -1167
  86. package/out/cli/components/vue/vue-sheet-application-generator.js.map +1 -1
  87. package/out/cli/components/vue/vue-sheet-class-generator.js +510 -510
  88. package/out/cli/generator.js +438 -433
  89. package/out/cli/generator.js.map +1 -1
  90. package/out/extension/github/githubAuthProvider.js +71 -29
  91. package/out/extension/github/githubAuthProvider.js.map +1 -1
  92. package/out/extension/github/githubGistManager.js +4 -3
  93. package/out/extension/github/githubGistManager.js.map +1 -1
  94. package/out/extension/github/githubManager.js +40 -38
  95. package/out/extension/github/githubManager.js.map +1 -1
  96. package/out/extension/github/githubQuickActions.js +120 -120
  97. package/out/extension/github/system-workflow.yml +47 -47
  98. package/out/extension/main.cjs +909 -532
  99. package/out/extension/main.cjs.map +3 -3
  100. package/out/extension/package.json +419 -419
  101. package/out/language/generated/ast.js +51 -2
  102. package/out/language/generated/ast.js.map +1 -1
  103. package/out/language/generated/grammar.js +14240 -13991
  104. package/out/language/generated/grammar.js.map +1 -1
  105. package/out/language/intelligent-system-design-language-validator.js +32 -2
  106. package/out/language/intelligent-system-design-language-validator.js.map +1 -1
  107. package/out/language/isdl-scope-provider.js +14 -1
  108. package/out/language/isdl-scope-provider.js.map +1 -1
  109. package/out/language/main.cjs +913 -569
  110. package/out/language/main.cjs.map +3 -3
  111. package/out/package.json +419 -419
  112. package/out/progressbar.min.js +6 -6
  113. package/out/styles.scss +762 -747
  114. package/out/test/validating/diagnostics.test.js +40 -0
  115. package/out/test/validating/diagnostics.test.js.map +1 -1
  116. 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
- &nbsp; {{ 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
+ &nbsp; {{ 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
  }