UNPKG

ember-source

Version:

A JavaScript framework for creating ambitious web applications

313 lines (296 loc) 10.2 kB
import { j as setInternalHelperManager, C as CustomHelperManager, k as setInternalModifierManager, s as setInternalComponentManager } from './api-CkUl6KyJ.js'; import { registerDestructor } from '../@glimmer/destroyable/index.js'; import { debugAssert } from '../@glimmer/global-context/index.js'; import './debug-to-string-BsFOvUtQ.js'; import { isDevelopingApp } from '@embroider/macros'; import { untrack, createUpdatableTag } from '../@glimmer/validator/index.js'; import { d as createConstRef } from './reference-B6HMX4y0.js'; import { a as argsProxyFor } from './args-proxy-B91L3LRK.js'; import { b as buildCapabilities, F as FROM_CAPABILITIES } from './capabilities-DHiXCCuB.js'; import { c as castToBrowser } from './simple-cast-BXTrayoV.js'; const CAPABILITIES = { dynamicLayout: false, dynamicTag: false, prepareArgs: false, createArgs: true, attributeHook: false, elementHook: false, createCaller: false, dynamicScope: true, updateHook: true, createInstance: true, wrapped: false, willDestroy: false, hasSubOwner: false }; function componentCapabilities(managerAPI, options = {}) { if (isDevelopingApp() && managerAPI !== '3.13') { throw new Error('Invalid component manager compatibility specified'); } let updateHook = Boolean(options.updateHook); return buildCapabilities({ asyncLifeCycleCallbacks: Boolean(options.asyncLifecycleCallbacks), destructor: Boolean(options.destructor), updateHook }); } function hasAsyncLifeCycleCallbacks(delegate) { return delegate.capabilities.asyncLifeCycleCallbacks; } function hasUpdateHook(delegate) { return delegate.capabilities.updateHook; } function hasAsyncUpdateHook(delegate) { return hasAsyncLifeCycleCallbacks(delegate) && hasUpdateHook(delegate); } function hasDestructors(delegate) { return delegate.capabilities.destructor; } /** The CustomComponentManager allows addons to provide custom component implementations that integrate seamlessly into Ember. This is accomplished through a delegate, registered with the custom component manager, which implements a set of hooks that determine component behavior. To create a custom component manager, instantiate a new CustomComponentManager class and pass the delegate as the first argument: ```js let manager = new CustomComponentManager({ // ...delegate implementation... }); ``` ## Delegate Hooks Throughout the lifecycle of a component, the component manager will invoke delegate hooks that are responsible for surfacing those lifecycle changes to the end developer. * `create()` - invoked when a new instance of a component should be created * `update()` - invoked when the arguments passed to a component change * `getContext()` - returns the object that should be */ class CustomComponentManager { componentManagerDelegates = new WeakMap(); constructor(factory) { this.factory = factory; } getDelegateFor(owner) { let { componentManagerDelegates } = this; let delegate = componentManagerDelegates.get(owner); if (delegate === undefined) { let { factory } = this; delegate = factory(owner); // eslint-disable-next-line @typescript-eslint/no-non-null-assertion -- @fixme if (isDevelopingApp() && !FROM_CAPABILITIES.has(delegate.capabilities)) { // TODO: This error message should make sense in both Ember and Glimmer https://github.com/glimmerjs/glimmer-vm/issues/1200 throw new Error(`Custom component managers must have a \`capabilities\` property that is the result of calling the \`capabilities('3.13')\` (imported via \`import { capabilities } from '@ember/component';\`). Received: \`${JSON.stringify(delegate.capabilities // eslint-disable-next-line @typescript-eslint/no-base-to-string )}\` for: \`${delegate}\``); } componentManagerDelegates.set(owner, delegate); } return delegate; } create(owner, definition, vmArgs) { let delegate = this.getDelegateFor(owner); let args = argsProxyFor(vmArgs.capture(), 'component'); let component = delegate.createComponent(definition, args); return new CustomComponentState(component, delegate, args); } getDebugName(definition) { // eslint-disable-next-line @typescript-eslint/no-base-to-string return typeof definition === 'function' ? definition.name : definition.toString(); } update(bucket) { let { delegate } = bucket; if (hasUpdateHook(delegate)) { let { component, args } = bucket; delegate.updateComponent(component, args); } } didCreate({ component, delegate }) { if (hasAsyncLifeCycleCallbacks(delegate)) { delegate.didCreateComponent(component); } } didUpdate({ component, delegate }) { if (hasAsyncUpdateHook(delegate)) { delegate.didUpdateComponent(component); } } didRenderLayout() {} didUpdateLayout() {} getSelf({ component, delegate }) { return createConstRef(delegate.getContext(component), 'this'); } getDestroyable(bucket) { const { delegate } = bucket; if (hasDestructors(delegate)) { const { component } = bucket; registerDestructor(bucket, () => delegate.destroyComponent(component)); return bucket; } return null; } getCapabilities() { return CAPABILITIES; } } /** * Stores internal state about a component instance after it's been created. */ class CustomComponentState { constructor(component, delegate, args) { this.component = component; this.delegate = delegate; this.args = args; } } function modifierCapabilities(managerAPI, optionalFeatures = {}) { debugAssert(managerAPI === '3.22', () => `Invalid modifier manager compatibility specified; you specified ${managerAPI}, but only '3.22' is supported.`); return buildCapabilities({ disableAutoTracking: Boolean(optionalFeatures.disableAutoTracking) }); } /** The CustomModifierManager allows addons to provide custom modifier implementations that integrate seamlessly into Ember. This is accomplished through a delegate, registered with the custom modifier manager, which implements a set of hooks that determine modifier behavior. To create a custom modifier manager, instantiate a new CustomModifierManager class and pass the delegate as the first argument: ```js let manager = new CustomModifierManager({ // ...delegate implementation... }); ``` ## Delegate Hooks Throughout the lifecycle of a modifier, the modifier manager will invoke delegate hooks that are responsible for surfacing those lifecycle changes to the end developer. * `createModifier()` - invoked when a new instance of a modifier should be created * `installModifier()` - invoked when the modifier is installed on the element * `updateModifier()` - invoked when the arguments passed to a modifier change * `destroyModifier()` - invoked when the modifier is about to be destroyed */ class CustomModifierManager { componentManagerDelegates = new WeakMap(); constructor(factory) { this.factory = factory; } getDelegateFor(owner) { let { componentManagerDelegates } = this; let delegate = componentManagerDelegates.get(owner); if (delegate === undefined) { let { factory } = this; delegate = factory(owner); // eslint-disable-next-line @typescript-eslint/no-non-null-assertion -- @fixme if (isDevelopingApp() && !FROM_CAPABILITIES.has(delegate.capabilities)) { // TODO: This error message should make sense in both Ember and Glimmer https://github.com/glimmerjs/glimmer-vm/issues/1200 throw new Error(`Custom modifier managers must have a \`capabilities\` property that is the result of calling the \`capabilities('3.22')\` (imported via \`import { capabilities } from '@ember/modifier';\`). Received: \`${JSON.stringify(delegate.capabilities // eslint-disable-next-line @typescript-eslint/no-base-to-string )}\` for: \`${delegate}\``); } componentManagerDelegates.set(owner, delegate); } return delegate; } create(owner, element, definition, capturedArgs) { let delegate = this.getDelegateFor(owner); let args = argsProxyFor(capturedArgs, 'modifier'); let instance = delegate.createModifier(definition, args); let tag = createUpdatableTag(); let state; state = { tag, element, delegate, args, modifier: instance }; registerDestructor(state, () => delegate.destroyModifier(instance, args)); return state; } getDebugName(definition) { if (typeof definition === 'function') { return definition.name || definition.toString(); } else { return '<unknown>'; } } getDebugInstance({ modifier }) { return modifier; } getTag({ tag }) { return tag; } install({ element, args, modifier, delegate }) { let { capabilities } = delegate; if (capabilities.disableAutoTracking) { untrack(() => delegate.installModifier(modifier, castToBrowser(element, 'ELEMENT'), args)); } else { delegate.installModifier(modifier, castToBrowser(element), args); } } update({ args, modifier, delegate }) { let { capabilities } = delegate; if (capabilities.disableAutoTracking) { untrack(() => delegate.updateModifier(modifier, args)); } else { delegate.updateModifier(modifier, args); } } getDestroyable(state) { return state; } } function setComponentManager(factory, obj) { return setInternalComponentManager(new CustomComponentManager(factory), obj); } function setModifierManager(factory, obj) { return setInternalModifierManager(new CustomModifierManager(factory), obj); } function setHelperManager(factory, obj) { return setInternalHelperManager(new CustomHelperManager(factory), obj); } export { CustomComponentManager as C, CustomModifierManager as a, setHelperManager as b, componentCapabilities as c, setModifierManager as d, modifierCapabilities as m, setComponentManager as s };