Skip to content

Commit 5b8809e

Browse files
vincentfretinclaude
andcommitted
Use the texture picker for map properties in the materials modal
Map-type properties of an <a-material> now use TextureWidget, opening the textures modal stacked on top of the materials modal. Modal gains stacking awareness: only the topmost open modal reacts to ESC and outside clicks, so interacting with the textures modal no longer closes the materials modal beneath it. ModalTextures is rendered after ModalMaterials so it paints on top, and material swatches refresh when textures finish loading. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
1 parent 19f6b4b commit 5b8809e

3 files changed

Lines changed: 40 additions & 15 deletions

File tree

src/components/Main.js

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -240,17 +240,19 @@ export default class Main extends React.Component {
240240
isOpen={this.state.isModalSponsorOpen}
241241
onClose={this.onCloseModalSponsor}
242242
/>
243-
<ModalTextures
244-
isOpen={this.state.isModalTexturesOpen}
245-
selectedTexture={this.state.selectedTexture}
246-
onClose={this.onModalTextureOnClose}
247-
/>
248243
<ModalMaterials
249244
isOpen={this.state.isModalMaterialsOpen}
250245
selectedMaterial={this.state.selectedMaterial}
251246
pickEnabled={!!this.state.materialOnClose}
252247
onClose={this.onModalMaterialsClose}
253248
/>
249+
{/* Rendered after ModalMaterials so it stacks on top when the textures
250+
modal is opened from a map property in the materials modal. */}
251+
<ModalTextures
252+
isOpen={this.state.isModalTexturesOpen}
253+
selectedTexture={this.state.selectedTexture}
254+
onClose={this.onModalTextureOnClose}
255+
/>
254256
</div>
255257
);
256258
}

src/components/modals/Modal.js

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ export default class Modal extends React.Component {
3131
handleGlobalKeydown = (event) => {
3232
if (
3333
this.state.isOpen &&
34+
this.isTopmostModal() &&
3435
(event.keyCode === 27 ||
3536
(this.props.extraCloseKeyCode &&
3637
event.keyCode === this.props.extraCloseKeyCode))
@@ -42,6 +43,16 @@ export default class Modal extends React.Component {
4243
}
4344
};
4445

46+
// Modals can be stacked (e.g., the textures modal opened from the materials
47+
// modal); only the topmost open modal should react to ESC or outside clicks.
48+
isTopmostModal = () => {
49+
const openModals = document.querySelectorAll('.modal:not(.hide)');
50+
return (
51+
openModals.length > 0 &&
52+
openModals[openModals.length - 1].contains(this.self.current)
53+
);
54+
};
55+
4556
shouldClickDismiss = (event) => {
4657
var target = event.target;
4758
// This piece of code isolates targets which are fake clicked by things
@@ -52,6 +63,10 @@ export default class Modal extends React.Component {
5263
if (target === this.self.current || this.self.current.contains(target)) {
5364
return false;
5465
}
66+
// Don't dismiss when interacting with another modal stacked on top.
67+
if (!this.isTopmostModal()) {
68+
return false;
69+
}
5570
return true;
5671
};
5772

src/components/modals/ModalMaterials.js

Lines changed: 18 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import ColorWidget from '../widgets/ColorWidget';
77
import InputWidget from '../widgets/InputWidget';
88
import NumberWidget from '../widgets/NumberWidget';
99
import SelectWidget from '../widgets/SelectWidget';
10+
import TextureWidget from '../widgets/TextureWidget';
1011
import Vec2Widget from '../widgets/Vec2Widget';
1112

1213
/**
@@ -41,6 +42,21 @@ export default class ModalMaterials extends React.Component {
4142
return null;
4243
}
4344

45+
componentDidMount() {
46+
// Textures load asynchronously; refresh swatches and widgets when they do.
47+
document.addEventListener('materialtextureloaded', this.onTextureLoaded);
48+
}
49+
50+
componentWillUnmount() {
51+
document.removeEventListener('materialtextureloaded', this.onTextureLoaded);
52+
}
53+
54+
onTextureLoaded = () => {
55+
if (this.state.isOpen) {
56+
this.forceUpdate();
57+
}
58+
};
59+
4460
componentDidUpdate(prevProps) {
4561
if (this.props.isOpen && !prevProps.isOpen) {
4662
this.refresh();
@@ -174,16 +190,8 @@ export default class ModalMaterials extends React.Component {
174190
} else if (propDef.oneOf && propDef.oneOf.length > 0) {
175191
widget = <SelectWidget {...widgetProps} options={propDef.oneOf} />;
176192
} else if (propDef.type === 'map') {
177-
// Commit on blur; a partial URL or selector is not worth loading.
178-
widget = (
179-
<InputWidget
180-
id={id}
181-
name={key}
182-
onBlur={onChange}
183-
value={value}
184-
schema={propDef}
185-
/>
186-
);
193+
// Opens the textures modal, stacked on top of this one.
194+
widget = <TextureWidget {...widgetProps} />;
187195
} else {
188196
switch (propDef.type) {
189197
case 'number': {

0 commit comments

Comments
 (0)