Skip to content

Commit

Permalink
✨ (admin) add link/unlink button to time selection fields
Browse files Browse the repository at this point in the history
  • Loading branch information
sophiamersmann committed Sep 20, 2024
1 parent 7c0efa3 commit 8de194d
Show file tree
Hide file tree
Showing 3 changed files with 250 additions and 82 deletions.
223 changes: 142 additions & 81 deletions adminSiteClient/EditorCustomizeTab.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import {
TextField,
Button,
RadioGroup,
BindAutoFloatExt,
} from "./Forms.js"
import {
debounce,
Expand All @@ -27,6 +28,8 @@ import {
SortOrder,
SortBy,
SortConfig,
minTimeBoundFromJSONOrNegativeInfinity,
maxTimeBoundFromJSONOrPositiveInfinity,
} from "@ourworldindata/utils"
import { faPlus, faMinus } from "@fortawesome/free-solid-svg-icons"
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome/index.js"
Expand All @@ -42,6 +45,73 @@ import { ErrorMessages } from "./ChartEditorTypes.js"
const debounceOnLeadingEdge = (fn: (...args: any[]) => void) =>
debounce(fn, 0, { leading: true, trailing: false })

@observer
class TimeField<
T extends { [field: string]: any },
K extends Extract<keyof T, string>,
> extends React.Component<{
field: K
store: T
label: string
defaultValue: number
parentValue: number
isInherited: boolean
allowLinking: boolean
}> {
private setValue(value: number) {
this.props.store[this.props.field] = value as any
}

@computed get currentValue(): number | undefined {
return this.props.store[this.props.field]
}

@action.bound onChange(value: number | undefined) {
this.setValue(value ?? this.props.defaultValue)
}

@action.bound onBlur() {
if (this.currentValue === undefined) {
this.setValue(this.props.defaultValue)
}
}

render() {
const { label, field, defaultValue } = this.props

// the reset button resets the value to its default
const resetButton = {
onClick: action(() => this.setValue(defaultValue)),
disabled: this.currentValue === defaultValue,
}

return this.props.allowLinking ? (
<BindAutoFloatExt
label={label}
readFn={(store) => store[field]}
writeFn={(store, newVal) =>
(store[this.props.field] = newVal as any)
}
auto={this.props.parentValue}
isAuto={this.props.isInherited}
store={this.props.store}
onBlur={this.onBlur}
resetButton={resetButton}
/>
) : (
<NumberField
label={label}
value={this.currentValue}
// invoke on the leading edge to avoid interference with onBlur
onValue={debounceOnLeadingEdge(this.onChange)}
onBlur={this.onBlur}
allowNegative
resetButton={resetButton}
/>
)
}
}

@observer
export class ColorSchemeSelector extends React.Component<{
grapher: Grapher
Expand Down Expand Up @@ -310,60 +380,6 @@ class TimelineSection<
return this.props.editor.grapher
}

@computed get minTime() {
return this.grapher.minTime
}
@computed get maxTime() {
return this.grapher.maxTime
}

@computed get timelineMinTime() {
return this.grapher.timelineMinTime
}
@computed get timelineMaxTime() {
return this.grapher.timelineMaxTime
}

@action.bound onMinTime(value: number | undefined) {
this.grapher.minTime = value ?? TimeBoundValue.negativeInfinity
}

@action.bound onMaxTime(value: number | undefined) {
this.grapher.maxTime = value ?? TimeBoundValue.positiveInfinity
}

@action.bound onBlurMinTime() {
if (this.minTime === undefined) {
this.grapher.minTime = TimeBoundValue.negativeInfinity
}
}

@action.bound onBlurMaxTime() {
if (this.maxTime === undefined) {
this.grapher.maxTime = TimeBoundValue.positiveInfinity
}
}

@action.bound onTimelineMinTime(value: number | undefined) {
this.grapher.timelineMinTime = value
}

@action.bound onBlurTimelineMinTime() {
if (this.grapher.timelineMinTime === undefined) {
this.grapher.timelineMinTime = TimeBoundValue.negativeInfinity
}
}

@action.bound onTimelineMaxTime(value: number | undefined) {
this.grapher.timelineMaxTime = value
}

@action.bound onBlurTimelineMaxTime() {
if (this.grapher.timelineMaxTime === undefined) {
this.grapher.timelineMaxTime = TimeBoundValue.positiveInfinity
}
}

@action.bound onToggleHideTimeline(value: boolean) {
this.grapher.hideTimeline = value || undefined
}
Expand All @@ -373,56 +389,77 @@ class TimelineSection<
}

