Skip to content

Commit 0ab4199

Browse files
committed
✨ Improve item display item input and undo history
1 parent ab3dcc6 commit 0ab4199

4 files changed

Lines changed: 111 additions & 110 deletions

File tree

src/components/vanillaItemDisplayElementPanel.svelte

Lines changed: 42 additions & 80 deletions
Original file line numberDiff line numberDiff line change
@@ -1,101 +1,63 @@
11
<script lang="ts" context="module">
2+
import { validateItem } from 'src/util/minecraftUtil'
23
import { ITEM_DISPLAY_ITEM_DISPLAY_SELECT } from '../interface/panel/vanillaItemDisplayElement'
34
import { VanillaItemDisplay } from '../outliner/vanillaItemDisplay'
4-
import EVENTS from '../util/events'
5-
import { Valuable } from '../util/stores'
65
import { translate } from '../util/translation'
76
</script>
87

98
<script lang="ts">
10-
let selectedDisplay = VanillaItemDisplay.selected.at(0)
11-
let lastSelected = selectedDisplay
12-
13-
let item = new Valuable<string>('')
14-
let error = new Valuable<string>('')
15-
let itemDisplaySlot: HTMLDivElement
16-
let visible = false
17-
18-
let unsub: (() => void) | undefined
19-
20-
EVENTS.UPDATE_SELECTION.subscribe(() => {
21-
selectedDisplay = VanillaItemDisplay.selected.at(0)
22-
if (!selectedDisplay || selected.length > 1) {
23-
item = new Valuable('')
24-
error = new Valuable('')
25-
visible = false
26-
return
27-
}
28-
$item = selectedDisplay.item
29-
error = selectedDisplay.error
30-
visible = true
31-
})
32-
33-
EVENTS.UPDATE_SELECTION.subscribe(() => {
34-
unsub?.()
35-
36-
lastSelected = selectedDisplay
37-
selectedDisplay = VanillaItemDisplay.selected.at(0)
38-
39-
if (!selectedDisplay) {
40-
visible = false
41-
return
9+
export let selected: VanillaItemDisplay
10+
11+
let item = selected.item
12+
let error = selected.error
13+
14+
$: {
15+
$error = ''
16+
if (selected.item !== item) {
17+
void validateItem(item)
18+
.then(err => {
19+
if (err) {
20+
$error = err
21+
console.log('Item validation error:', err)
22+
return
23+
}
24+
console.log('Changing item to', item)
25+
Undo.initEdit({ elements: [selected] })
26+
27+
selected.item = item
28+
Project!.saved = false
29+
30+
Undo.finishEdit(`Change Item Display Item to "${item}"`, {
31+
elements: [selected],
32+
})
33+
})
34+
.catch(err => {
35+
$error = err.message
36+
})
4237
}
38+
}
4339
44-
$item = selectedDisplay.item
45-
error = selectedDisplay.error
46-
ITEM_DISPLAY_ITEM_DISPLAY_SELECT.set(selectedDisplay.itemDisplay)
47-
visible = true
48-
49-
unsub = item.subscribe(value => {
50-
if (selectedDisplay == undefined || selectedDisplay !== lastSelected) {
51-
lastSelected = selectedDisplay
52-
return
53-
}
54-
if (value === selectedDisplay.item) return
55-
56-
Undo.initEdit({ elements: VanillaItemDisplay.selected })
57-
58-
if (VanillaItemDisplay.selected.length > 1) {
59-
for (const display of VanillaItemDisplay.selected) {
60-
display.item = value
61-
}
62-
} else {
63-
selectedDisplay.item = value
64-
}
65-
Project!.saved = false
66-
67-
Undo.finishEdit(`Change Item Display's Item to "${$item}"`, {
68-
elements: VanillaItemDisplay.selected,
69-
})
70-
})
71-
})
72-
73-
requestAnimationFrame(() => {
74-
itemDisplaySlot.appendChild(ITEM_DISPLAY_ITEM_DISPLAY_SELECT.node)
75-
})
40+
const mountItemDisplaySelect = (node: HTMLDivElement) => {
41+
node.appendChild(ITEM_DISPLAY_ITEM_DISPLAY_SELECT.node)
42+
}
7643
</script>
7744

78-
<p class="panel_toolbar_label label" style={!!visible ? '' : 'visibility:hidden; height: 0px;'}>
45+
<p class="panel_toolbar_label label">
7946
{translate('panel.vanilla_item_display.title')}
8047
</p>
8148

82-
<div
83-
class="toolbar custom-toolbar"
84-
style={!!visible ? '' : 'visibility:hidden; height: 0px;'}
85-
title={translate('panel.vanilla_item_display.description')}
86-
>
49+
<div class="toolbar custom-toolbar" title={translate('panel.vanilla_item_display.description')}>
8750
<div class="content" style="width: 95%;">
88-
<input type="text" bind:value={$item} />
51+
<input type="text" bind:value={item} />
8952
</div>
90-
<div class="content" bind:this={itemDisplaySlot}></div>
53+
<div class="content" use:mountItemDisplaySelect></div>
9154
</div>
9255

93-
<div
94-
class="error"
95-
style={!!$error ? '' : 'visibility:hidden; height: 0px; color: var(--color-error);'}
96-
>
97-
{$error}
98-
</div>
56+
{#if $error}
57+
<div class="error">
58+
{$error}
59+
</div>
60+
{/if}
9961

10062
<style>
10163
input {

src/interface/panel/vanillaItemDisplayElement.ts

Lines changed: 39 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,49 @@
1-
import { registerMountSvelteComponentMod } from 'src/util/mountSvelteComponent'
1+
import { registerProjectMod } from 'src/util/moddingTools'
2+
import { mountSvelteComponent } from 'src/util/mountSvelteComponent'
23
import VanillaItemDisplayElementPanel from '../../components/vanillaItemDisplayElementPanel.svelte'
34
import { PACKAGE } from '../../constants'
4-
import { activeProjectIsBlueprintFormat } from '../../formats/blueprint'
5+
import { activeProjectIsBlueprintFormat, BLUEPRINT_FORMAT_ID } from '../../formats/blueprint'
56
import { type ItemDisplayMode, VanillaItemDisplay } from '../../outliner/vanillaItemDisplay'
67
import EVENTS from '../../util/events'
78
import { translate } from '../../util/translation'
89

9-
registerMountSvelteComponentMod({
10+
let mounted: VanillaItemDisplayElementPanel | null = null
11+
12+
const destroyMounted = () => {
13+
mounted?.$destroy()
14+
mounted = null
15+
}
16+
17+
const updatePanel = () => {
18+
destroyMounted()
19+
const itemDisplay = VanillaItemDisplay.selected.at(0)
20+
if (itemDisplay) {
21+
mounted = mountSvelteComponent({
22+
component: VanillaItemDisplayElementPanel,
23+
props: { selected: itemDisplay },
24+
target: '#panel_element',
25+
})
26+
}
27+
}
28+
29+
registerProjectMod({
1030
id: 'animated-java:append-element-panel/vanilla-item-display',
11-
component: VanillaItemDisplayElementPanel,
12-
target: '#panel_element',
31+
32+
condition: project => project.format.id === BLUEPRINT_FORMAT_ID,
33+
34+
apply: () => {
35+
const unsubscribers = [
36+
EVENTS.UNDO.subscribe(updatePanel),
37+
EVENTS.REDO.subscribe(updatePanel),
38+
EVENTS.UPDATE_SELECTION.subscribe(updatePanel),
39+
]
40+
return { unsubscribers }
41+
},
42+
43+
revert: ({ unsubscribers }) => {
44+
unsubscribers.forEach(u => u())
45+
destroyMounted()
46+
},
1347
})
1448

1549
export const ITEM_DISPLAY_ITEM_DISPLAY_SELECT = new BarSelect(

src/outliner/vanillaItemDisplay.ts

Lines changed: 13 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,9 @@
11
import { getItemModel } from 'src/systems/minecraft/itemModelManager'
2+
import { validateItem } from 'src/util/minecraftUtil'
23
import { registerAction } from 'src/util/moddingTools'
34
import { PACKAGE } from '../constants'
45
import { type IBlueprintBoneConfigJSON, activeProjectIsBlueprintFormat } from '../formats/blueprint'
56
import { BoneConfig } from '../nodeConfigs'
6-
import { MINECRAFT_REGISTRY } from '../systems/minecraft/registryManager'
7-
import { getCurrentVersion } from '../systems/minecraft/versionManager'
87
import EVENTS from '../util/events'
98
import { Valuable } from '../util/stores'
109
import { translate } from '../util/translation'
@@ -74,29 +73,8 @@ export class VanillaItemDisplay extends ResizableOutlinerElement {
7473

7574
this.sanitizeName()
7675

77-
const updateItem = (newItem: string) => {
78-
if (!MINECRAFT_REGISTRY.item) {
79-
requestAnimationFrame(() => updateItem(newItem))
80-
return
81-
}
82-
let [namespace, id] = newItem.split(':')
83-
if (!id) {
84-
id = namespace
85-
namespace = 'minecraft'
86-
}
87-
if (
88-
(namespace === 'minecraft' || namespace === '') &&
89-
MINECRAFT_REGISTRY.item.has(id)
90-
) {
91-
this.error.set('')
92-
this.preview_controller.updateGeometry(this)
93-
} else {
94-
this.error.set(`This item does not exist in Minecraft ${getCurrentVersion()!.id}.`)
95-
}
96-
}
97-
98-
this.__item.subscribe(value => {
99-
updateItem(value)
76+
this.__item.subscribe(() => {
77+
void this.updateItem()
10078
})
10179
}
10280

@@ -182,6 +160,16 @@ export class VanillaItemDisplay extends ResizableOutlinerElement {
182160
TickUpdates.selection = true
183161
this.preview_controller.updateHighlight(this)
184162
}
163+
164+
async updateItem() {
165+
const error = await validateItem(this.item)
166+
if (error) {
167+
this.error.set(error)
168+
return
169+
}
170+
this.error.set('')
171+
this.preview_controller.updateGeometry(this)
172+
}
185173
}
186174
VanillaItemDisplay.prototype.icon = VanillaItemDisplay.icon
187175
new Property(VanillaItemDisplay, 'string', 'item', { default: 'minecraft:diamond' })

src/util/minecraftUtil.ts

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -263,6 +263,23 @@ export interface IParsedBlock {
263263
blockStateRegistryEntry: BlockStateRegistryEntry | undefined
264264
}
265265

266+
export async function validateItem(item: string) {
267+
if (!MINECRAFT_REGISTRY.item) {
268+
await assetsLoaded()
269+
return
270+
}
271+
let [namespace, id] = item.split(':')
272+
if (!id) {
273+
id = namespace
274+
namespace = 'minecraft'
275+
}
276+
if ((namespace === 'minecraft' || namespace === '') && MINECRAFT_REGISTRY.item.has(id)) {
277+
return ''
278+
} else {
279+
return `This item does not exist in Minecraft ${getCurrentVersion()!.id}.`
280+
}
281+
}
282+
266283
export async function validateBlock(block: string) {
267284
if (!MINECRAFT_REGISTRY.block) await assetsLoaded()
268285
const parsed = await parseBlock(block)

0 commit comments

Comments
 (0)