Skip to content

Commit

Permalink
Merge branch 'lineup-v4' into sgratzl/datefilter
Browse files Browse the repository at this point in the history
  • Loading branch information
thinkh committed Apr 16, 2020
2 parents d962e3c + 9f0d7a0 commit 4ccfa9a
Show file tree
Hide file tree
Showing 7 changed files with 109 additions and 29 deletions.
18 changes: 12 additions & 6 deletions src/internal/drag.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import {cssClass} from '../styles';

/** @internal */
export interface IDragHandleOptions {
Expand Down Expand Up @@ -38,6 +39,8 @@ export function dragHandle(handle: HTMLElement | SVGElement, options: Partial<ID
minDelta: 2
}, options);

let ueberElement: HTMLElement | null = null;

// converts the given x coordinate to be relative to the given element
const toContainerRelative = (x: number, elem: HTMLElement | SVGElement) => {
const rect = elem.getBoundingClientRect();
Expand Down Expand Up @@ -73,9 +76,10 @@ export function dragHandle(handle: HTMLElement | SVGElement, options: Partial<ID
evt.preventDefault();

const end = toContainerRelative(evt.clientX, o.container) - handleShift;
o.container.removeEventListener('mousemove', <any>mouseMove);
o.container.removeEventListener('mouseup', <any>mouseUp);
o.container.removeEventListener('mouseleave', <any>mouseUp);
ueberElement!.removeEventListener('mousemove', <any>mouseMove);
ueberElement!.removeEventListener('mouseup', <any>mouseUp);
ueberElement!.removeEventListener('mouseleave', <any>mouseUp);
ueberElement!.classList.remove(cssClass('dragging'));

if (Math.abs(start - end) < 2) {
//ignore
Expand All @@ -96,9 +100,11 @@ export function dragHandle(handle: HTMLElement | SVGElement, options: Partial<ID
start = last = toContainerRelative(evt.clientX, o.container) - handleShift;

// register other event listeners
o.container.addEventListener('mousemove', <any>mouseMove);
o.container.addEventListener('mouseup', <any>mouseUp);
o.container.addEventListener('mouseleave', <any>mouseUp);
ueberElement = <HTMLElement>handle.closest('body') || <HTMLElement>handle.closest(`.${cssClass()}`)!; // take the whole body or root lineup
ueberElement.addEventListener('mousemove', <any>mouseMove);
ueberElement.addEventListener('mouseup', <any>mouseUp);
ueberElement.addEventListener('mouseleave', <any>mouseUp);
ueberElement.classList.add(cssClass('dragging'));

o.onStart(handle, start, 0, evt);
};
Expand Down
4 changes: 4 additions & 0 deletions src/styles/_dialogs.scss
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,10 @@
float: right;
color: darken($lu_toolbar_color_base, 20%); // slightly darker primary action
}

&[title=Remove] {
color: darken($lu_toolbar_color_base, 20%); // slightly darker primary action
}
}

.#{$lu_css_prefix}-dialog-sub-nested {
Expand Down
36 changes: 31 additions & 5 deletions src/styles/_mappingeditor.scss
Original file line number Diff line number Diff line change
Expand Up @@ -31,18 +31,21 @@ div.#{$lu_css_prefix}-dialog-mapper-script {

.#{$lu_css_prefix}-dialog-mapper-domain,
.#{$lu_css_prefix}-dialog-mapper-range {
transform: translate(0, 10px);
transform: translate(0, 24px);
display: flex;
align-items: center;
justify-content: space-between;
margin: 0 14px;
border: 3px solid $lu_mapping_box;
padding: 0.4em 2px;
border-bottom: 3px solid $lu_mapping_box;
padding: 0.4em 2px 20px;
text-align: center;
}

.#{$lu_css_prefix}-dialog-mapper-range {
transform: translate(0, -10px);
padding: 20px 2px 0.4em;
transform: translate(0, -24px);
border-bottom: none;
border-top: 3px solid $lu_mapping_box;
justify-content: space-around;
}

Expand All @@ -59,7 +62,7 @@ svg.#{$lu_css_prefix}-dialog-mapper-details {

line {
stroke: $lu_mapping_handle;
stroke-width: 1;
stroke-width: 0.8;
stroke-linejoin: round;
stroke-linecap: round;

Expand Down Expand Up @@ -100,6 +103,29 @@ g.#{$lu_css_prefix}-dialog-mapper-mapping {
stroke: $lu_mapping_hover;
}
}

&.#{$lu_css_prefix}-frozen > .#{$lu_css_prefix}-dialog-mapper-mapping-domain {
display: none;
}
}

.#{$lu_css_prefix}-dialog-mapper-mapping-domain {
pointer-events: none;
font-size: 4px;
}

.#{$lu_css_prefix}-dialog-mapper-mapping-range {
pointer-events: none;
font-size: 4px;
dominant-baseline: hanging;
}

.#{$lu_css_prefix}-dialog-mapper-mapping-right {
text-anchor: end;
}

