Skip to content

Instantly share code, notes, and snippets.

@anandthakker
Last active April 17, 2018 19:55
Show Gist options
  • Save anandthakker/5d5a18392a099ff25479bad9dca01311 to your computer and use it in GitHub Desktop.
Save anandthakker/5d5a18392a099ff25479bad9dca01311 to your computer and use it in GitHub Desktop.
/* @flow */
type MutablePropertyValues<Props: {[string]: mixed}>
= $Exact<$ObjMap<Props, <T,R>(p: Property<T, R>) => PropertyValue<T, R>>>
type PropertyValues<Props: {[string]: mixed}>
= $ReadOnly<MutablePropertyValues<Props>>;
interface Layout<Props: {[string]: mixed}> {
getValue<S: string>(name: S): $ElementType<PropertyValues<Props>, S>;
getMutable(): MutableLayout<Props>;
}
interface MutableLayout<Props: {[string]: mixed}> extends Layout<Props> {
setValue<S: string>(name: S, value: $ElementType<PropertyValues<Props>, S>): void;
}
class Layer {
layout: Layout<any>;
constructor(layout: *) {
this.layout = layout;
}
setLayoutProperty(name: string, value: any) {
// Direct mutation not allowed:
// this.layout.setValue(name, value);
// Also not allowed:
// this.layout._values[name] = value;
// Instead:
const nextLayout = this.layout.getMutable();
nextLayout.setValue(name, value);
this.layout = nextLayout;
}
}
class LayoutImpl<Props: {[string]: mixed}> implements Layout<Props>, MutableLayout<Props> {
_properties: Properties<Props>;
_values: MutablePropertyValues<Props>;
constructor(properties: Properties<Props>) {
this._properties = properties;
this._values = cloneAsMutable(properties._defaultValues);
}
getValue<S: string>(name: S): $ElementType<PropertyValues<Props>, S> {
return this._values[name];
}
setValue<S: string>(name: S, value: $ElementType<PropertyValues<Props>, S>) {
this._values[name] = value;
}
getMutable(): MutableLayout<Props> {
const copy = new LayoutImpl(this._properties);
copy._values = cloneAsMutable(this._values);
return copy;
}
}
class BasicProperty<T> implements Property<T, T> { constructor(value: T) {} }
class Properties<Props: {[string]: mixed}> {
_properties: Props;
_defaultValues: PropertyValues<Props>;
constructor(properties: Props) { /* ... */ }
}
const layoutProperties = new Properties({
size: new BasicProperty(0),
anchor: new BasicProperty('top')
});
const layer = new Layer(new LayoutImpl(layoutProperties));
layer.setLayoutProperty('size', 2);
const prevLayout = layer.layout;
layer.setLayoutProperty('size', 3);
console.log(prevLayout.getValue('size')); // => 2
console.log(layer.layout.getValue('size')); // => 3
console.log(prevLayout === layer.layout); // => false
// ---------------------------------------------------------------------------
interface Property<T, R> { /* ... */ }
class PropertyValue<T, R> {
property: Property<T, R>;
value: PropertyValueSpecification<T>;
constructor(property: *, value: *) {
this.property = property;
this.value = value;
}
/* ... */
}
declare type CameraFunctionSpecification<T> =
| {| type: 'exponential', stops: Array<[number, T]> |}
| {| type: 'interval', stops: Array<[number, T]> |};
declare type PropertyValueSpecification<T> =
| T
| CameraFunctionSpecification<T>
| Array<mixed>;
function cloneAsMutable<T: Object>(x: $ReadOnly<T>): T {
const result = {};
for (let key in x) {
result[key] = x[key];
}
return (result: any);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment