Skip to content

Commit 25d15fd

Browse files
authored
fix: zelos full block fields rendering badly (#7490)
* fix: text input fields * fix: colour field * chore: cleanup override keywords * chore: revert playground changes * fix: applyColour not being called * chore: swap visibility for display * chore: cleanup for PR comments * fix: PR comments
1 parent 00d870e commit 25d15fd

4 files changed

Lines changed: 201 additions & 78 deletions

File tree

core/block.ts

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -939,6 +939,19 @@ export class Block implements IASTNodeLocation, IDeletable {
939939
return this.disposed;
940940
}
941941

942+
/**
943+
* @returns True if this block is a value block with a single editable field.
944+
* @internal
945+
*/
946+
isSimpleReporter(): boolean {
947+
if (!this.outputConnection) return false;
948+
949+
for (const input of this.inputList) {
950+
if (input.connection || input.fieldRow.length > 1) return false;
951+
}
952+
return true;
953+
}
954+
942955
/**
943956
* Find the connection on this block that corresponds to the given connection
944957
* on the other block.

core/field.ts

Lines changed: 17 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -330,6 +330,18 @@ export abstract class Field<T = any>
330330
*/
331331
initModel() {}
332332

333+
/**
334+
* Defines whether this field should take up the full block or not.
335+
*
336+
* Be cautious when overriding this function. It may not work as you expect /
337+
* intend because the behavior was kind of hacked in. If you are thinking
338+
* about overriding this function, post on the forum with your intended
339+
* behavior to see if there's another approach.
340+
*/
341+
protected isFullBlockField(): boolean {
342+
return !this.borderRect_;
343+
}
344+
333345
/**
334346
* Create a field border rect element. Not to be overridden by subclasses.
335347
* Instead modify the result of the function inside initView, or create a
@@ -797,7 +809,7 @@ export abstract class Field<T = any>
797809
const xOffset =
798810
margin !== undefined
799811
? margin
800-
: this.borderRect_
812+
: !this.isFullBlockField()
801813
? this.getConstants()!.FIELD_BORDER_RECT_X_PADDING
802814
: 0;
803815
let totalWidth = xOffset * 2;
@@ -813,7 +825,7 @@ export abstract class Field<T = any>
813825
);
814826
totalWidth += contentWidth;
815827
}
816-
if (this.borderRect_) {
828+
if (!this.isFullBlockField()) {
817829
totalHeight = Math.max(totalHeight, constants!.FIELD_BORDER_RECT_HEIGHT);
818830
}
819831

@@ -922,7 +934,7 @@ export abstract class Field<T = any>
922934
throw new UnattachedFieldError();
923935
}
924936

925-
if (!this.borderRect_) {
937+
if (this.isFullBlockField()) {
926938
// Browsers are inconsistent in what they return for a bounding box.
927939
// - Webkit / Blink: fill-box / object bounding box
928940
// - Gecko: stroke-box
@@ -940,8 +952,8 @@ export abstract class Field<T = any>
940952
xy.y -= 0.5 * scale;
941953
}
942954
} else {
943-
const bBox = this.borderRect_.getBoundingClientRect();
944-
xy = style.getPageOffset(this.borderRect_);
955+
const bBox = this.borderRect_!.getBoundingClientRect();
956+
xy = style.getPageOffset(this.borderRect_!);
945957
scaledWidth = bBox.width;
946958
scaledHeight = bBox.height;
947959
}

core/field_colour.ts

Lines changed: 110 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,12 @@ import * as browserEvents from './browser_events.js';
1919
import * as Css from './css.js';
2020
import * as dom from './utils/dom.js';
2121
import * as dropDownDiv from './dropdowndiv.js';
22-
import {Field, FieldConfig, FieldValidator} from './field.js';
22+
import {
23+
Field,
24+
FieldConfig,
25+
FieldValidator,
26+
UnattachedFieldError,
27+
} from './field.js';
2328
import * as fieldRegistry from './field_registry.js';
2429
import * as aria from './utils/aria.js';
2530
import * as colour from './utils/colour.js';
@@ -168,29 +173,118 @@ export class FieldColour extends Field<string> {
168173
this.getConstants()!.FIELD_COLOUR_DEFAULT_WIDTH,
169174
this.getConstants()!.FIELD_COLOUR_DEFAULT_HEIGHT,
170175
);
171-
if (!this.getConstants()!.FIELD_COLOUR_FULL_BLOCK) {
172-
this.createBorderRect_();
173-
this.getBorderRect().style['fillOpacity'] = '1';
174-
} else if (this.sourceBlock_ instanceof BlockSvg) {
175-
this.clickTarget_ = this.sourceBlock_.getSvgRoot();
176+
this.createBorderRect_();
177+
this.getBorderRect().style['fillOpacity'] = '1';
178+
this.getBorderRect().setAttribute('stroke', '#fff');
179+
if (this.isFullBlockField()) {
180+
this.clickTarget_ = (this.sourceBlock_ as BlockSvg).getSvgRoot();
176181
}
177182
}
178183

184+
protected override isFullBlockField(): boolean {
185+
const block = this.getSourceBlock();
186+
if (!block) throw new UnattachedFieldError();
187+
188+
const constants = this.getConstants();
189+
return block.isSimpleReporter() && !!constants?.FIELD_COLOUR_FULL_BLOCK;
190+
}
191+
179192
/**
180193
* Updates text field to match the colour/style of the block.
181194
*/
182195
override applyColour() {
183-
if (!this.getConstants()!.FIELD_COLOUR_FULL_BLOCK) {
184-
if (this.borderRect_) {
185-
this.borderRect_.style.fill = this.getValue() as string;
186-
}
187-
} else if (this.sourceBlock_ instanceof BlockSvg) {
188-
this.sourceBlock_.pathObject.svgPath.setAttribute(
189-
'fill',
190-
this.getValue() as string,
191-
);
192-
this.sourceBlock_.pathObject.svgPath.setAttribute('stroke', '#fff');
196+
const block = this.getSourceBlock() as BlockSvg | null;
197+
if (!block) throw new UnattachedFieldError();
198+
199+
if (!this.fieldGroup_) return;
200+
201+
const borderRect = this.borderRect_;
202+
if (!borderRect) {
203+
throw new Error('The border rect has not been initialized');
204+
}
205+
206+
if (!this.isFullBlockField()) {
207+
borderRect.style.display = 'block';
208+
borderRect.style.fill = this.getValue() as string;
209+
} else {
210+
borderRect.style.display = 'none';
211+
// In general, do *not* let fields control the color of blocks. Having the
212+
// field control the color is unexpected, and could have performance
213+
// impacts.
214+
block.pathObject.svgPath.setAttribute('fill', this.getValue() as string);
215+
block.pathObject.svgPath.setAttribute('stroke', '#fff');
216+
}
217+
}
218+
219+
/**
220+
* Returns the height and width of the field.
221+
*
222+
* This should *in general* be the only place render_ gets called from.
223+
*
224+
* @returns Height and width.
225+
*/
226+
override getSize(): Size {
227+
if (this.getConstants()?.FIELD_COLOUR_FULL_BLOCK) {
228+
// In general, do *not* let fields control the color of blocks. Having the
229+
// field control the color is unexpected, and could have performance
230+
// impacts.
231+
// Full block fields have more control of the block than they should
232+
// (i.e. updating fill colour). Whenever we get the size, the field may
233+
// no longer be a full-block field, so we need to rerender.
234+
this.render_();
235+
this.isDirty_ = false;
193236
}
237+
return super.getSize();
238+
}
239+
240+
/**
241+
* Updates the colour of the block to reflect whether this is a full
242+
* block field or not.
243+
*/
244+
protected override render_() {
245+
super.render_();
246+
247+
const block = this.getSourceBlock() as BlockSvg | null;
248+
if (!block) throw new UnattachedFieldError();
249+
// In general, do *not* let fields control the color of blocks. Having the
250+
// field control the color is unexpected, and could have performance
251+
// impacts.
252+
// Whenever we render, the field may no longer be a full-block-field so
253+
// we need to update the colour.
254+
if (this.getConstants()!.FIELD_COLOUR_FULL_BLOCK) block.applyColour();
255+
}
256+
257+
/**
258+
* Updates the size of the field based on whether it is a full block field
259+
* or not.
260+
*
261+
* @param margin margin to use when positioning the field.
262+
*/
263+
protected updateSize_(margin?: number) {
264+
const constants = this.getConstants();
265+
const xOffset =
266+
margin !== undefined
267+
? margin
268+
: !this.isFullBlockField()
269+
? constants!.FIELD_BORDER_RECT_X_PADDING
270+
: 0;
271+
let totalWidth = xOffset * 2;
272+
let contentWidth = 0;
273+
if (!this.isFullBlockField()) {
274+
contentWidth = constants!.FIELD_COLOUR_DEFAULT_WIDTH;
275+
totalWidth += contentWidth;
276+
}
277+
278+
let totalHeight = constants!.FIELD_TEXT_HEIGHT;
279+
if (!this.isFullBlockField()) {
280+
totalHeight = Math.max(totalHeight, constants!.FIELD_BORDER_RECT_HEIGHT);
281+
}
282+
283+
this.size_.height = totalHeight;
284+
this.size_.width = totalWidth;
285+
286+
this.positionTextElement_(xOffset, contentWidth);
287+
this.positionBorderRect_();
194288
}
195289

196290
/**
@@ -206,26 +300,6 @@ export class FieldColour extends Field<string> {
206300
return colour.parse(newValue);
207301
}
208302

209-
/**
210-
* Update the value of this colour field, and update the displayed colour.
211-
*
212-
* @param newValue The value to be saved. The default validator guarantees
213-
* that this is a colour in '#rrggbb' format.
214-
*/
215-
protected override doValueUpdate_(newValue: string) {
216-
this.value_ = newValue;
217-
if (this.borderRect_) {
218-
this.borderRect_.style.fill = newValue;
219-
} else if (
220-
this.sourceBlock_ &&
221-
this.sourceBlock_.rendered &&
222-
this.sourceBlock_ instanceof BlockSvg
223-
) {
224-
this.sourceBlock_.pathObject.svgPath.setAttribute('fill', newValue);
225-
this.sourceBlock_.pathObject.svgPath.setAttribute('stroke', '#fff');
226-
}
227-
}
228-
229303
/**
230304
* Get the text for this field. Used when the block is collapsed.
231305
*

0 commit comments

Comments
 (0)