.#{$lu_css_prefix}-dialog-mapper-mapping-middle {
text-anchor: middle;
}

.#{$lu_css_prefix}-dialog-mapper[data-scale=script] div.#{$lu_css_prefix}-dialog-mapper-script {
Expand Down
4 changes: 4 additions & 0 deletions src/styles/engine/_header.scss
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,10 @@
cursor: col-resize !important;
}

.#{$lu_css_prefix}-dragging {
cursor: ew-resize !important;
}

// FIXME
.#{$lu_css_prefix}-dialog-sub-nested > section {
margin-bottom: $lu_engine_grip_gap + 15px;
Expand Down
2 changes: 2 additions & 0 deletions src/styles/renderer/_histogram.scss
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,7 @@ span.#{$lu_css_prefix}-mapping-hint {
top: 0;
height: 100%;
border-right: 1px solid black;
transform: translate(-1px, 0);
transition: border-width 0.2s ease;
width: 1px;
cursor: ew-resize;
Expand All @@ -104,6 +105,7 @@ span.#{$lu_css_prefix}-mapping-hint {
.#{$lu_css_prefix}-histogram-max {
right: 0;
border-right: none;
transform: translate(1px, 0);
border-left: 1px solid black;
}

Expand Down
23 changes: 13 additions & 10 deletions src/ui/dialogs/MappingDialog.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,12 +22,14 @@ export default class MappingDialog extends ADialog {
private readonly mappingAdapter: IMappingAdapter = {
destroyed: (self: MappingLine) => {
this.mappingLines.splice(this.mappingLines.indexOf(self), 1);
this.updateLines(this.computeScale());
},
updated: () => this.updateLines(this.computeScale()),
domain: () => this.rawDomain,
normalizeRaw: this.normalizeRaw.bind(this),
unnormalizeRaw: this.unnormalizeRaw.bind(this),
dialog: this.dialog
dialog: this.dialog,
formatter: this.column.getNumberFormat()
};

constructor(private readonly column: IMapAbleColumn, dialog: IDialogContext, ctx: IRankingHeaderContext) {
Expand Down Expand Up @@ -94,11 +96,11 @@ export default class MappingDialog extends ADialog {
<input id="${this.idPrefix}max" required type="number" value="${round(this.rawDomain[1], 3)}" step="any">
</div>
<svg class="${cssClass('dialog-mapper-details')}" viewBox="0 0 106 66">
<g transform="translate(3,3)">
<g transform="translate(3,7)">
<rect y="-3" width="100" height="10">
<title>Click to create a new mapping line</title>
</rect>
<rect y="53" width="100" height="10">
<rect y="49" width="100" height="10">
<title>Click to create a new mapping line</title>
</rect>
</g>
Expand Down Expand Up @@ -195,14 +197,14 @@ export default class MappingDialog extends ADialog {
this.data.then((values) => {
values.forEach((v) => {
if (!isMissingValue(v)) {
g.insertAdjacentHTML('afterbegin', `<line data-v="${v}" x1="${round(this.normalizeRaw(v), 2)}" x2="${round(this.scale.apply(v) * 100, 2)}" y2="60"></line>`);
g.insertAdjacentHTML('afterbegin', `<line data-v="${v}" x1="${round(this.normalizeRaw(v), 2)}" x2="${round(this.scale.apply(v) * 100, 2)}" y2="52"></line>`);
}
});
});
}

private createMappings() {
this.mappingLines.splice(0, this.mappingLines.length).forEach((d) => d.destroy());
this.mappingLines.splice(0, this.mappingLines.length).forEach((d) => d.destroy(true));
if (!(this.scale instanceof ScaleMappingFunction)) {
return;
}
Expand All @@ -221,9 +223,8 @@ export default class MappingDialog extends ADialog {
if (scaleType === 'script') {
(<HTMLTextAreaElement>this.find('textarea')).value = (<ScriptMappingFunction>this.scale).code;
}
const domain = this.scale.domain;
this.forEach(`input[type=number]`, (d: HTMLInputElement, i) => {
d.value = String(domain[i]);
d.value = round(this.rawDomain[i], 3).toString();
});
}

Expand All @@ -236,11 +237,12 @@ export default class MappingDialog extends ADialog {
}

protected reset() {
this.scale = this.column.getOriginalMapping();
this.rawDomain = <[number, number]>this.scale.domain.slice();
this.scale = this.column.getOriginalMapping().clone();
const domain = this.scale.domain;
this.rawDomain = [domain[0], domain[domain.length - 1]];
this.update();
this.updateLines();
this.createMappings();
this.updateLines();
}

private copyMapping(columnId: string) {
Expand All @@ -252,6 +254,7 @@ export default class MappingDialog extends ADialog {
this.scale = ref.getMapping().clone();
this.rawDomain = <[number, number]>this.scale.domain.slice();
this.update();
this.createMappings();
this.updateLines();
}

Expand Down
51 changes: 43 additions & 8 deletions src/ui/dialogs/MappingLineDialog.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ export interface IMappingAdapter {
unnormalizeRaw(v: number): number;

dialog: IDialogContext;

formatter(v: number): string;
}

/** @internal */
Expand All @@ -39,9 +41,9 @@ export default class MappingLineDialog extends ADialog {
build(node: HTMLElement) {
const domain = this.adapter.domain();
node.insertAdjacentHTML('beforeend', `
<button class="${cssClass('dialog-button')} lu-action-remove" type="button" ${this.line.frozen ? 'style="display: none"' : ''} ><span style="margin-left: 3px">Remove Mapping Line</span></button>
<button class="${cssClass('dialog-button')} lu-action-remove" title="Remove" type="button" ${this.line.frozen ? 'style="display: none"' : ''} ><span style="margin-left: 3px">Remove Mapping Line</span></button>
<strong>Input Domain Value (min ... max)</strong>
<input type="number" value="${round(this.adapter.unnormalizeRaw(this.line.domain), 3)}" ${this.line.frozen ? 'readonly disabled' : ''} autofocus required min="${domain[0]}" max="${domain[1]}" step="any">
<input type="number" value="${this.adapter.formatter(this.adapter.unnormalizeRaw(this.line.domain))}" ${this.line.frozen ? 'readonly disabled' : ''} autofocus required min="${domain[0]}" max="${domain[1]}" step="any">
<strong>Output Normalized Value (0 ... 1)</strong>
<input type="number" value="${round(this.line.range / 100, 3)}" required min="0" max="1" step="any">
`);
Expand Down Expand Up @@ -85,11 +87,18 @@ export class MappingLine {
readonly node: SVGGElement;

constructor(g: SVGGElement, public domain: number, public range: number, private readonly adapter: IMappingAdapter) {
const h = 52;
g.insertAdjacentHTML('beforeend', `<g class="${cssClass('dialog-mapper-mapping')}" transform="translate(${domain},0)">
<line x1="0" x2="${range - domain}" y2="60"></line>
<line x1="0" x2="${range - domain}" y2="60"></line>
<circle r="2.5"></circle>
<circle cx="${range - domain}" cy="60" r="2.5"></circle>
<line x1="0" x2="${range - domain}" y2="${h}"></line>
<line x1="0" x2="${range - domain}" y2="${h}"></line>
<circle r="2"></circle>
<circle cx="${range - domain}" cy="${h}" r="2"></circle>
<text class="${cssClass('dialog-mapper-mapping-domain')} ${domain > 25 && domain < 75 ? cssClass('dialog-mapper-mapping-middle') : ''}${domain > 75 ? cssClass('dialog-mapper-mapping-right') : ''}" dy="-3">
${this.adapter.formatter(this.adapter.unnormalizeRaw(domain))}
</text>
<text class="${cssClass('dialog-mapper-mapping-range')} ${range > 25 && range < 75 ? cssClass('dialog-mapper-mapping-middle') : ''}${range > 50 ? cssClass('dialog-mapper-mapping-right') : ''}" dy="3" x="${range - domain}" y="${h}">
${round(range / 100, 3)}
</text>
<title>Drag the anchor circle to change the mapping, double click to edit</title>
</g>`);
this.node = <SVGGElement>g.lastElementChild!;
Expand Down Expand Up @@ -175,12 +184,27 @@ export class MappingLine {
return this.node.classList.contains(cssClass('frozen'));
}

destroy() {
destroy(handled = false) {
this.node.remove();
this.adapter.destroyed(this);
if (!handled) {
this.adapter.destroyed(this);
}
}

update(domain: number, range: number, trigger = false) {
if (similar(domain, 100)) {
domain = 100;
}
if (similar(domain, 0)) {
domain = 0;
}
if (similar(range, 100)) {
range = 100;
}
if (similar(range, 0)) {
range = 0;
}

if (similar(domain, this.domain) && similar(range, this.range)) {
return;
}
Expand All @@ -194,6 +218,17 @@ export class MappingLine {
Array.from(this.node.querySelectorAll<SVGLineElement>('line')).forEach((d) => d.setAttribute('x2', String(shift)));
this.node.querySelector<SVGCircleElement>('circle[cx]')!.setAttribute('cx', String(shift));

const t1 = this.node.querySelector<SVGTextElement>('text')!;
t1.textContent = this.adapter.formatter(this.adapter.unnormalizeRaw(domain));
t1.classList.toggle(cssClass('dialog-mapper-mapping-right'), domain > 75);
t1.classList.toggle(cssClass('dialog-mapper-mapping-middle'), domain >= 25 && domain <= 75);

const t2 = this.node.querySelector<SVGTextElement>('text[x]')!;
t2.textContent = round(range / 100, 3).toString();
t2.classList.toggle(cssClass('dialog-mapper-mapping-right'), range > 75);
t2.classList.toggle(cssClass('dialog-mapper-mapping-middle'), range >= 25 && range <= 75);
t2.setAttribute('x', String(shift));

if (trigger) {
this.adapter.updated(this);
}
Expand Down

0 comments on commit 4ccfa9a

Please sign in to comment.