Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions .eslintignore
Original file line number Diff line number Diff line change
Expand Up @@ -18,3 +18,7 @@
/.node_modules.ember-try/
/bower.json.ember-try
/package.json.ember-try

# type definitions
*.d.ts
example.ts
31 changes: 31 additions & 0 deletions foo.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import Component from "@glimmer/component";
import { dedupeTracked, localCopy, trackedReset } from ".";

export default class Foo extends Component<{
breakfast: boolean;
timeOfDay: number;
}> {
@dedupeTracked someData = 0;

@localCopy((component: Foo, key, prev: "potato" | "waffles") => {
return prev === "potato" && component.args.breakfast ? prev : "waffles";
})
blah = "neato";

@localCopy<Foo>("args.whatUp") potato = "hello";

@localCopy<Foo>("args.breakfast") myBreakfast = true;

@localCopy<Foo>("blah") bar = true;

@trackedReset<Foo>("args.breakfast") cool = "yeah";

@trackedReset<Foo, number>({
memo: "potato",
update: (component, key, last: number) =>
component.args.timeOfDay < 12 ? last + 1 : last,
})
heyo = 0;
}

type X = AllNestedPropsOf<Foo>;
87 changes: 87 additions & 0 deletions index.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
import Component from "@glimmer/component";

type Join<K extends string, L extends string> = `${K}.${L}`;

type AllNestedPropsOf<
T extends unknown,
K extends keyof T = keyof T
> = K extends string
? T[K] extends Record<string, unknown>
? K | Join<K, AllNestedPropsOf<T[K]>>
: K
: never;

type UsefulPropsOf<C extends Component> = Exclude<
AllNestedPropsOf<C>,
Exclude<AllNestedPropsOf<Component>, "args">
>;

/**
* Turns a field in a deduped `@tracked` property. If you set the field to the
* same value as it is currently, it will not notify a property change (thus,
* deduping property changes). Otherwise, it is exactly the same as `@tracked`.
*/
export const dedupeTracked: PropertyDecorator;

/**
* Creates a local copy of a remote value. The local copy can be updated
* locally, but will also update if the remote value ever changes.
*
* @param memo The path to a "remote" property to track.
* @param initializer This will be used if the remote value is `undefined`. If
* the initializer is a function, it will be called and its return value will
* be used as the default value.
*/
export function localCopy<C extends Component = Component, T = unknown>(
memo: UsefulPropsOf<C>,
initializer?: T | (() => T)
): PropertyDecorator;
/**
* Creates a local copy of a remote value. The local copy can be updated
* locally, but will also update if the remote value ever changes.
*
* @param getterFunction You can provide a getter function instead of a path to
* the decorator, which receives the object, key, and last as arguments. This
* allows you to get a remote value using more complex logic. You can also use
* this to do more complex logic for checking the value for changes, and for
* deriving the local value
*/
export function localCopy<C extends Component = Component, T = unknown>(
getterFunction: (component: C, key: UsefulPropsOf<C>, last: T) => T
): PropertyDecorator;

interface TrackedResetConfig<C extends Component = Component, T = unknown> {
/**
* A path to a "remote" property to use to indicate when to reset to the
* default value, or a function which returns the default value.
*/
memo: UsefulPropsOf<C> | (() => T);

/**
* A function which can be used to provide a different value than the original
* on updates.
*/
update: (component: C, key: string, last: T) => T;
}

/**
* Similar to @localCopy, but instead of copying the remote value, it will reset
* to the class field's default value when another value changes.
*
* @param memo The path to a "remote" property to use to indicate when to reset
* to the default value.
*/
export function trackedReset<C extends Component>(
memo: UsefulPropsOf<C>
): PropertyDecorator;

/**
* Similar to @localCopy, but instead of copying the remote value, it will reset
* to the class field's default value when another value changes.

* @param config a configuration object with an update function, which can be
* used to provide a different value than the original on updates
*/
export function trackedReset<C extends Component = Component, T = unknown>(
config: TrackedResetConfig<C, T>
): PropertyDecorator;
4 changes: 3 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@
"ember-try": "^1.4.0",
"eslint-plugin-ember": "^7.7.1",
"eslint-plugin-node": "^10.0.0",
"expect-type": "^0.11.0",
"loader.js": "^4.7.0",
"qunit-dom": "^0.9.2",
"release-it": "^13.6.0",
Expand Down Expand Up @@ -84,5 +85,6 @@
"release": true,
"tokenRef": "GITHUB_AUTH"
}
}
},
"types": "index.d.ts"
}
7 changes: 7 additions & 0 deletions tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"compilerOptions": {
"target": "es2020",
"strict": true
},
"files": "example.ts"
}
6 changes: 6 additions & 0 deletions type-tests/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"compilerOptions": {
"strict": true,
"paths": { "tracked-toolbox": [".."], "tracked-toolbox/*": ["../*"] }
}
}
Loading