-
Notifications
You must be signed in to change notification settings - Fork 1.1k
Add standard decorators to @lit-labs/context #4009
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
174118b
936d4a4
cf5e7f4
dc88822
8e54d06
f75b0d1
ed700f3
aacfa06
d41208a
f9e9950
6e22756
16e0af9
0f5a543
4bf3f6b
d5e6fc9
70c34e9
5106169
fcaca77
b482f33
b5459e3
4913342
9d33f8b
d54a3d0
f162b3c
db073ac
1eb08b5
7f91515
f251a6b
724306b
35fb53c
a26a59e
04c306c
1574500
0713495
e6dabc0
439dffb
f32bc2f
4712d3d
74694f8
d0f66b7
7cb1472
8c671d1
e707e68
c42e980
2d451c1
e7760ee
5593b88
707d69b
5a1c895
4896728
562f044
7f5ce4e
37eb5b0
bf8ca6a
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -3,4 +3,5 @@ | |
| /node_modules/ | ||
| /lib/ | ||
| /index.* | ||
| /context.* | ||
| /legacy-decorators.* | ||
| /std-decorators.* | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,2 @@ | ||
| export * from './lib/decorators/consume.js'; | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. add a comment about keeping this in sync with decorators.ts? and do we need a license header? |
||
| export * from './lib/decorators/provide.js'; | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,68 @@ | ||
| /** | ||
| * @license | ||
| * Copyright 2023 Google LLC | ||
| * SPDX-License-Identifier: BSD-3-Clause | ||
| */ | ||
|
|
||
| import {ReactiveElement} from '@lit/reactive-element'; | ||
| import {ContextConsumer} from '../controllers/context-consumer.js'; | ||
| import {Context} from '../create-context.js'; | ||
|
|
||
| /** | ||
| * A property decorator that adds a ContextConsumer controller to the component | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Do we want to get more specific with the decorator type? "A field and accessor decorator" maybe? |
||
| * which will try and retrieve a value for the property via the Context API. | ||
| * | ||
| * @param context A Context identifier value created via `createContext` | ||
| * @param subscribe An optional boolean which when true allows the value to be | ||
| * updated multiple times. | ||
| * | ||
| * @example | ||
| * | ||
| * ```ts | ||
| * import {consume} from '@lit-labs/context/std-decorators/consume.js'; | ||
| * import {loggerContext, Logger} from 'community-protocols/logger'; | ||
| * | ||
| * class MyElement { | ||
| * @consume({context: loggerContext}) | ||
| * logger?: Logger; | ||
| * | ||
| * doThing() { | ||
| * this.logger?.log('thing was done'); | ||
| * } | ||
| * } | ||
| * ``` | ||
| * @category Decorator | ||
| */ | ||
| export function consume<Provided>({ | ||
| context, | ||
| subscribe, | ||
| }: { | ||
| context: Context<unknown, Provided>; | ||
| subscribe?: boolean; | ||
| }) { | ||
| return <This extends ReactiveElement, Consuming>( | ||
| _target: unknown, | ||
| decoratorContext: | ||
| | ClassFieldDecoratorContext<This, Consuming> | ||
| | ClassAccessorDecoratorContext<This, Consuming> | ||
| ): EnforceTypesMatch<Provided, Consuming, void> => { | ||
| decoratorContext.addInitializer(function (this: This) { | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is cool! I'm looking forward to getting rid of our own |
||
| new ContextConsumer(this, { | ||
| context, | ||
| callback: (value: Provided) => { | ||
| decoratorContext.access.set(this, value as unknown as Consuming); | ||
| }, | ||
| subscribe, | ||
| }); | ||
| }); | ||
| return undefined as unknown as EnforceTypesMatch<Provided, Consuming, void>; | ||
| }; | ||
| } | ||
|
|
||
| type EnforceTypesMatch<Provided, Consuming, Ok> = [Provided] extends [Consuming] | ||
| ? Ok | ||
| : { | ||
| message: "the provided type isn't assignable to the consuming field"; | ||
| provided: Provided; | ||
| consuming: Consuming; | ||
| }; | ||
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
| @@ -0,0 +1,100 @@ | ||||||
| /** | ||||||
| * @license | ||||||
| * Copyright 2017 Google LLC | ||||||
| * SPDX-License-Identifier: BSD-3-Clause | ||||||
| */ | ||||||
|
|
||||||
| import {ReactiveElement} from '@lit/reactive-element'; | ||||||
| import {Context} from '../create-context.js'; | ||||||
| import {ContextProvider} from '../controllers/context-provider.js'; | ||||||
|
|
||||||
| /* | ||||||
| * IMPORTANT: For compatibility with tsickle and the Closure JS compiler, all | ||||||
| * property decorators (but not class decorators) in this file that have | ||||||
| * an @ExportDecoratedItems annotation must be defined as a regular function, | ||||||
| * not an arrow function. | ||||||
| */ | ||||||
|
|
||||||
| /** | ||||||
| * A property decorator that adds a ContextConsumer controller to the component | ||||||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
Not sure fi this needs to be done in the existing decorators too |
||||||
| * which will try and retrieve a value for the property via the Context API. | ||||||
| * | ||||||
| * @param context A Context identifier value created via `createContext` | ||||||
| * @param multiple An optional boolean which when true allows the value to be updated | ||||||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. remove this param |
||||||
| * multiple times. | ||||||
| * | ||||||
| * See: https://developer.mozilla.org/en-US/docs/Web/API/Document/querySelector | ||||||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. remove |
||||||
| * | ||||||
| * @example | ||||||
| * | ||||||
| * ```ts | ||||||
| * import {consume} from '@lit-labs/context'; | ||||||
| * import {loggerContext} from 'community-protocols/logger'; | ||||||
| * | ||||||
| * class MyElement { | ||||||
| * @provide({context: loggerContext}) | ||||||
| * logger; | ||||||
| * | ||||||
| * doThing() { | ||||||
| * this.logger.log('thing was done'); | ||||||
| * } | ||||||
| * } | ||||||
| * ``` | ||||||
| * @category Decorator | ||||||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. should we make a new category for standard decorators? |
||||||
| */ | ||||||
| export function provide<Expected>({ | ||||||
| context: context, | ||||||
| }: { | ||||||
| context: Context<unknown, Expected>; | ||||||
| }) { | ||||||
| return <This extends ReactiveElement, Providing>( | ||||||
| target: ClassAccessorDecoratorTarget<This, Providing>, | ||||||
| decoratorContext: ClassAccessorDecoratorContext<This, Providing> | ||||||
| ): EnforceTypesMatch< | ||||||
| Expected, | ||||||
| Providing, | ||||||
| ClassAccessorDecoratorResult<This, Providing> | ||||||
| > => { | ||||||
| const controllerMap = new WeakMap< | ||||||
| This, | ||||||
| ContextProvider<Context<unknown, Providing>> | ||||||
| >(); | ||||||
| decoratorContext.addInitializer(function (this: This) { | ||||||
| controllerMap.set( | ||||||
| this, | ||||||
| new ContextProvider( | ||||||
| this, | ||||||
| // this cast is ok because we're already using the type system to | ||||||
| // ensure that Providing is assignable to Expected | ||||||
| {context: context as unknown as Context<unknown, Providing>} | ||||||
| ) | ||||||
| ); | ||||||
| }); | ||||||
| const result: ClassAccessorDecoratorResult<This, Providing> = { | ||||||
| get(this: This) { | ||||||
| return target.get.call(this); | ||||||
| }, | ||||||
| set(this: This, value: Providing) { | ||||||
| controllerMap.get(this)?.setValue(value); | ||||||
| return target.set.call(this, value); | ||||||
| }, | ||||||
| init(this: This, value: Providing) { | ||||||
| controllerMap.get(this)?.setValue(value); | ||||||
| return value; | ||||||
| }, | ||||||
| }; | ||||||
| return result as unknown as EnforceTypesMatch< | ||||||
| Expected, | ||||||
| Providing, | ||||||
| ClassAccessorDecoratorResult<This, Providing> | ||||||
| >; | ||||||
| }; | ||||||
| } | ||||||
|
|
||||||
| type EnforceTypesMatch<Expected, Providing, Ok> = [Providing] extends [Expected] | ||||||
| ? Ok | ||||||
| : { | ||||||
| message: "the providing field isn't assignable to this context"; | ||||||
| providing: Providing; | ||||||
| context: Expected; | ||||||
| }; | ||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,2 @@ | ||
| export * from './lib/std-decorators/consume.js'; | ||
| export * from './lib/std-decorators/provide.js'; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
is this needed now?