intelligent-system-design-language 0.3.21 → 0.3.23
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude/agents/langium-language-designer.md +38 -38
- package/.claude/agents/typescript-vscode-expert.md +29 -29
- package/.claude/agents/ui-ux-designer.md +36 -36
- package/.claude/settings.local.json +33 -33
- package/.idea/inspectionProfiles/Project_Default.xml +6 -6
- package/.idea/isdl.iml +13 -13
- package/.idea/modules.xml +8 -8
- package/.idea/vcs.xml +6 -6
- package/.idea/watcherTasks.xml +3 -3
- package/.vscodeignore +18 -18
- package/LICENSE +673 -673
- package/README.md +86 -86
- package/bin/cli.js +4 -4
- package/bin/lsp.js +8 -8
- package/out/_backgrounds.scss +91 -91
- package/out/_handlebars.scss +497 -497
- package/out/_isdlStyles.scss +1444 -1381
- package/out/_vuetifyOverrides.scss +425 -425
- package/out/_vuetifyStyles.scss +31957 -31957
- package/out/cli/components/_backgrounds.scss +91 -91
- package/out/cli/components/_handlebars.scss +497 -497
- package/out/cli/components/_isdlStyles.scss +1444 -1381
- package/out/cli/components/_vuetifyOverrides.scss +425 -425
- package/out/cli/components/_vuetifyStyles.scss +31957 -31957
- package/out/cli/components/active-effect-sheet-generator.js +453 -453
- package/out/cli/components/chat-card-generator.js +654 -651
- package/out/cli/components/chat-card-generator.js.map +1 -1
- package/out/cli/components/css-generator.js +4 -4
- package/out/cli/components/damage-roll-generator.js +160 -160
- package/out/cli/components/datamodel-generator.js +264 -257
- package/out/cli/components/datamodel-generator.js.map +1 -1
- package/out/cli/components/derived-data-generator.js +923 -923
- package/out/cli/components/hotbar-drop-hook-generator.js +82 -82
- package/out/cli/components/init-hook-generator.js +495 -495
- package/out/cli/components/language-generator.js +1 -1
- package/out/cli/components/language-generator.js.map +1 -1
- package/out/cli/components/measured-template-preview.js +221 -221
- package/out/cli/components/method-generator.js +979 -887
- package/out/cli/components/method-generator.js.map +1 -1
- package/out/cli/components/ready-hook-generator.js +404 -404
- package/out/cli/components/token-generator.js +116 -116
- package/out/cli/components/vue/base-components/vue-attribute.js +138 -138
- package/out/cli/components/vue/base-components/vue-boolean.js +64 -64
- package/out/cli/components/vue/base-components/vue-calculator.js +93 -93
- package/out/cli/components/vue/base-components/vue-damage-application.js +356 -356
- package/out/cli/components/vue/base-components/vue-damage-bonuses.js +165 -165
- package/out/cli/components/vue/base-components/vue-damage-resistances.js +196 -196
- package/out/cli/components/vue/base-components/vue-damage-track.js +121 -121
- package/out/cli/components/vue/base-components/vue-date-time.js +42 -42
- package/out/cli/components/vue/base-components/vue-dice.js +98 -98
- package/out/cli/components/vue/base-components/vue-die.js +73 -73
- package/out/cli/components/vue/base-components/vue-document-choice.js +149 -149
- package/out/cli/components/vue/base-components/vue-document-choices.js +179 -179
- package/out/cli/components/vue/base-components/vue-document-link.js +60 -60
- package/out/cli/components/vue/base-components/vue-extended-choice.js +88 -88
- package/out/cli/components/vue/base-components/vue-inventory.js +519 -519
- package/out/cli/components/vue/base-components/vue-macro-choice.js +138 -138
- package/out/cli/components/vue/base-components/vue-measured-template.js +530 -530
- package/out/cli/components/vue/base-components/vue-money.js +483 -483
- package/out/cli/components/vue/base-components/vue-number.js +174 -174
- package/out/cli/components/vue/base-components/vue-paperdoll.js +43 -43
- package/out/cli/components/vue/base-components/vue-parent-property-reference.js +76 -76
- package/out/cli/components/vue/base-components/vue-prosemirror.js +18 -18
- package/out/cli/components/vue/base-components/vue-resource.js +136 -136
- package/out/cli/components/vue/base-components/vue-roll-visualizer.js +286 -109
- package/out/cli/components/vue/base-components/vue-roll-visualizer.js.map +1 -1
- package/out/cli/components/vue/base-components/vue-self-property-reference.js +62 -62
- package/out/cli/components/vue/base-components/vue-string-choice.js +98 -98
- package/out/cli/components/vue/base-components/vue-string-choices.js +203 -203
- package/out/cli/components/vue/base-components/vue-string.js +60 -60
- package/out/cli/components/vue/base-components/vue-text-field.js +53 -53
- package/out/cli/components/vue/base-components/vue-tracker.js +431 -431
- package/out/cli/components/vue/vue-action-component-generator.js +64 -64
- package/out/cli/components/vue/vue-active-effect-sheet-generator.js +856 -856
- package/out/cli/components/vue/vue-datatable-sheet-class-generator.js +292 -292
- package/out/cli/components/vue/vue-datatable2-component-generator.js +824 -824
- package/out/cli/components/vue/vue-document-creation-app.js +121 -121
- package/out/cli/components/vue/vue-document-creation-sheet.js +94 -94
- package/out/cli/components/vue/vue-generator.js +40 -40
- package/out/cli/components/vue/vue-mixin.js +296 -296
- package/out/cli/components/vue/vue-pinned-datatable-component-generator.js +260 -260
- package/out/cli/components/vue/vue-prompt-generator.js +91 -76
- package/out/cli/components/vue/vue-prompt-generator.js.map +1 -1
- package/out/cli/components/vue/vue-prompt-sheet-class-generator.js +317 -317
- package/out/cli/components/vue/vue-sheet-application-generator.js +1177 -1167
- package/out/cli/components/vue/vue-sheet-application-generator.js.map +1 -1
- package/out/cli/components/vue/vue-sheet-class-generator.js +510 -510
- package/out/cli/generator.js +438 -433
- package/out/cli/generator.js.map +1 -1
- package/out/extension/github/githubAuthProvider.js +71 -29
- package/out/extension/github/githubAuthProvider.js.map +1 -1
- package/out/extension/github/githubGistManager.js +4 -3
- package/out/extension/github/githubGistManager.js.map +1 -1
- package/out/extension/github/githubManager.js +40 -38
- package/out/extension/github/githubManager.js.map +1 -1
- package/out/extension/github/githubQuickActions.js +120 -120
- package/out/extension/github/system-workflow.yml +47 -47
- package/out/extension/main.cjs +909 -532
- package/out/extension/main.cjs.map +3 -3
- package/out/extension/package.json +419 -419
- package/out/language/generated/ast.js +51 -2
- package/out/language/generated/ast.js.map +1 -1
- package/out/language/generated/grammar.js +14240 -13991
- package/out/language/generated/grammar.js.map +1 -1
- package/out/language/intelligent-system-design-language-validator.js +32 -2
- package/out/language/intelligent-system-design-language-validator.js.map +1 -1
- package/out/language/isdl-scope-provider.js +14 -1
- package/out/language/isdl-scope-provider.js.map +1 -1
- package/out/language/main.cjs +913 -569
- package/out/language/main.cjs.map +3 -3
- package/out/package.json +419 -419
- package/out/progressbar.min.js +6 -6
- package/out/styles.scss +762 -747
- package/out/test/validating/diagnostics.test.js +40 -0
- package/out/test/validating/diagnostics.test.js.map +1 -1
- package/package.json +419 -419
|
@@ -7,104 +7,104 @@ export default function generateStringChoiceComponent(destination, entry) {
|
|
|
7
7
|
if (!fs.existsSync(generatedFileDir)) {
|
|
8
8
|
fs.mkdirSync(generatedFileDir, { recursive: true });
|
|
9
9
|
}
|
|
10
|
-
const fileNode = expandToNode `
|
|
11
|
-
<script setup>
|
|
12
|
-
import { ref, computed, inject } from "vue";
|
|
13
|
-
|
|
14
|
-
const props = defineProps({
|
|
15
|
-
label: String,
|
|
16
|
-
systemPath: String,
|
|
17
|
-
context: Object,
|
|
18
|
-
visibility: String,
|
|
19
|
-
editMode: Boolean,
|
|
20
|
-
icon: String,
|
|
21
|
-
color: String,
|
|
22
|
-
disabled: Boolean,
|
|
23
|
-
items: {
|
|
24
|
-
type: Array,
|
|
25
|
-
default: () => []
|
|
26
|
-
},
|
|
27
|
-
isExtended: {
|
|
28
|
-
type: Boolean,
|
|
29
|
-
default: false
|
|
30
|
-
},
|
|
31
|
-
primaryColor: String,
|
|
32
|
-
secondaryColor: String
|
|
33
|
-
});
|
|
34
|
-
|
|
35
|
-
const document = inject("rawDocument");
|
|
36
|
-
|
|
37
|
-
const value = computed({
|
|
38
|
-
get: () => foundry.utils.getProperty(props.context, props.systemPath),
|
|
39
|
-
set: (newValue) => foundry.utils.setProperty(props.context, props.systemPath, newValue)
|
|
40
|
-
});
|
|
41
|
-
|
|
42
|
-
const isHidden = computed(() => {
|
|
43
|
-
if (props.visibility === "hidden") {
|
|
44
|
-
return true;
|
|
45
|
-
}
|
|
46
|
-
if (props.visibility === "gm" && !game.user.isGM) {
|
|
47
|
-
return true;
|
|
48
|
-
}
|
|
49
|
-
return false;
|
|
50
|
-
});
|
|
51
|
-
|
|
52
|
-
const isDisabled = computed(() => {
|
|
53
|
-
return props.disabled ||
|
|
54
|
-
props.visibility === "locked" ||
|
|
55
|
-
props.visibility === "readonly" ||
|
|
56
|
-
(props.visibility === "gmOnly" && !game.user.isGM);
|
|
57
|
-
});
|
|
58
|
-
|
|
59
|
-
const fieldColor = computed(() => {
|
|
60
|
-
return props.color || 'primary';
|
|
61
|
-
});
|
|
62
|
-
|
|
63
|
-
const localizedLabel = computed(() => {
|
|
64
|
-
return game.i18n.localize(props.label);
|
|
65
|
-
});
|
|
66
|
-
</script>
|
|
67
|
-
|
|
68
|
-
<template>
|
|
69
|
-
<div v-if="!isHidden" class="isdl-string-choice single-wide">
|
|
70
|
-
<!-- Simple choice field - uses v-select -->
|
|
71
|
-
<v-select
|
|
72
|
-
v-if="!props.isExtended"
|
|
73
|
-
:model-value="value"
|
|
74
|
-
@update:model-value="(v) => { value = v; if (document) document.update({ [props.systemPath]: v }); }"
|
|
75
|
-
:name="props.systemPath"
|
|
76
|
-
:items="props.items"
|
|
77
|
-
item-title="label"
|
|
78
|
-
item-value="value"
|
|
79
|
-
:disabled="isDisabled"
|
|
80
|
-
:color="fieldColor"
|
|
81
|
-
variant="outlined"
|
|
82
|
-
density="compact"
|
|
83
|
-
>
|
|
84
|
-
<template #label>
|
|
85
|
-
<span class="field-label">
|
|
86
|
-
<v-icon v-if="props.icon" :icon="props.icon" size="small" class="me-1"></v-icon>
|
|
87
|
-
{{ localizedLabel }}
|
|
88
|
-
</span>
|
|
89
|
-
</template>
|
|
90
|
-
</v-select>
|
|
91
|
-
|
|
92
|
-
<!-- Extended choice field - uses i-extended-choice -->
|
|
93
|
-
<i-extended-choice
|
|
94
|
-
v-else
|
|
95
|
-
:label="props.label"
|
|
96
|
-
:icon="props.icon"
|
|
97
|
-
:systemPath="props.systemPath"
|
|
98
|
-
:context="props.context"
|
|
99
|
-
:items="props.items"
|
|
100
|
-
:primaryColor="props.primaryColor"
|
|
101
|
-
:secondaryColor="props.secondaryColor"
|
|
102
|
-
:visibility="props.visibility"
|
|
103
|
-
:disabled="props.disabled"
|
|
104
|
-
:color="props.color">
|
|
105
|
-
</i-extended-choice>
|
|
106
|
-
</div>
|
|
107
|
-
</template>
|
|
10
|
+
const fileNode = expandToNode `
|
|
11
|
+
<script setup>
|
|
12
|
+
import { ref, computed, inject } from "vue";
|
|
13
|
+
|
|
14
|
+
const props = defineProps({
|
|
15
|
+
label: String,
|
|
16
|
+
systemPath: String,
|
|
17
|
+
context: Object,
|
|
18
|
+
visibility: String,
|
|
19
|
+
editMode: Boolean,
|
|
20
|
+
icon: String,
|
|
21
|
+
color: String,
|
|
22
|
+
disabled: Boolean,
|
|
23
|
+
items: {
|
|
24
|
+
type: Array,
|
|
25
|
+
default: () => []
|
|
26
|
+
},
|
|
27
|
+
isExtended: {
|
|
28
|
+
type: Boolean,
|
|
29
|
+
default: false
|
|
30
|
+
},
|
|
31
|
+
primaryColor: String,
|
|
32
|
+
secondaryColor: String
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
const document = inject("rawDocument");
|
|
36
|
+
|
|
37
|
+
const value = computed({
|
|
38
|
+
get: () => foundry.utils.getProperty(props.context, props.systemPath),
|
|
39
|
+
set: (newValue) => foundry.utils.setProperty(props.context, props.systemPath, newValue)
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
const isHidden = computed(() => {
|
|
43
|
+
if (props.visibility === "hidden") {
|
|
44
|
+
return true;
|
|
45
|
+
}
|
|
46
|
+
if (props.visibility === "gm" && !game.user.isGM) {
|
|
47
|
+
return true;
|
|
48
|
+
}
|
|
49
|
+
return false;
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
const isDisabled = computed(() => {
|
|
53
|
+
return props.disabled ||
|
|
54
|
+
props.visibility === "locked" ||
|
|
55
|
+
props.visibility === "readonly" ||
|
|
56
|
+
(props.visibility === "gmOnly" && !game.user.isGM);
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
const fieldColor = computed(() => {
|
|
60
|
+
return props.color || 'primary';
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
const localizedLabel = computed(() => {
|
|
64
|
+
return game.i18n.localize(props.label);
|
|
65
|
+
});
|
|
66
|
+
</script>
|
|
67
|
+
|
|
68
|
+
<template>
|
|
69
|
+
<div v-if="!isHidden" class="isdl-string-choice single-wide">
|
|
70
|
+
<!-- Simple choice field - uses v-select -->
|
|
71
|
+
<v-select
|
|
72
|
+
v-if="!props.isExtended"
|
|
73
|
+
:model-value="value"
|
|
74
|
+
@update:model-value="(v) => { value = v; if (document) document.update({ [props.systemPath]: v }); }"
|
|
75
|
+
:name="props.systemPath"
|
|
76
|
+
:items="props.items"
|
|
77
|
+
item-title="label"
|
|
78
|
+
item-value="value"
|
|
79
|
+
:disabled="isDisabled"
|
|
80
|
+
:color="fieldColor"
|
|
81
|
+
variant="outlined"
|
|
82
|
+
density="compact"
|
|
83
|
+
>
|
|
84
|
+
<template #label>
|
|
85
|
+
<span class="field-label">
|
|
86
|
+
<v-icon v-if="props.icon" :icon="props.icon" size="small" class="me-1"></v-icon>
|
|
87
|
+
{{ localizedLabel }}
|
|
88
|
+
</span>
|
|
89
|
+
</template>
|
|
90
|
+
</v-select>
|
|
91
|
+
|
|
92
|
+
<!-- Extended choice field - uses i-extended-choice -->
|
|
93
|
+
<i-extended-choice
|
|
94
|
+
v-else
|
|
95
|
+
:label="props.label"
|
|
96
|
+
:icon="props.icon"
|
|
97
|
+
:systemPath="props.systemPath"
|
|
98
|
+
:context="props.context"
|
|
99
|
+
:items="props.items"
|
|
100
|
+
:primaryColor="props.primaryColor"
|
|
101
|
+
:secondaryColor="props.secondaryColor"
|
|
102
|
+
:visibility="props.visibility"
|
|
103
|
+
:disabled="props.disabled"
|
|
104
|
+
:color="props.color">
|
|
105
|
+
</i-extended-choice>
|
|
106
|
+
</div>
|
|
107
|
+
</template>
|
|
108
108
|
`.appendNewLine();
|
|
109
109
|
fs.writeFileSync(generatedFilePath, toString(fileNode));
|
|
110
110
|
}
|
|
@@ -7,209 +7,209 @@ export default function generateStringChoicesComponent(destination, entry) {
|
|
|
7
7
|
if (!fs.existsSync(generatedFileDir)) {
|
|
8
8
|
fs.mkdirSync(generatedFileDir, { recursive: true });
|
|
9
9
|
}
|
|
10
|
-
const fileNode = expandToNode `
|
|
11
|
-
<script setup>
|
|
12
|
-
import { ref, computed, inject } from "vue";
|
|
13
|
-
|
|
14
|
-
const props = defineProps({
|
|
15
|
-
label: String,
|
|
16
|
-
systemPath: String,
|
|
17
|
-
context: Object,
|
|
18
|
-
icon: String,
|
|
19
|
-
color: String,
|
|
20
|
-
disabled: Boolean,
|
|
21
|
-
items: {
|
|
22
|
-
type: Array,
|
|
23
|
-
default: () => []
|
|
24
|
-
},
|
|
25
|
-
isExtended: {
|
|
26
|
-
type: Boolean,
|
|
27
|
-
default: false
|
|
28
|
-
},
|
|
29
|
-
maxSelections: Number,
|
|
30
|
-
primaryColor: String,
|
|
31
|
-
secondaryColor: String
|
|
32
|
-
});
|
|
33
|
-
|
|
34
|
-
const document = inject("rawDocument");
|
|
35
|
-
|
|
36
|
-
// Get the array of selected values
|
|
37
|
-
const selectedValues = computed({
|
|
38
|
-
get: () => {
|
|
39
|
-
const values = foundry.utils.getProperty(props.context, props.systemPath);
|
|
40
|
-
if (!Array.isArray(values)) return [];
|
|
41
|
-
|
|
42
|
-
if (props.isExtended) {
|
|
43
|
-
return values
|
|
44
|
-
.map(item => typeof item === 'object' && item !== null ? item.value : item)
|
|
45
|
-
.filter(v => v !== undefined && v !== null);
|
|
46
|
-
} else {
|
|
47
|
-
return values.filter(v => typeof v === 'string');
|
|
48
|
-
}
|
|
49
|
-
},
|
|
50
|
-
set: (newValues) => {
|
|
51
|
-
if (!Array.isArray(newValues)) {
|
|
52
|
-
foundry.utils.setProperty(props.context, props.systemPath, []);
|
|
53
|
-
return;
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
if (props.isExtended) {
|
|
57
|
-
// For extended choices, create objects with value, icon, color from items
|
|
58
|
-
const selectedObjects = newValues.map(value => {
|
|
59
|
-
const item = props.items.find(i => i.value === value);
|
|
60
|
-
if (item) {
|
|
61
|
-
const obj = { value: item.value, icon: item.icon || "", color: item.color || "#ffffff" };
|
|
62
|
-
// Add any custom properties
|
|
63
|
-
if (item.customKeys) {
|
|
64
|
-
item.customKeys.forEach(custom => {
|
|
65
|
-
obj[custom.key.toLowerCase()] = custom.value;
|
|
66
|
-
});
|
|
67
|
-
}
|
|
68
|
-
return obj;
|
|
69
|
-
}
|
|
70
|
-
return { value, icon: "", color: "#ffffff" };
|
|
71
|
-
});
|
|
72
|
-
foundry.utils.setProperty(props.context, props.systemPath, selectedObjects);
|
|
73
|
-
} else {
|
|
74
|
-
// For simple choices, store the array of strings directly
|
|
75
|
-
foundry.utils.setProperty(props.context, props.systemPath, newValues);
|
|
76
|
-
}
|
|
77
|
-
}
|
|
78
|
-
});
|
|
79
|
-
|
|
80
|
-
// v-select doesn't fire a native change event, so Foundry's
|
|
81
|
-
// submitOnChange never persists a selection until another field
|
|
82
|
-
// changes. Apply the selection (the computed setter transforms it into
|
|
83
|
-
// the stored shape) and then persist that stored value directly.
|
|
84
|
-
const onChoicesChange = (newValues) => {
|
|
85
|
-
selectedValues.value = newValues;
|
|
86
|
-
if (document) {
|
|
87
|
-
document.update({ [props.systemPath]: foundry.utils.getProperty(props.context, props.systemPath) });
|
|
88
|
-
}
|
|
89
|
-
};
|
|
90
|
-
|
|
91
|
-
const fieldColor = computed(() => {
|
|
92
|
-
return props.color || 'primary';
|
|
93
|
-
});
|
|
94
|
-
|
|
95
|
-
const localizedLabel = computed(() => {
|
|
96
|
-
return game.i18n.localize(props.label);
|
|
97
|
-
});
|
|
98
|
-
|
|
99
|
-
const maxReached = computed(() => {
|
|
100
|
-
return props.maxSelections && selectedValues.value.length >= props.maxSelections;
|
|
101
|
-
});
|
|
102
|
-
|
|
103
|
-
const getTooltip = (item) => {
|
|
104
|
-
// The choice item might have additional system properties other than the core value, color, and icon.
|
|
105
|
-
// We want to build a tooltip of these additional values
|
|
106
|
-
const tooltipParts = [];
|
|
107
|
-
if (item.customKeys && item.customKeys.length > 0) {
|
|
108
|
-
for (const custom of item.customKeys) {
|
|
109
|
-
const value = custom.value;
|
|
110
|
-
if (value !== undefined) {
|
|
111
|
-
tooltipParts.push(\`\${custom.label}: \${value}\`);
|
|
112
|
-
}
|
|
113
|
-
}
|
|
114
|
-
}
|
|
115
|
-
return tooltipParts.join('<br>');
|
|
116
|
-
};
|
|
117
|
-
|
|
118
|
-
const getLabel = (label, icon) => {
|
|
119
|
-
const localized = game.i18n.localize(label);
|
|
120
|
-
if (icon) {
|
|
121
|
-
return \`<i class="\${icon}"></i> \${localized}\`;
|
|
122
|
-
}
|
|
123
|
-
return localized;
|
|
124
|
-
};
|
|
125
|
-
</script>
|
|
126
|
-
|
|
127
|
-
<template>
|
|
128
|
-
<div class="isdl-string-choices double-wide">
|
|
129
|
-
<!-- Simple choices field - uses v-select with multiple -->
|
|
130
|
-
<v-select
|
|
131
|
-
v-if="!props.isExtended"
|
|
132
|
-
:model-value="selectedValues"
|
|
133
|
-
@update:model-value="onChoicesChange"
|
|
134
|
-
:name="props.systemPath"
|
|
135
|
-
:items="props.items"
|
|
136
|
-
item-title="label"
|
|
137
|
-
item-value="value"
|
|
138
|
-
:disabled="disabled"
|
|
139
|
-
:color="fieldColor"
|
|
140
|
-
variant="outlined"
|
|
141
|
-
density="compact"
|
|
142
|
-
multiple
|
|
143
|
-
chips
|
|
144
|
-
clearable
|
|
145
|
-
>
|
|
146
|
-
<template #label>
|
|
147
|
-
<span class="field-label">
|
|
148
|
-
<v-icon v-if="props.icon" :icon="props.icon" size="small" class="me-1"></v-icon>
|
|
149
|
-
{{ localizedLabel }}
|
|
150
|
-
</span>
|
|
151
|
-
</template>
|
|
152
|
-
</v-select>
|
|
153
|
-
|
|
154
|
-
<!-- Extended choices field - uses v-select with custom templates, same style as choice<string> -->
|
|
155
|
-
<v-select
|
|
156
|
-
v-else
|
|
157
|
-
:model-value="selectedValues"
|
|
158
|
-
@update:model-value="onChoicesChange"
|
|
159
|
-
:name="props.systemPath"
|
|
160
|
-
:items="props.items"
|
|
161
|
-
item-title="label"
|
|
162
|
-
item-value="value"
|
|
163
|
-
:disabled="disabled"
|
|
164
|
-
:color="fieldColor"
|
|
165
|
-
variant="outlined"
|
|
166
|
-
density="compact"
|
|
167
|
-
multiple
|
|
168
|
-
chips
|
|
169
|
-
clearable
|
|
170
|
-
>
|
|
171
|
-
<template #label>
|
|
172
|
-
<span v-html="getLabel(props.label, props.icon)" />
|
|
173
|
-
</template>
|
|
174
|
-
|
|
175
|
-
<template v-slot:item="{ props: itemProps, item }">
|
|
176
|
-
<v-list-item
|
|
177
|
-
v-bind="itemProps"
|
|
178
|
-
:value="item.raw.value"
|
|
179
|
-
title=""
|
|
180
|
-
:disabled="maxReached && !selectedValues.includes(item.raw.value)"
|
|
181
|
-
>
|
|
182
|
-
<v-list-item-title>
|
|
183
|
-
<v-chip
|
|
184
|
-
label
|
|
185
|
-
:color="item.raw.color"
|
|
186
|
-
variant="elevated"
|
|
187
|
-
class="text-caption"
|
|
188
|
-
size="small"
|
|
189
|
-
:data-tooltip="getTooltip(item.raw)"
|
|
190
|
-
>
|
|
191
|
-
<span v-html="getLabel(item.raw.label, item.raw.icon)"></span>
|
|
192
|
-
</v-chip>
|
|
193
|
-
</v-list-item-title>
|
|
194
|
-
</v-list-item>
|
|
195
|
-
</template>
|
|
196
|
-
|
|
197
|
-
<template v-slot:chip="{ props: chipProps, item }">
|
|
198
|
-
<v-chip
|
|
199
|
-
v-bind="chipProps"
|
|
200
|
-
label
|
|
201
|
-
:color="item.raw.color"
|
|
202
|
-
variant="elevated"
|
|
203
|
-
class="text-caption"
|
|
204
|
-
size="small"
|
|
205
|
-
:data-tooltip="getTooltip(item.raw)"
|
|
206
|
-
>
|
|
207
|
-
<span v-html="getLabel(item.raw.label, item.raw.icon)"></span>
|
|
208
|
-
</v-chip>
|
|
209
|
-
</template>
|
|
210
|
-
</v-select>
|
|
211
|
-
</div>
|
|
212
|
-
</template>
|
|
10
|
+
const fileNode = expandToNode `
|
|
11
|
+
<script setup>
|
|
12
|
+
import { ref, computed, inject } from "vue";
|
|
13
|
+
|
|
14
|
+
const props = defineProps({
|
|
15
|
+
label: String,
|
|
16
|
+
systemPath: String,
|
|
17
|
+
context: Object,
|
|
18
|
+
icon: String,
|
|
19
|
+
color: String,
|
|
20
|
+
disabled: Boolean,
|
|
21
|
+
items: {
|
|
22
|
+
type: Array,
|
|
23
|
+
default: () => []
|
|
24
|
+
},
|
|
25
|
+
isExtended: {
|
|
26
|
+
type: Boolean,
|
|
27
|
+
default: false
|
|
28
|
+
},
|
|
29
|
+
maxSelections: Number,
|
|
30
|
+
primaryColor: String,
|
|
31
|
+
secondaryColor: String
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
const document = inject("rawDocument");
|
|
35
|
+
|
|
36
|
+
// Get the array of selected values
|
|
37
|
+
const selectedValues = computed({
|
|
38
|
+
get: () => {
|
|
39
|
+
const values = foundry.utils.getProperty(props.context, props.systemPath);
|
|
40
|
+
if (!Array.isArray(values)) return [];
|
|
41
|
+
|
|
42
|
+
if (props.isExtended) {
|
|
43
|
+
return values
|
|
44
|
+
.map(item => typeof item === 'object' && item !== null ? item.value : item)
|
|
45
|
+
.filter(v => v !== undefined && v !== null);
|
|
46
|
+
} else {
|
|
47
|
+
return values.filter(v => typeof v === 'string');
|
|
48
|
+
}
|
|
49
|
+
},
|
|
50
|
+
set: (newValues) => {
|
|
51
|
+
if (!Array.isArray(newValues)) {
|
|
52
|
+
foundry.utils.setProperty(props.context, props.systemPath, []);
|
|
53
|
+
return;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
if (props.isExtended) {
|
|
57
|
+
// For extended choices, create objects with value, icon, color from items
|
|
58
|
+
const selectedObjects = newValues.map(value => {
|
|
59
|
+
const item = props.items.find(i => i.value === value);
|
|
60
|
+
if (item) {
|
|
61
|
+
const obj = { value: item.value, icon: item.icon || "", color: item.color || "#ffffff" };
|
|
62
|
+
// Add any custom properties
|
|
63
|
+
if (item.customKeys) {
|
|
64
|
+
item.customKeys.forEach(custom => {
|
|
65
|
+
obj[custom.key.toLowerCase()] = custom.value;
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
return obj;
|
|
69
|
+
}
|
|
70
|
+
return { value, icon: "", color: "#ffffff" };
|
|
71
|
+
});
|
|
72
|
+
foundry.utils.setProperty(props.context, props.systemPath, selectedObjects);
|
|
73
|
+
} else {
|
|
74
|
+
// For simple choices, store the array of strings directly
|
|
75
|
+
foundry.utils.setProperty(props.context, props.systemPath, newValues);
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
// v-select doesn't fire a native change event, so Foundry's
|
|
81
|
+
// submitOnChange never persists a selection until another field
|
|
82
|
+
// changes. Apply the selection (the computed setter transforms it into
|
|
83
|
+
// the stored shape) and then persist that stored value directly.
|
|
84
|
+
const onChoicesChange = (newValues) => {
|
|
85
|
+
selectedValues.value = newValues;
|
|
86
|
+
if (document) {
|
|
87
|
+
document.update({ [props.systemPath]: foundry.utils.getProperty(props.context, props.systemPath) });
|
|
88
|
+
}
|
|
89
|
+
};
|
|
90
|
+
|
|
91
|
+
const fieldColor = computed(() => {
|
|
92
|
+
return props.color || 'primary';
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
const localizedLabel = computed(() => {
|
|
96
|
+
return game.i18n.localize(props.label);
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
const maxReached = computed(() => {
|
|
100
|
+
return props.maxSelections && selectedValues.value.length >= props.maxSelections;
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
const getTooltip = (item) => {
|
|
104
|
+
// The choice item might have additional system properties other than the core value, color, and icon.
|
|
105
|
+
// We want to build a tooltip of these additional values
|
|
106
|
+
const tooltipParts = [];
|
|
107
|
+
if (item.customKeys && item.customKeys.length > 0) {
|
|
108
|
+
for (const custom of item.customKeys) {
|
|
109
|
+
const value = custom.value;
|
|
110
|
+
if (value !== undefined) {
|
|
111
|
+
tooltipParts.push(\`\${custom.label}: \${value}\`);
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
return tooltipParts.join('<br>');
|
|
116
|
+
};
|
|
117
|
+
|
|
118
|
+
const getLabel = (label, icon) => {
|
|
119
|
+
const localized = game.i18n.localize(label);
|
|
120
|
+
if (icon) {
|
|
121
|
+
return \`<i class="\${icon}"></i> \${localized}\`;
|
|
122
|
+
}
|
|
123
|
+
return localized;
|
|
124
|
+
};
|
|
125
|
+
</script>
|
|
126
|
+
|
|
127
|
+
<template>
|
|
128
|
+
<div class="isdl-string-choices double-wide">
|
|
129
|
+
<!-- Simple choices field - uses v-select with multiple -->
|
|
130
|
+
<v-select
|
|
131
|
+
v-if="!props.isExtended"
|
|
132
|
+
:model-value="selectedValues"
|
|
133
|
+
@update:model-value="onChoicesChange"
|
|
134
|
+
:name="props.systemPath"
|
|
135
|
+
:items="props.items"
|
|
136
|
+
item-title="label"
|
|
137
|
+
item-value="value"
|
|
138
|
+
:disabled="disabled"
|
|
139
|
+
:color="fieldColor"
|
|
140
|
+
variant="outlined"
|
|
141
|
+
density="compact"
|
|
142
|
+
multiple
|
|
143
|
+
chips
|
|
144
|
+
clearable
|
|
145
|
+
>
|
|
146
|
+
<template #label>
|
|
147
|
+
<span class="field-label">
|
|
148
|
+
<v-icon v-if="props.icon" :icon="props.icon" size="small" class="me-1"></v-icon>
|
|
149
|
+
{{ localizedLabel }}
|
|
150
|
+
</span>
|
|
151
|
+
</template>
|
|
152
|
+
</v-select>
|
|
153
|
+
|
|
154
|
+
<!-- Extended choices field - uses v-select with custom templates, same style as choice<string> -->
|
|
155
|
+
<v-select
|
|
156
|
+
v-else
|
|
157
|
+
:model-value="selectedValues"
|
|
158
|
+
@update:model-value="onChoicesChange"
|
|
159
|
+
:name="props.systemPath"
|
|
160
|
+
:items="props.items"
|
|
161
|
+
item-title="label"
|
|
162
|
+
item-value="value"
|
|
163
|
+
:disabled="disabled"
|
|
164
|
+
:color="fieldColor"
|
|
165
|
+
variant="outlined"
|
|
166
|
+
density="compact"
|
|
167
|
+
multiple
|
|
168
|
+
chips
|
|
169
|
+
clearable
|
|
170
|
+
>
|
|
171
|
+
<template #label>
|
|
172
|
+
<span v-html="getLabel(props.label, props.icon)" />
|
|
173
|
+
</template>
|
|
174
|
+
|
|
175
|
+
<template v-slot:item="{ props: itemProps, item }">
|
|
176
|
+
<v-list-item
|
|
177
|
+
v-bind="itemProps"
|
|
178
|
+
:value="item.raw.value"
|
|
179
|
+
title=""
|
|
180
|
+
:disabled="maxReached && !selectedValues.includes(item.raw.value)"
|
|
181
|
+
>
|
|
182
|
+
<v-list-item-title>
|
|
183
|
+
<v-chip
|
|
184
|
+
label
|
|
185
|
+
:color="item.raw.color"
|
|
186
|
+
variant="elevated"
|
|
187
|
+
class="text-caption"
|
|
188
|
+
size="small"
|
|
189
|
+
:data-tooltip="getTooltip(item.raw)"
|
|
190
|
+
>
|
|
191
|
+
<span v-html="getLabel(item.raw.label, item.raw.icon)"></span>
|
|
192
|
+
</v-chip>
|
|
193
|
+
</v-list-item-title>
|
|
194
|
+
</v-list-item>
|
|
195
|
+
</template>
|
|
196
|
+
|
|
197
|
+
<template v-slot:chip="{ props: chipProps, item }">
|
|
198
|
+
<v-chip
|
|
199
|
+
v-bind="chipProps"
|
|
200
|
+
label
|
|
201
|
+
:color="item.raw.color"
|
|
202
|
+
variant="elevated"
|
|
203
|
+
class="text-caption"
|
|
204
|
+
size="small"
|
|
205
|
+
:data-tooltip="getTooltip(item.raw)"
|
|
206
|
+
>
|
|
207
|
+
<span v-html="getLabel(item.raw.label, item.raw.icon)"></span>
|
|
208
|
+
</v-chip>
|
|
209
|
+
</template>
|
|
210
|
+
</v-select>
|
|
211
|
+
</div>
|
|
212
|
+
</template>
|
|
213
213
|
`.appendNewLine();
|
|
214
214
|
fs.writeFileSync(generatedFilePath, toString(fileNode));
|
|
215
215
|
}
|