render() {
const { features } = this.props.editor
const { editor } = this.props
const { features } = editor
const { grapher } = this

return (
<Section name="Timeline selection">
<FieldsRow>
{features.timeDomain && (
<NumberField
<TimeField
store={this.grapher}
field="minTime"
label="Selection start"
value={this.minTime}
// invoke on the leading edge to avoid interference with onBlur
onValue={debounceOnLeadingEdge(this.onMinTime)}
onBlur={this.onBlurMinTime}
allowNegative
defaultValue={TimeBoundValue.negativeInfinity}
parentValue={minTimeBoundFromJSONOrNegativeInfinity(
editor.activeParentConfig?.minTime
)}
isInherited={editor.isPropertyInherited("minTime")}
allowLinking={editor.couldPropertyBeInherited(
"minTime"
)}
/>
)}
<NumberField
<TimeField
store={this.grapher}
field="maxTime"
label={
features.timeDomain
? "Selection end"
: "Selected year"
}
value={this.maxTime}
// invoke on the leading edge to avoid interference with onBlur
onValue={debounceOnLeadingEdge(this.onMaxTime)}
onBlur={this.onBlurMaxTime}
allowNegative
defaultValue={TimeBoundValue.positiveInfinity}
parentValue={maxTimeBoundFromJSONOrPositiveInfinity(
editor.activeParentConfig?.maxTime
)}
isInherited={editor.isPropertyInherited("maxTime")}
allowLinking={editor.couldPropertyBeInherited(
"maxTime"
)}
/>
</FieldsRow>
{features.timelineRange && (
<FieldsRow>
<NumberField
<TimeField
store={this.grapher}
field="timelineMinTime"
label="Timeline min"
value={this.timelineMinTime}
// invoke on the leading edge to avoid interference with onBlur
onValue={debounceOnLeadingEdge(
this.onTimelineMinTime
defaultValue={TimeBoundValue.negativeInfinity}
parentValue={minTimeBoundFromJSONOrNegativeInfinity(
editor.activeParentConfig?.timelineMinTime
)}
isInherited={editor.isPropertyInherited(
"timelineMinTime"
)}
allowLinking={editor.couldPropertyBeInherited(
"timelineMinTime"
)}
onBlur={this.onBlurTimelineMinTime}
allowNegative
/>
<NumberField
<TimeField
store={this.grapher}
field="timelineMaxTime"
label="Timeline max"
value={this.timelineMaxTime}
// invoke on the leading edge to avoid interference with onBlur
onValue={debounceOnLeadingEdge(
this.onTimelineMaxTime
defaultValue={TimeBoundValue.positiveInfinity}
parentValue={maxTimeBoundFromJSONOrPositiveInfinity(
editor.activeParentConfig?.timelineMaxTime
)}
isInherited={editor.isPropertyInherited(
"timelineMaxTime"
)}
allowLinking={editor.couldPropertyBeInherited(
"timelineMaxTime"
)}
onBlur={this.onBlurTimelineMaxTime}
allowNegative
/>
</FieldsRow>
)}
Expand Down Expand Up @@ -538,6 +575,12 @@ export class EditorCustomizeTab<
onValue={(value) =>
(yAxisConfig.min = value)
}
resetButton={{
onClick: () =>
(yAxisConfig.min = undefined),
disabled:
yAxisConfig.min === undefined,
}}
allowDecimal
allowNegative
/>
Expand All @@ -547,6 +590,12 @@ export class EditorCustomizeTab<
onValue={(value) =>
(yAxisConfig.max = value)
}
resetButton={{
onClick: () =>
(yAxisConfig.max = undefined),
disabled:
yAxisConfig.max === undefined,
}}
allowDecimal
allowNegative
/>
Expand Down Expand Up @@ -611,6 +660,12 @@ export class EditorCustomizeTab<
onValue={(value) =>
(xAxisConfig.min = value)
}
resetButton={{
onClick: () =>
(xAxisConfig.min = undefined),
disabled:
xAxisConfig.min === undefined,
}}
allowDecimal
allowNegative
/>
Expand All @@ -620,6 +675,12 @@ export class EditorCustomizeTab<
onValue={(value) =>
(xAxisConfig.max = value)
}
resetButton={{
onClick: () =>
(xAxisConfig.max = undefined),
disabled:
xAxisConfig.max === undefined,
}}
allowDecimal
allowNegative
/>
Expand Down
Loading

0 comments on commit 8de194d

Please sign in to comment.