@api-client/ui 0.1.6 → 0.1.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.vscode/settings.json +1 -0
- package/build/src/core/Activity.d.ts +2 -2
- package/build/src/core/Activity.d.ts.map +1 -1
- package/build/src/core/Activity.js +2 -2
- package/build/src/core/Activity.js.map +1 -1
- package/build/src/core/ActivityManager.d.ts +29 -1
- package/build/src/core/ActivityManager.d.ts.map +1 -1
- package/build/src/core/ActivityManager.js +69 -45
- package/build/src/core/ActivityManager.js.map +1 -1
- package/build/src/core/Application.d.ts.map +1 -1
- package/build/src/core/Application.js +2 -2
- package/build/src/core/Application.js.map +1 -1
- package/build/src/core/ModalActivity.js +1 -1
- package/build/src/core/ModalActivity.js.map +1 -1
- package/build/src/reactive/reactive.d.ts.map +1 -1
- package/build/src/reactive/reactive.js +43 -1
- package/build/src/reactive/reactive.js.map +1 -1
- package/demo/elements/md/notification/snack.html +1 -1
- package/demo/elements/user/user-avatar.ts +4 -0
- package/eslint.config.js +3 -0
- package/package.json +1 -1
- package/src/core/Activity.ts +2 -2
- package/src/core/ActivityManager.ts +60 -36
- package/src/core/Application.ts +2 -2
- package/src/core/ModalActivity.ts +1 -1
- package/src/reactive/reactive.ts +43 -1
- package/test/core/activity.spec.ts +2 -2
- package/test/core/activity_manager.spec.ts +25 -25
- package/test/core/application.spec.ts +3 -3
- package/test/core/fragment.spec.ts +2 -2
- package/test/core/live_data.spec.ts +33 -3
- package/test/elements/data-table/DataTable.browser.test.ts +0 -3
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Application.js","sourceRoot":"","sources":["../../../src/core/Application.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAuB,MAAM,KAAK,CAAA;AAClD,OAAO,EAAE,eAAe,EAAE,
|
|
1
|
+
{"version":3,"file":"Application.js","sourceRoot":"","sources":["../../../src/core/Application.ts"],"names":[],"mappings":"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","sourcesContent":["import { nothing, type TemplateResult } from 'lit'\nimport { ActivityManager, ActivityLifecycle } from './ActivityManager.js'\nimport type { Activity } from './Activity.js'\nimport { ApplicationRenderer } from './renderer/ApplicationRenderer.js'\nimport { ThemeManager } from './ThemeManager.js'\n\n/**\n * Allows to fine-tune the update request.\n * For example, if a `Fragment` wants to update only itself, then\n * list other types should be set to false.\n */\nexport interface UpdateRequest {\n /**\n * Whether the whole application should be updated.\n * Default to true.\n */\n app?: boolean\n /**\n * Whether the activity should be updated.\n * Default to true.\n */\n activity?: boolean\n /**\n * Whether the fragment should be updated.\n * Default to true.\n */\n fragment?: boolean\n}\n/**\n * The application represents an application screen that hosts\n * Activities. Activities are managed by the Activity manager.\n *\n * An application is a separate app within the ecosystem. For example,\n * the file browser is one application. The domain designer is another.\n *\n * Each application manages it's own lifecycle. The application is rendered\n * by the lit's library `render()` function. Activities can call application's\n * main `requestUpdate()` function to render the entire application.\n *\n * Activities have the render function that renders its UI.\n * The Activity's UI can be composed of fragments, but this is optional.\n * When an activity uses fragments, they receive control over rendering their\n * UIs. An activity implementation has to take care of passing HTMLElement\n * references to their Fragments to make that happen. Otherwise, the activity\n * has to communicate with their fragments to request to re-render the application.\n */\nexport abstract class Application extends EventTarget {\n private activityUpdateListener = this.handleActivityUpdate.bind(this)\n\n #activities: ActivityManager\n\n #theme: ThemeManager\n\n /**\n * Application's theme manager.\n */\n get theme(): ThemeManager {\n return this.#theme\n }\n\n /**\n * Returns the application's activity manager.\n */\n get manager(): ActivityManager {\n return this.#activities\n }\n\n #events: EventTarget\n\n /**\n * The application main event bus.\n * All activities and fragments use it to communicate.\n * This way, events do not leak outside the application.\n */\n get events(): EventTarget {\n return this.#events\n }\n\n #renderer: ApplicationRenderer\n\n get renderer(): ApplicationRenderer {\n return this.#renderer\n }\n\n /**\n * @type A promise resolved when the render finished.\n */\n get updateComplete(): Promise<void> | undefined {\n return this.#renderer.updateComplete\n }\n\n /**\n * @type True when the application was first rendered.\n */\n get firstRendered(): boolean {\n return this.#renderer.firstRendered\n }\n\n /**\n * @param renderRoot The element or a CSS query to the element where the application will be rendered.\n */\n constructor(renderRoot: HTMLElement | string) {\n super()\n this.#events = new EventTarget()\n this.#activities = new ActivityManager(this)\n this.#renderer = new ApplicationRenderer(this)\n this.#renderer.setRenderRoot(renderRoot)\n this.#theme = new ThemeManager()\n\n // Listen for activity updates\n this.#events.addEventListener('activity:update', this.activityUpdateListener as EventListener)\n }\n\n /**\n * @see {@link ./Renderer.js}\n */\n public setRenderRoot(renderRoot: HTMLElement | string): void {\n this.#renderer.setRenderRoot(renderRoot)\n }\n\n /**\n * Called when the application starts.\n * This is where you should start the initial Activity using\n * `ActivityManager.startActivity(...)`.\n */\n abstract onStart(): void | Promise<void>\n\n /**\n * Called when the application is stopped/closed.\n * Perform cleanup here.\n */\n onStop(): void | Promise<void> {\n //\n }\n\n /**\n * Called when the application was rendered for the first time.\n * Call `super.onFirstRender()` when overriding this method.\n */\n onFirstRender(): void {\n this.#events.dispatchEvent(new Event('app:first-render'))\n }\n\n /**\n * Called when the application was rendered.\n */\n onRendered(): void {\n //\n }\n\n /**\n * The main render method for the Application. Called whenever an activity\n * requests an update. Renders the currently active Activity's content.\n *\n * If you override this method then any activity won't be rendered.\n *\n * @returns The Lit template to render.\n */\n render(): TemplateResult | typeof nothing {\n const currentActivity = this.#activities.getCurrentActivity()\n // Only render if started or beyond\n if (currentActivity && currentActivity.lifecycle >= ActivityLifecycle.Started) {\n return currentActivity.render()\n }\n return nothing\n }\n\n /**\n * Handles activity update requests and re-renders the application.\n */\n private handleActivityUpdate(e: CustomEvent<UpdateRequest | undefined>): void {\n this.requestUpdate(e.detail || undefined)\n }\n\n /**\n * Requests an update of the application UI. This will call the `render()` method\n * and update the DOM.\n */\n requestUpdate(opts: UpdateRequest = {}): void {\n const { app = true } = opts\n if (app) {\n this.#renderer.requestUpdate()\n }\n }\n\n /**\n * Starts the application by calling onStart and performing an initial render.\n */\n public async run(): Promise<void> {\n await this.onStart()\n this.requestUpdate()\n }\n\n /**\n * Stops the application, performing cleanup if necessary.\n */\n public async stop(): Promise<void> {\n this.#events.removeEventListener('activity:update', this.activityUpdateListener as EventListener)\n // Allow subclasses to clean up\n await this.onStop()\n // ... any other application-level cleanup\n }\n\n getCurrentActivity(): Activity | undefined {\n return this.#activities.getCurrentActivity()\n }\n\n /**\n * Unifies Fragment and Application interfaces.\n * @returns `this`\n */\n getApplication() {\n return this\n }\n\n findActiveActivity(id: string): Activity | undefined {\n return this.#activities.findActiveActivity(id)\n }\n}\n"]}
|
|
@@ -49,7 +49,7 @@ let ModalActivity = (() => {
|
|
|
49
49
|
formId = __runInitializers(this, _opened_extraInitializers);
|
|
50
50
|
onResume() {
|
|
51
51
|
this.renderRoot = document.createElement('div');
|
|
52
|
-
this.renderRoot.classList.add('modal-
|
|
52
|
+
this.renderRoot.classList.add('modal-activity-container');
|
|
53
53
|
document.body.appendChild(this.renderRoot);
|
|
54
54
|
super.onResume();
|
|
55
55
|
this.requestUpdate();
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ModalActivity.js","sourceRoot":"","sources":["../../../src/core/ModalActivity.ts"],"names":[],"mappings":";AAAA,OAAO,EAAE,IAAI,EAAuB,MAAM,KAAK,CAAA;AAC/C,OAAO,EAAE,SAAS,EAAE,MAAM,8BAA8B,CAAA;AACxD,OAAO,EAAE,QAAQ,EAAE,MAAM,6BAA6B,CAAA;AACtD,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAA;AAExC,OAAO,KAAK,MAAM,wBAAwB,CAAA;AAC1C,OAAO,EAAE,YAAY,EAAE,MAAM,sBAAsB,CAAA;AAEnD,OAAO,2BAA2B,CAAA;AAClC,OAAO,gCAAgC,CAAA;AAavC;;;;;;;;;GASG;IACmB,aAAa;sBAAS,QAAQ;;;;iBAA9B,aAAc,SAAQ,WAAQ;;;kCAQjD,KAAK,EAAE;YAAC,uKAAS,MAAM,6BAAN,MAAM,uFAAO;;;QAP/B;;WAEG;QACO,aAAa,GAA4B,EAAE,CAAA;QAI5C,yEAAkB,IAAI;QAE/B;;WAEG;UAJ4B;QAH/B;;WAEG;QACM,IAAS,MAAM,4CAAO;QAAtB,IAAS,MAAM,kDAAO;QAE/B;;WAEG;QACO,MAAM,sDAAS;QAEhB,QAAQ;YACf,IAAI,CAAC,UAAU,GAAG,QAAQ,CAAC,aAAa,CAAC,KAAK,CAAC,CAAA;YAC/C,IAAI,CAAC,UAAU,CAAC,SAAS,CAAC,GAAG,CAAC,
|
|
1
|
+
{"version":3,"file":"ModalActivity.js","sourceRoot":"","sources":["../../../src/core/ModalActivity.ts"],"names":[],"mappings":"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","sourcesContent":["import { html, type TemplateResult } from 'lit'\nimport { ifDefined } from 'lit/directives/if-defined.js'\nimport { classMap } from 'lit/directives/class-map.js'\nimport { Activity } from './Activity.js'\nimport type { UiDialogClosingReason } from '../md/dialog/internals/Dialog.js'\nimport state from '../decorators/state.js'\nimport { IntentResult } from './ActivityManager.js'\n\nimport '../md/dialog/ui-dialog.js'\nimport '../md/button/ui-text-button.js'\n\nexport interface ActionButtonInit {\n /**\n * The label to use for the button.\n */\n label?: string\n /**\n * Whether the button is disabled.\n */\n disabled?: boolean\n}\n\n/**\n * A special kind of activity that renders content in a modal dialog.\n * When a modal activity is running the underlying activities are paused.\n *\n * A modal activity uses the UiDialogElement to render its content. The dialog\n * is inserted directly into the `<body>` element of the document and rendered\n * in the top layer. Because of that, the modal activity does not support\n * regular rendering logic. It creates its own render root that is used to render\n * the template.\n */\nexport abstract class ModalActivity extends Activity {\n /**\n * The list of classes to apply to the dialog.\n */\n protected dialogClasses: Record<string, boolean> = {}\n /**\n * Controls dialog visibility without controlling the activity state.\n */\n @state() accessor opened = true\n\n /**\n * The id of the form used to render the dialog.\n */\n protected formId?: string\n\n override onResume(): void | Promise<void> {\n this.renderRoot = document.createElement('div')\n this.renderRoot.classList.add('modal-activity-container')\n document.body.appendChild(this.renderRoot)\n super.onResume()\n this.requestUpdate()\n }\n\n override async finish(): Promise<void> {\n if (this.renderRoot) {\n document.body.removeChild(this.renderRoot)\n this.renderRoot = undefined\n }\n await super.finish()\n }\n\n protected handleDialogClosed(e: CustomEvent<UiDialogClosingReason>): void {\n const { cancelled, value } = e.detail\n if (cancelled) {\n this.handleNegativeAction(value)\n } else {\n this.handlePositiveAction(value)\n }\n }\n\n /**\n * Called when the dialog is closed with a negative action.\n * Override this function to handle the negative action.\n * @param value The value passed to the dialog when it is closed.\n */\n protected handleNegativeAction(value: unknown): void {\n this.setResult(IntentResult.RESULT_CANCELED, value)\n this.finish()\n }\n\n /**\n * Called when the dialog is closed with a positive action.\n * Override this function to handle the positive action.\n * @param value The value passed to the dialog when it is closed.\n */\n // eslint-disable-next-line @typescript-eslint/no-unused-vars\n protected handlePositiveAction(value: unknown): void {\n this.finish()\n }\n\n /**\n * Override this method to handle the form submission.\n * @param e The submit event.\n */\n protected handleSubmit(e: SubmitEvent): void {\n e.preventDefault()\n }\n\n /**\n * The function called by the rendered when rendering this dialog.\n * This way, the `render()` function is still used as the primary template provider.\n */\n renderDialog(): TemplateResult {\n const classes = {\n activityDialog: true,\n [this.constructor.name.toLowerCase()]: true,\n ...this.dialogClasses,\n }\n return html`<ui-dialog modal .open=\"${this.opened}\" class=\"${classMap(classes)}\" @close=\"${this.handleDialogClosed}\"\n >${this.render()}</ui-dialog\n >`\n }\n\n /**\n * Used with dialogs that render forms. Override the `handleSubmit` method\n * to handle the form submission.\n * @param content The content to render in the form.\n * @param name Optional form name\n */\n protected renderForm(content: TemplateResult, name?: string): TemplateResult {\n if (!this.formId) {\n this.formId = `form-${this.constructor.name.toLowerCase()}`\n }\n return html`<form\n id=\"${this.formId}\"\n name=\"${ifDefined(name)}\"\n class=\"dialog-content\"\n @submit=\"${this.handleSubmit}\"\n >\n ${content}\n </form>`\n }\n\n /**\n * Renders the cancel button for the dialog.\n * @param label The label to use for the cancel button.\n */\n protected renderNegativeButton(init: ActionButtonInit = {}): TemplateResult {\n const { label = 'Cancel' } = init\n return html`<ui-text-button slot=\"button\" type=\"button\" value=\"dismiss\">${label}</ui-text-button>`\n }\n\n /**\n * Renders the submit button for the dialog.\n * If the formId is set, the button will be a submit button.\n * Otherwise, it will be a regular button.\n * @param label The label to use for the submit button.\n */\n protected renderPositiveButton(init: ActionButtonInit = {}): TemplateResult {\n const { label = 'OK', disabled = false } = init\n const { formId } = this\n const type = formId ? 'submit' : 'button'\n // UI buttons have a proper support for form association.\n return html`<ui-text-button\n slot=\"button\"\n ?disabled=\"${disabled}\"\n type=\"${type}\"\n value=\"confirm\"\n form=\"${ifDefined(formId)}\"\n >${label}</ui-text-button\n >`\n }\n\n /**\n * Renders the title for the dialog.\n * @param title The title to render in the dialog.\n */\n protected renderTitle(title: string): TemplateResult {\n return html`<span slot=\"title\">${title}</span>`\n }\n}\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"reactive.d.ts","sourceRoot":"","sources":["../../../src/reactive/reactive.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,iBAAiB;IAChC,IAAI,EAAE,MAAM,CAAA;IACZ,KAAK,EAAE,OAAO,CAAA;IACd,QAAQ,EAAE,OAAO,CAAA;CAClB;AAOD,wBAAgB,cAAc,CAAC,CAAC,SAAS,MAAM,EAC7C,MAAM,EAAE,CAAC,EACT,QAAQ,EAAE,CAAC,MAAM,EAAE,iBAAiB,KAAK,IAAI,EAC7C,IAAI,SAAK,GACR,CAAC,
|
|
1
|
+
{"version":3,"file":"reactive.d.ts","sourceRoot":"","sources":["../../../src/reactive/reactive.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,iBAAiB;IAChC,IAAI,EAAE,MAAM,CAAA;IACZ,KAAK,EAAE,OAAO,CAAA;IACd,QAAQ,EAAE,OAAO,CAAA;CAClB;AAOD,wBAAgB,cAAc,CAAC,CAAC,SAAS,MAAM,EAC7C,MAAM,EAAE,CAAC,EACT,QAAQ,EAAE,CAAC,MAAM,EAAE,iBAAiB,KAAK,IAAI,EAC7C,IAAI,SAAK,GACR,CAAC,CA2GH;AAED;;;;;GAKG;AACH,wBAAgB,cAAc,CAAC,cAAc,EAAE,MAAM,GAAG,OAAO,CAQ9D"}
|
|
@@ -12,17 +12,59 @@ export function createReactive(target, onChange, path = '') {
|
|
|
12
12
|
}
|
|
13
13
|
const handler = {
|
|
14
14
|
get(target, prop, receiver) {
|
|
15
|
+
// Retrieve the original value. If 'prop' is a getter, 'receiver' (the proxy) is used as 'this'.
|
|
15
16
|
const value = Reflect.get(target, prop, receiver);
|
|
17
|
+
// If the retrieved value is a function, bind it to the original 'target' object.
|
|
18
|
+
// This ensures that methods (e.g., Set.prototype.add, Map.prototype.set) are called
|
|
19
|
+
// with the correct 'this' context, which is the original unwrapped object.
|
|
20
|
+
// Special handling for Set and Map mutating methods
|
|
21
|
+
if (target instanceof Set) {
|
|
22
|
+
if (['add', 'delete', 'clear'].includes(String(prop))) {
|
|
23
|
+
return (...args) => {
|
|
24
|
+
const oldValue = new Set(target); // shallow copy before mutation
|
|
25
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-function-type
|
|
26
|
+
const result = value.apply(target, args);
|
|
27
|
+
onChange({
|
|
28
|
+
path,
|
|
29
|
+
value: target,
|
|
30
|
+
oldValue,
|
|
31
|
+
});
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
if (target instanceof Map) {
|
|
37
|
+
if (['set', 'delete', 'clear'].includes(String(prop))) {
|
|
38
|
+
return (...args) => {
|
|
39
|
+
const oldValue = new Map(target); // shallow copy before mutation
|
|
40
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-function-type
|
|
41
|
+
const result = value.apply(target, args);
|
|
42
|
+
onChange({
|
|
43
|
+
path,
|
|
44
|
+
value: target,
|
|
45
|
+
oldValue,
|
|
46
|
+
});
|
|
47
|
+
return result;
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
if (typeof value === 'function' && !Array.isArray(target)) {
|
|
52
|
+
// If the value is a function, bind it to the original target object.
|
|
53
|
+
return value.bind(target);
|
|
54
|
+
}
|
|
55
|
+
// If the value is an object (and not null, and not a function due to the check above),
|
|
56
|
+
// then recursively create a reactive proxy for it.
|
|
16
57
|
if (typeof value === 'object' && value !== null) {
|
|
17
58
|
const finalPath = path ? `${path}.${String(prop)}` : String(prop);
|
|
18
59
|
return createReactive(value, onChange, finalPath);
|
|
19
60
|
}
|
|
61
|
+
// For primitive values or null, return the value directly.
|
|
20
62
|
return value;
|
|
21
63
|
},
|
|
22
64
|
set(target, prop, value, receiver) {
|
|
23
65
|
const oldValue = Reflect.get(target, prop, receiver);
|
|
24
66
|
const success = Reflect.set(target, prop, value); // do not set the receiver here
|
|
25
|
-
//
|
|
67
|
+
// The receiver is the proxy itself and if we set the value on the proxy, it will
|
|
26
68
|
// create an infinite loop.
|
|
27
69
|
if (success && oldValue !== value) {
|
|
28
70
|
// Check if the old value was proxied and remove it from the cache
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"reactive.js","sourceRoot":"","sources":["../../../src/reactive/reactive.ts"],"names":[],"mappings":"AAMA,kBAAkB;AAClB,MAAM,SAAS,GAAG,IAAI,OAAO,EAAkB,CAAA;AAC/C,kBAAkB;AAClB,MAAM,aAAa,GAAG,IAAI,OAAO,EAAkB,CAAA;AAEnD,MAAM,UAAU,cAAc,CAC5B,MAAS,EACT,QAA6C,EAC7C,IAAI,GAAG,EAAE;IAET,IAAI,SAAS,CAAC,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC;QAC1B,gBAAgB;QAChB,OAAO,SAAS,CAAC,GAAG,CAAC,MAAM,CAAM,CAAA,CAAC,wBAAwB;IAC5D,CAAC;IACD,IAAI,aAAa,CAAC,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC;QAC9B,OAAO,MAAM,CAAA;IACf,CAAC;IAED,MAAM,OAAO,GAAoB;QAC/B,GAAG,CAAC,MAAM,EAAE,IAAI,EAAE,QAAQ;YACxB,MAAM,KAAK,GAAG,OAAO,CAAC,GAAG,CAAC,MAAM,EAAE,IAAI,EAAE,QAAQ,CAAC,CAAA;YACjD,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,KAAK,IAAI,EAAE,CAAC;gBAChD,MAAM,SAAS,GAAG,IAAI,CAAC,CAAC,CAAC,GAAG,IAAI,IAAI,MAAM,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,CAAA;gBACjE,OAAO,cAAc,CAAC,KAAK,EAAE,QAAQ,EAAE,SAAS,CAAC,CAAA;YACnD,CAAC;YACD,OAAO,KAAK,CAAA;QACd,CAAC;QACD,GAAG,CAAC,MAAM,EAAE,IAAI,EAAE,KAAK,EAAE,QAAQ;YAC/B,MAAM,QAAQ,GAAG,OAAO,CAAC,GAAG,CAAC,MAAM,EAAE,IAAI,EAAE,QAAQ,CAAC,CAAA;YACpD,MAAM,OAAO,GAAG,OAAO,CAAC,GAAG,CAAC,MAAM,EAAE,IAAI,EAAE,KAAK,CAAC,CAAA,CAAC,+BAA+B;YAChF,
|
|
1
|
+
{"version":3,"file":"reactive.js","sourceRoot":"","sources":["../../../src/reactive/reactive.ts"],"names":[],"mappings":"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","sourcesContent":["export interface ChangeEventDetail {\n path: string\n value: unknown\n oldValue: unknown\n}\n\n// target -> Proxy\nconst cachedRaw = new WeakMap<object, object>()\n// Proxy -> target\nconst cachedProxies = new WeakMap<object, object>()\n\nexport function createReactive<T extends object>(\n target: T,\n onChange: (detail: ChangeEventDetail) => void,\n path = ''\n): T {\n if (cachedRaw.has(target)) {\n // return target\n return cachedRaw.get(target) as T // Return existing proxy\n }\n if (cachedProxies.has(target)) {\n return target\n }\n\n const handler: ProxyHandler<T> = {\n get(target, prop, receiver) {\n // Retrieve the original value. If 'prop' is a getter, 'receiver' (the proxy) is used as 'this'.\n const value = Reflect.get(target, prop, receiver)\n // If the retrieved value is a function, bind it to the original 'target' object.\n // This ensures that methods (e.g., Set.prototype.add, Map.prototype.set) are called\n // with the correct 'this' context, which is the original unwrapped object.\n // Special handling for Set and Map mutating methods\n if (target instanceof Set) {\n if (['add', 'delete', 'clear'].includes(String(prop))) {\n return (...args: unknown[]) => {\n const oldValue = new Set(target) // shallow copy before mutation\n // eslint-disable-next-line @typescript-eslint/no-unsafe-function-type\n const result = (value as Function).apply(target, args)\n onChange({\n path,\n value: target,\n oldValue,\n })\n return result\n }\n }\n }\n if (target instanceof Map) {\n if (['set', 'delete', 'clear'].includes(String(prop))) {\n return (...args: unknown[]) => {\n const oldValue = new Map(target) // shallow copy before mutation\n // eslint-disable-next-line @typescript-eslint/no-unsafe-function-type\n const result = (value as Function).apply(target, args)\n onChange({\n path,\n value: target,\n oldValue,\n })\n return result\n }\n }\n }\n if (typeof value === 'function' && !Array.isArray(target)) {\n // If the value is a function, bind it to the original target object.\n return value.bind(target)\n }\n // If the value is an object (and not null, and not a function due to the check above),\n // then recursively create a reactive proxy for it.\n if (typeof value === 'object' && value !== null) {\n const finalPath = path ? `${path}.${String(prop)}` : String(prop)\n return createReactive(value, onChange, finalPath)\n }\n // For primitive values or null, return the value directly.\n return value\n },\n set(target, prop, value, receiver) {\n const oldValue = Reflect.get(target, prop, receiver)\n const success = Reflect.set(target, prop, value) // do not set the receiver here\n // The receiver is the proxy itself and if we set the value on the proxy, it will\n // create an infinite loop.\n if (success && oldValue !== value) {\n // Check if the old value was proxied and remove it from the cache\n if (typeof oldValue === 'object' && oldValue !== null) {\n const oldProxy = cachedRaw.get(oldValue)\n if (oldProxy) {\n cachedRaw.delete(oldValue)\n cachedProxies.delete(oldProxy)\n }\n }\n onChange({\n path: path ? `${path}.${String(prop)}` : String(prop),\n value,\n oldValue,\n })\n }\n return success\n },\n deleteProperty(target, prop) {\n const oldValue = Reflect.get(target, prop)\n const success = Reflect.deleteProperty(target, prop)\n if (success) {\n // Check if the old value was proxied and remove it from the cache\n if (typeof oldValue === 'object' && oldValue !== null) {\n const oldProxy = cachedRaw.get(oldValue)\n if (oldProxy) {\n cachedRaw.delete(oldValue)\n cachedProxies.delete(oldProxy)\n }\n }\n onChange({\n path: path ? `${path}.${String(prop)}` : String(prop),\n value: undefined,\n oldValue,\n })\n }\n return success\n },\n }\n const proxy = new Proxy(target, handler)\n cachedRaw.set(target, proxy)\n cachedProxies.set(proxy, target)\n return proxy\n}\n\n/**\n * Removes the proxy and cleans up the cached data.\n *\n * @param reactiveObject The reactive object to unhook.\n * @returns true if the object was reactive and was unhooked, false otherwise.\n */\nexport function unhookReactive(reactiveObject: object): boolean {\n const raw = cachedProxies.get(reactiveObject)\n if (!raw) {\n return false // Not a reactive object\n }\n cachedRaw.delete(raw)\n cachedProxies.delete(reactiveObject)\n return true\n}\n"]}
|
|
@@ -22,6 +22,8 @@ class ComponentDemoPage extends DemoPage {
|
|
|
22
22
|
kind: UserKind,
|
|
23
23
|
status: 'active',
|
|
24
24
|
grantType: 'editor',
|
|
25
|
+
created: new Date('2023-01-01T00:00:00Z').getTime(),
|
|
26
|
+
updated: new Date('2023-01-02T00:00:00Z').getTime(),
|
|
25
27
|
}
|
|
26
28
|
this.user2 = {
|
|
27
29
|
key: '2',
|
|
@@ -30,6 +32,8 @@ class ComponentDemoPage extends DemoPage {
|
|
|
30
32
|
kind: UserKind,
|
|
31
33
|
status: 'active',
|
|
32
34
|
grantType: 'editor',
|
|
35
|
+
created: new Date('2023-01-01T00:00:00Z').getTime(),
|
|
36
|
+
updated: new Date('2023-01-02T00:00:00Z').getTime(),
|
|
33
37
|
}
|
|
34
38
|
}
|
|
35
39
|
|
package/eslint.config.js
CHANGED
package/package.json
CHANGED
package/src/core/Activity.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/* eslint-disable @typescript-eslint/no-unused-vars */
|
|
2
2
|
import { nothing, TemplateResult } from 'lit'
|
|
3
|
-
import {
|
|
3
|
+
import { ActivityLifecycle, IntentResult, type Intent } from './ActivityManager.js'
|
|
4
4
|
import { FragmentManager } from './FragmentManager.js'
|
|
5
5
|
import type { Fragment } from './Fragment.js'
|
|
6
6
|
import type { Application, UpdateRequest } from './Application.js'
|
|
@@ -36,7 +36,7 @@ export class Activity extends EventTarget {
|
|
|
36
36
|
*/
|
|
37
37
|
static action?: string
|
|
38
38
|
|
|
39
|
-
public
|
|
39
|
+
public lifecycle: ActivityLifecycle = ActivityLifecycle.Initialized
|
|
40
40
|
protected parent: Application
|
|
41
41
|
/**
|
|
42
42
|
* The fragment manager that manages fragments in this activity.
|
|
@@ -2,13 +2,41 @@ import type { Activity } from './Activity.js'
|
|
|
2
2
|
import type { Application } from './Application.js'
|
|
3
3
|
import { navigateScreen } from './ApplicationRoute.js'
|
|
4
4
|
|
|
5
|
-
export enum
|
|
5
|
+
export enum ActivityLifecycle {
|
|
6
|
+
/**
|
|
7
|
+
* The activity is initialized, but not yet created.
|
|
8
|
+
* This is the initial state of the activity.
|
|
9
|
+
*/
|
|
6
10
|
Initialized,
|
|
11
|
+
/**
|
|
12
|
+
* The activity is created, but not yet started.
|
|
13
|
+
* This is the state after the `onCreate()` method is called.
|
|
14
|
+
*/
|
|
7
15
|
Created,
|
|
16
|
+
/**
|
|
17
|
+
* The activity is started, but not yet resumed.
|
|
18
|
+
* This is the state after the `onStart()` method is called.
|
|
19
|
+
*/
|
|
8
20
|
Started,
|
|
21
|
+
/**
|
|
22
|
+
* The activity is resumed and visible to the user.
|
|
23
|
+
* This is the state after the `onResume()` method is called.
|
|
24
|
+
*/
|
|
9
25
|
Resumed,
|
|
26
|
+
/**
|
|
27
|
+
* The activity is paused, but still visible to the user.
|
|
28
|
+
* This is the state after the `onPause()` method is called.
|
|
29
|
+
*/
|
|
10
30
|
Paused,
|
|
31
|
+
/**
|
|
32
|
+
* The activity is stopped and no longer visible to the user.
|
|
33
|
+
* This is the state after the `onStop()` method is called.
|
|
34
|
+
*/
|
|
11
35
|
Stopped,
|
|
36
|
+
/**
|
|
37
|
+
* The activity is destroyed and no longer exists.
|
|
38
|
+
* This is the state after the `onDestroy()` method is called.
|
|
39
|
+
*/
|
|
12
40
|
Destroyed,
|
|
13
41
|
}
|
|
14
42
|
|
|
@@ -16,12 +44,10 @@ export enum IntentFlags {
|
|
|
16
44
|
/**
|
|
17
45
|
* Marks that the activity should be rendered as a modal.
|
|
18
46
|
*/
|
|
19
|
-
// eslint-disable-next-line @typescript-eslint/prefer-literal-enum-member
|
|
20
47
|
Modal = 1 << 0,
|
|
21
48
|
/**
|
|
22
49
|
* The activity is started for result.
|
|
23
50
|
*/
|
|
24
|
-
// eslint-disable-next-line @typescript-eslint/prefer-literal-enum-member
|
|
25
51
|
ForResult = 1 << 1,
|
|
26
52
|
/**
|
|
27
53
|
* When set, the intent data is passed by reference.
|
|
@@ -29,7 +55,6 @@ export enum IntentFlags {
|
|
|
29
55
|
* This is useful when the data is large and you want to avoid copying it
|
|
30
56
|
* or when you want to share the same data between activities.
|
|
31
57
|
*/
|
|
32
|
-
// eslint-disable-next-line @typescript-eslint/prefer-literal-enum-member
|
|
33
58
|
ByReference = 1 << 2,
|
|
34
59
|
}
|
|
35
60
|
|
|
@@ -178,14 +203,13 @@ export class ActivityManager {
|
|
|
178
203
|
await info.activity.onNewIntent(intent)
|
|
179
204
|
// TODO: Check if the activity is destroyed.
|
|
180
205
|
this.setupRoute(intent)
|
|
181
|
-
// this.currentActivity = info.activity
|
|
182
206
|
await this.updateCurrentActivity(info.activity, false)
|
|
183
207
|
return
|
|
184
208
|
}
|
|
185
209
|
const activity = this.buildActivity(intent)
|
|
186
210
|
if (currentActivity) {
|
|
187
211
|
await currentActivity.onPause()
|
|
188
|
-
currentActivity.
|
|
212
|
+
currentActivity.lifecycle = ActivityLifecycle.Paused
|
|
189
213
|
}
|
|
190
214
|
|
|
191
215
|
const activityId = this.generateActivityId()
|
|
@@ -204,7 +228,7 @@ export class ActivityManager {
|
|
|
204
228
|
// the activity finished and the manager already took care of this situation.
|
|
205
229
|
return
|
|
206
230
|
}
|
|
207
|
-
activity.
|
|
231
|
+
activity.lifecycle = ActivityLifecycle.Created
|
|
208
232
|
|
|
209
233
|
await this.updateCurrentActivity(activity, !!isModal)
|
|
210
234
|
if (this.isDestroyed(activity)) {
|
|
@@ -257,7 +281,7 @@ export class ActivityManager {
|
|
|
257
281
|
}
|
|
258
282
|
|
|
259
283
|
isDestroyed(activity: Activity): boolean {
|
|
260
|
-
return activity.
|
|
284
|
+
return activity.lifecycle === ActivityLifecycle.Destroyed
|
|
261
285
|
}
|
|
262
286
|
|
|
263
287
|
/**
|
|
@@ -272,23 +296,23 @@ export class ActivityManager {
|
|
|
272
296
|
const stackEntry = stack.splice(index, 1)[0]
|
|
273
297
|
|
|
274
298
|
await stackEntry.activity.onPause()
|
|
275
|
-
stackEntry.activity.
|
|
299
|
+
stackEntry.activity.lifecycle = ActivityLifecycle.Paused
|
|
276
300
|
await stackEntry.activity.onStop()
|
|
277
|
-
stackEntry.activity.
|
|
301
|
+
stackEntry.activity.lifecycle = ActivityLifecycle.Stopped
|
|
278
302
|
await stackEntry.activity.onDestroy()
|
|
279
|
-
stackEntry.activity.
|
|
303
|
+
stackEntry.activity.lifecycle = ActivityLifecycle.Destroyed
|
|
280
304
|
|
|
281
305
|
await this.manageActivityResult(stackEntry.activity, stackEntry.intent)
|
|
282
306
|
|
|
283
307
|
// Resume the previous activity
|
|
284
308
|
await this.bringLastActivityToFront()
|
|
285
309
|
} else {
|
|
286
|
-
if (activity.
|
|
310
|
+
if (activity.lifecycle === ActivityLifecycle.Created) {
|
|
287
311
|
await activity.onStop()
|
|
288
|
-
activity.
|
|
312
|
+
activity.lifecycle = ActivityLifecycle.Stopped
|
|
289
313
|
}
|
|
290
314
|
await activity.onDestroy()
|
|
291
|
-
activity.
|
|
315
|
+
activity.lifecycle = ActivityLifecycle.Destroyed
|
|
292
316
|
// This can happen when an activity finishes in one of the callback methods,
|
|
293
317
|
// before it is added to the stack. In that case, we bring the last activity back to the front.
|
|
294
318
|
await this.bringLastActivityToFront()
|
|
@@ -302,12 +326,12 @@ export class ActivityManager {
|
|
|
302
326
|
const all = [...this.activityStack, ...this.modalActivityStack]
|
|
303
327
|
const target = all.find((entry) => entry.activity.hasRequestCode(requestCode))
|
|
304
328
|
if (target) {
|
|
305
|
-
if (target.activity.
|
|
329
|
+
if (target.activity.lifecycle === ActivityLifecycle.Destroyed) {
|
|
306
330
|
return
|
|
307
331
|
}
|
|
308
|
-
if (target.activity.
|
|
332
|
+
if (target.activity.lifecycle === ActivityLifecycle.Resumed) {
|
|
309
333
|
await target.activity.onPause()
|
|
310
|
-
target.activity.
|
|
334
|
+
target.activity.lifecycle = ActivityLifecycle.Paused
|
|
311
335
|
}
|
|
312
336
|
const intentCopy = structuredClone(intent)
|
|
313
337
|
intentCopy.data = activity.getResult()
|
|
@@ -329,11 +353,11 @@ export class ActivityManager {
|
|
|
329
353
|
}
|
|
330
354
|
|
|
331
355
|
getTopActivity(): Activity | undefined {
|
|
332
|
-
const topModal = this.modalActivityStack.findLast((a) => a.activity.
|
|
356
|
+
const topModal = this.modalActivityStack.findLast((a) => a.activity.lifecycle === ActivityLifecycle.Resumed)
|
|
333
357
|
if (topModal) {
|
|
334
358
|
return topModal.activity
|
|
335
359
|
}
|
|
336
|
-
const top = this.activityStack.findLast((a) => a.activity.
|
|
360
|
+
const top = this.activityStack.findLast((a) => a.activity.lifecycle === ActivityLifecycle.Resumed)
|
|
337
361
|
return top ? top.activity : undefined
|
|
338
362
|
}
|
|
339
363
|
|
|
@@ -385,48 +409,48 @@ export class ActivityManager {
|
|
|
385
409
|
if (
|
|
386
410
|
this.currentActivity &&
|
|
387
411
|
this.currentActivity !== activity &&
|
|
388
|
-
this.currentActivity.
|
|
412
|
+
this.currentActivity.lifecycle !== ActivityLifecycle.Destroyed &&
|
|
389
413
|
// Make sure we don't pause the current activity if it's already paused.
|
|
390
|
-
this.currentActivity.
|
|
414
|
+
this.currentActivity.lifecycle !== ActivityLifecycle.Paused
|
|
391
415
|
) {
|
|
392
416
|
await this.currentActivity.onPause()
|
|
393
|
-
this.currentActivity.
|
|
417
|
+
this.currentActivity.lifecycle = ActivityLifecycle.Paused
|
|
394
418
|
}
|
|
395
|
-
if (activity.
|
|
419
|
+
if (activity.lifecycle === ActivityLifecycle.Paused) {
|
|
396
420
|
await activity.onResume()
|
|
397
421
|
if (this.isDestroyed(activity)) {
|
|
398
422
|
return
|
|
399
423
|
}
|
|
400
|
-
activity.
|
|
424
|
+
activity.lifecycle = ActivityLifecycle.Resumed
|
|
401
425
|
activity.requestUpdate()
|
|
402
|
-
} else if (activity.
|
|
426
|
+
} else if (activity.lifecycle === ActivityLifecycle.Stopped) {
|
|
403
427
|
await activity.onRestart()
|
|
404
|
-
activity.
|
|
405
|
-
} else if (activity.
|
|
428
|
+
activity.lifecycle = ActivityLifecycle.Resumed
|
|
429
|
+
} else if (activity.lifecycle === ActivityLifecycle.Destroyed) {
|
|
406
430
|
throw new Error(`Invalid state. The activity is already destroyed.`)
|
|
407
|
-
} else if (activity.
|
|
431
|
+
} else if (activity.lifecycle === ActivityLifecycle.Created) {
|
|
408
432
|
await activity.onStart()
|
|
409
433
|
if (this.isDestroyed(activity)) {
|
|
410
434
|
return
|
|
411
435
|
}
|
|
412
|
-
activity.
|
|
436
|
+
activity.lifecycle = ActivityLifecycle.Started
|
|
413
437
|
await activity.onResume()
|
|
414
438
|
if (this.isDestroyed(activity)) {
|
|
415
439
|
return
|
|
416
440
|
}
|
|
417
|
-
activity.
|
|
441
|
+
activity.lifecycle = ActivityLifecycle.Resumed
|
|
418
442
|
activity.requestUpdate()
|
|
419
|
-
} else if (activity.
|
|
443
|
+
} else if (activity.lifecycle === ActivityLifecycle.Started) {
|
|
420
444
|
await activity.onResume()
|
|
421
445
|
if (this.isDestroyed(activity)) {
|
|
422
446
|
return
|
|
423
447
|
}
|
|
424
|
-
activity.
|
|
448
|
+
activity.lifecycle = ActivityLifecycle.Resumed
|
|
425
449
|
activity.requestUpdate()
|
|
426
|
-
} else if (activity.
|
|
450
|
+
} else if (activity.lifecycle === ActivityLifecycle.Initialized) {
|
|
427
451
|
// TODO: Figure out intent passing here.
|
|
428
452
|
await activity.onCreate()
|
|
429
|
-
activity.
|
|
453
|
+
activity.lifecycle = ActivityLifecycle.Created
|
|
430
454
|
if (this.isDestroyed(activity)) {
|
|
431
455
|
// the activity finished and the manager already took care of this situation.
|
|
432
456
|
return
|
|
@@ -435,12 +459,12 @@ export class ActivityManager {
|
|
|
435
459
|
if (this.isDestroyed(activity)) {
|
|
436
460
|
return
|
|
437
461
|
}
|
|
438
|
-
activity.
|
|
462
|
+
activity.lifecycle = ActivityLifecycle.Started
|
|
439
463
|
await activity.onResume()
|
|
440
464
|
if (this.isDestroyed(activity)) {
|
|
441
465
|
return
|
|
442
466
|
}
|
|
443
|
-
activity.
|
|
467
|
+
activity.lifecycle = ActivityLifecycle.Resumed
|
|
444
468
|
activity.requestUpdate()
|
|
445
469
|
}
|
|
446
470
|
if (!isModal) {
|
package/src/core/Application.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { nothing, type TemplateResult } from 'lit'
|
|
2
|
-
import { ActivityManager,
|
|
2
|
+
import { ActivityManager, ActivityLifecycle } from './ActivityManager.js'
|
|
3
3
|
import type { Activity } from './Activity.js'
|
|
4
4
|
import { ApplicationRenderer } from './renderer/ApplicationRenderer.js'
|
|
5
5
|
import { ThemeManager } from './ThemeManager.js'
|
|
@@ -159,7 +159,7 @@ export abstract class Application extends EventTarget {
|
|
|
159
159
|
render(): TemplateResult | typeof nothing {
|
|
160
160
|
const currentActivity = this.#activities.getCurrentActivity()
|
|
161
161
|
// Only render if started or beyond
|
|
162
|
-
if (currentActivity && currentActivity.
|
|
162
|
+
if (currentActivity && currentActivity.lifecycle >= ActivityLifecycle.Started) {
|
|
163
163
|
return currentActivity.render()
|
|
164
164
|
}
|
|
165
165
|
return nothing
|
|
@@ -47,7 +47,7 @@ export abstract class ModalActivity extends Activity {
|
|
|
47
47
|
|
|
48
48
|
override onResume(): void | Promise<void> {
|
|
49
49
|
this.renderRoot = document.createElement('div')
|
|
50
|
-
this.renderRoot.classList.add('modal-
|
|
50
|
+
this.renderRoot.classList.add('modal-activity-container')
|
|
51
51
|
document.body.appendChild(this.renderRoot)
|
|
52
52
|
super.onResume()
|
|
53
53
|
this.requestUpdate()
|
package/src/reactive/reactive.ts
CHANGED
|
@@ -24,17 +24,59 @@ export function createReactive<T extends object>(
|
|
|
24
24
|
|
|
25
25
|
const handler: ProxyHandler<T> = {
|
|
26
26
|
get(target, prop, receiver) {
|
|
27
|
+
// Retrieve the original value. If 'prop' is a getter, 'receiver' (the proxy) is used as 'this'.
|
|
27
28
|
const value = Reflect.get(target, prop, receiver)
|
|
29
|
+
// If the retrieved value is a function, bind it to the original 'target' object.
|
|
30
|
+
// This ensures that methods (e.g., Set.prototype.add, Map.prototype.set) are called
|
|
31
|
+
// with the correct 'this' context, which is the original unwrapped object.
|
|
32
|
+
// Special handling for Set and Map mutating methods
|
|
33
|
+
if (target instanceof Set) {
|
|
34
|
+
if (['add', 'delete', 'clear'].includes(String(prop))) {
|
|
35
|
+
return (...args: unknown[]) => {
|
|
36
|
+
const oldValue = new Set(target) // shallow copy before mutation
|
|
37
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-function-type
|
|
38
|
+
const result = (value as Function).apply(target, args)
|
|
39
|
+
onChange({
|
|
40
|
+
path,
|
|
41
|
+
value: target,
|
|
42
|
+
oldValue,
|
|
43
|
+
})
|
|
44
|
+
return result
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
if (target instanceof Map) {
|
|
49
|
+
if (['set', 'delete', 'clear'].includes(String(prop))) {
|
|
50
|
+
return (...args: unknown[]) => {
|
|
51
|
+
const oldValue = new Map(target) // shallow copy before mutation
|
|
52
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-function-type
|
|
53
|
+
const result = (value as Function).apply(target, args)
|
|
54
|
+
onChange({
|
|
55
|
+
path,
|
|
56
|
+
value: target,
|
|
57
|
+
oldValue,
|
|
58
|
+
})
|
|
59
|
+
return result
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
if (typeof value === 'function' && !Array.isArray(target)) {
|
|
64
|
+
// If the value is a function, bind it to the original target object.
|
|
65
|
+
return value.bind(target)
|
|
66
|
+
}
|
|
67
|
+
// If the value is an object (and not null, and not a function due to the check above),
|
|
68
|
+
// then recursively create a reactive proxy for it.
|
|
28
69
|
if (typeof value === 'object' && value !== null) {
|
|
29
70
|
const finalPath = path ? `${path}.${String(prop)}` : String(prop)
|
|
30
71
|
return createReactive(value, onChange, finalPath)
|
|
31
72
|
}
|
|
73
|
+
// For primitive values or null, return the value directly.
|
|
32
74
|
return value
|
|
33
75
|
},
|
|
34
76
|
set(target, prop, value, receiver) {
|
|
35
77
|
const oldValue = Reflect.get(target, prop, receiver)
|
|
36
78
|
const success = Reflect.set(target, prop, value) // do not set the receiver here
|
|
37
|
-
//
|
|
79
|
+
// The receiver is the proxy itself and if we set the value on the proxy, it will
|
|
38
80
|
// create an infinite loop.
|
|
39
81
|
if (success && oldValue !== value) {
|
|
40
82
|
// Check if the old value was proxied and remove it from the cache
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { assert } from '@esm-bundle/chai'
|
|
2
2
|
import { Activity } from '../../src/core/Activity.js'
|
|
3
3
|
import { Application } from '../../src/core/Application.js'
|
|
4
|
-
import { IntentResult,
|
|
4
|
+
import { IntentResult, ActivityLifecycle } from '../../src/core/ActivityManager.js'
|
|
5
5
|
import { fixture, html } from '@open-wc/testing'
|
|
6
6
|
import { EventTypes } from '../../src/events/EventTypes.js'
|
|
7
7
|
import sinon from 'sinon'
|
|
@@ -102,7 +102,7 @@ describe('Activity Lifecycle and Events', () => {
|
|
|
102
102
|
})
|
|
103
103
|
|
|
104
104
|
it('sets the default state', async () => {
|
|
105
|
-
assert.equal(activity.
|
|
105
|
+
assert.equal(activity.lifecycle, ActivityLifecycle.Initialized)
|
|
106
106
|
})
|
|
107
107
|
|
|
108
108
|
it('sets the default resultCode', async () => {
|