@studiometa/js-toolkit 4.0.0-alpha.1 → 4.0.0-alpha.3
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/README.md +10 -0
- package/dist/Base.d.ts +0 -2
- package/dist/Base.js +1 -2
- package/dist/Base.js.map +1 -1
- package/dist/attribute-namespaces.d.ts +0 -2
- package/dist/attribute-namespaces.js +1 -2
- package/dist/attributes.d.ts +0 -2
- package/dist/attributes.js +1 -2
- package/dist/children-watchers.d.ts +0 -2
- package/dist/children-watchers.js +1 -2
- package/dist/component-brand.d.ts +0 -2
- package/dist/component-brand.js +1 -2
- package/dist/component-declarations.d.ts +0 -2
- package/dist/component-declarations.js +1 -2
- package/dist/context-subscription.d.ts +0 -2
- package/dist/context-subscription.js +1 -2
- package/dist/context.d.ts +0 -2
- package/dist/context.js +1 -2
- package/dist/decorators.d.ts +0 -2
- package/dist/decorators.js +1 -2
- package/dist/diagnostic-contract.d.ts +0 -2
- package/dist/diagnostic-contract.js +1 -2
- package/dist/diagnostics.d.ts +0 -2
- package/dist/diagnostics.js +1 -2
- package/dist/document-order.d.ts +0 -2
- package/dist/document-order.js +1 -2
- package/dist/dom-mutations.d.ts +0 -2
- package/dist/dom-mutations.js +1 -2
- package/dist/events.d.ts +0 -2
- package/dist/events.js +1 -2
- package/dist/group.d.ts +0 -2
- package/dist/group.js +1 -2
- package/dist/instances.d.ts +0 -2
- package/dist/instances.js +1 -2
- package/dist/manifest.d.ts +0 -2
- package/dist/manifest.js +1 -2
- package/dist/mount-strategies.d.ts +0 -2
- package/dist/mount-strategies.js +1 -2
- package/dist/negotiated-events.d.ts +0 -2
- package/dist/negotiated-events.js +1 -2
- package/dist/protocol-symbols.d.ts +0 -2
- package/dist/protocol-symbols.js +1 -2
- package/dist/registry.d.ts +0 -2
- package/dist/registry.js +1 -2
- package/dist/responsive-options.d.ts +0 -2
- package/dist/responsive-options.js +1 -2
- package/dist/scheduler.d.ts +0 -2
- package/dist/scheduler.js +1 -2
- package/dist/scheduler.js.map +1 -1
- package/dist/services/breakpoint.d.ts +0 -2
- package/dist/services/breakpoint.js +1 -2
- package/dist/services/drag.d.ts +0 -2
- package/dist/services/drag.js +1 -2
- package/dist/services/in-view.d.ts +0 -2
- package/dist/services/in-view.js +1 -2
- package/dist/services/key.d.ts +0 -2
- package/dist/services/key.js +1 -2
- package/dist/services/media.d.ts +0 -2
- package/dist/services/media.js +1 -2
- package/dist/services/mixin.d.ts +0 -2
- package/dist/services/mixin.js +1 -2
- package/dist/services/mutation.d.ts +0 -2
- package/dist/services/mutation.js +1 -2
- package/dist/services/pointer.d.ts +0 -2
- package/dist/services/pointer.js +1 -2
- package/dist/services/raf.d.ts +0 -2
- package/dist/services/raf.js +1 -2
- package/dist/services/resize.d.ts +0 -2
- package/dist/services/resize.js +1 -2
- package/dist/services/scroll-progress-offset.d.ts +0 -2
- package/dist/services/scroll-progress-offset.js +1 -2
- package/dist/services/scroll-progress.d.ts +0 -2
- package/dist/services/scroll-progress.js +1 -2
- package/dist/services/scroll.d.ts +0 -2
- package/dist/services/scroll.js +1 -2
- package/dist/services/service.d.ts +0 -2
- package/dist/services/service.js +1 -2
- package/dist/services/toggle.d.ts +0 -2
- package/dist/services/toggle.js +1 -2
- package/dist/services/until.d.ts +0 -2
- package/dist/services/until.js +1 -2
- package/dist/shared-runtime.d.ts +0 -2
- package/dist/shared-runtime.js +1 -2
- package/dist/storage/createStorage.d.ts +0 -2
- package/dist/storage/createStorage.js +1 -2
- package/dist/storage/index.d.ts +0 -2
- package/dist/storage/index.js +1 -2
- package/dist/storage/providers.d.ts +0 -2
- package/dist/storage/providers.js +1 -2
- package/dist/storage/serializers.d.ts +0 -2
- package/dist/storage/serializers.js +1 -2
- package/dist/storage/sync.d.ts +0 -2
- package/dist/storage/sync.js +1 -2
- package/dist/storage/types.d.ts +0 -2
- package/dist/subpaths/BREAKPOINTS.js +1 -2
- package/dist/subpaths/Base.js +1 -2
- package/dist/subpaths/DIAGNOSTICS.js +1 -2
- package/dist/subpaths/DRAG_MODES.js +1 -2
- package/dist/subpaths/EVENTS.js +1 -2
- package/dist/subpaths/MOUNT_ATTRIBUTE.js +1 -2
- package/dist/subpaths/SWAP_MODES.js +1 -2
- package/dist/subpaths/children.js +1 -2
- package/dist/subpaths/component.js +1 -2
- package/dist/subpaths/createContext.js +1 -2
- package/dist/subpaths/createFallbackProvider.js +1 -2
- package/dist/subpaths/createGroup.js +1 -2
- package/dist/subpaths/createLocalStorage.js +1 -2
- package/dist/subpaths/createMemoryStorageProvider.js +1 -2
- package/dist/subpaths/createService.js +1 -2
- package/dist/subpaths/createServiceMixin.js +1 -2
- package/dist/subpaths/createSessionStorage.js +1 -2
- package/dist/subpaths/createStorage.js +1 -2
- package/dist/subpaths/createUrlSearchParamsInHashProvider.js +1 -2
- package/dist/subpaths/createUrlSearchParamsInHashStorage.js +1 -2
- package/dist/subpaths/createUrlSearchParamsProvider.js +1 -2
- package/dist/subpaths/createUrlSearchParamsStorage.js +1 -2
- package/dist/subpaths/defaultScheduler.js +1 -2
- package/dist/subpaths/defineManifest.js +1 -2
- package/dist/subpaths/domUpdate.js +1 -2
- package/dist/subpaths/emitExtendable.js +1 -2
- package/dist/subpaths/fromMetaGlob.js +1 -2
- package/dist/subpaths/fromWebpackContext.js +1 -2
- package/dist/subpaths/getBreakpoints.js +1 -2
- package/dist/subpaths/getInstance.js +1 -2
- package/dist/subpaths/getInstances.js +1 -2
- package/dist/subpaths/getMountedInstances.js +1 -2
- package/dist/subpaths/getUnmountedInstances.js +1 -2
- package/dist/subpaths/inject.js +1 -2
- package/dist/subpaths/injectContext.js +1 -2
- package/dist/subpaths/injectContextSync.js +1 -2
- package/dist/subpaths/jsonSerializer.js +1 -2
- package/dist/subpaths/localStorageProvider.js +1 -2
- package/dist/subpaths/memoryStorageProvider.js +1 -2
- package/dist/subpaths/namespaceQualifier.js +1 -2
- package/dist/subpaths/nextFrame.js +1 -2
- package/dist/subpaths/on.js +1 -2
- package/dist/subpaths/perTarget.js +1 -2
- package/dist/subpaths/provide.js +1 -2
- package/dist/subpaths/provideContext.js +1 -2
- package/dist/subpaths/provideRootContext.js +1 -2
- package/dist/subpaths/read.js +1 -2
- package/dist/subpaths/registerComponent.js +1 -2
- package/dist/subpaths/registerComponents.js +1 -2
- package/dist/subpaths/registerManifest.js +1 -2
- package/dist/subpaths/reportDiagnostic.js +1 -2
- package/dist/subpaths/sessionStorageProvider.js +1 -2
- package/dist/subpaths/setBreakpoints.js +1 -2
- package/dist/subpaths/signal.js +1 -2
- package/dist/subpaths/subscribeContext.js +1 -2
- package/dist/subpaths/swap.js +1 -2
- package/dist/subpaths/toggle.js +1 -2
- package/dist/subpaths/until.js +1 -2
- package/dist/subpaths/urlSearchParamsInHashProvider.js +1 -2
- package/dist/subpaths/urlSearchParamsProvider.js +1 -2
- package/dist/subpaths/useBreakpoint.js +1 -2
- package/dist/subpaths/useDrag.js +1 -2
- package/dist/subpaths/useInView.js +1 -2
- package/dist/subpaths/useKey.js +1 -2
- package/dist/subpaths/useMediaQuery.js +1 -2
- package/dist/subpaths/useMutation.js +1 -2
- package/dist/subpaths/usePointer.js +1 -2
- package/dist/subpaths/usePrefersReducedMotion.js +1 -2
- package/dist/subpaths/useRaf.js +1 -2
- package/dist/subpaths/useResize.js +1 -2
- package/dist/subpaths/useScroll.js +1 -2
- package/dist/subpaths/useScrollProgress.js +1 -2
- package/dist/subpaths/useWindowScroll.js +1 -2
- package/dist/subpaths/useWindowSize.js +1 -2
- package/dist/subpaths/utils/DEFAULT_DAMP_FACTOR.js +1 -2
- package/dist/subpaths/utils/INERTIA_FRAME.js +1 -2
- package/dist/subpaths/utils/MAX_SPRING_RATIO.js +1 -2
- package/dist/subpaths/utils/SCROLL_ALIGNMENTS.js +1 -2
- package/dist/subpaths/utils/SCROLL_AXES.js +1 -2
- package/dist/subpaths/utils/TRANSFORM_PROPS.js +1 -2
- package/dist/subpaths/utils/TRANSITION_OPTIONS.js +1 -2
- package/dist/subpaths/utils/camelCase.js +1 -2
- package/dist/subpaths/utils/capitalize.js +1 -2
- package/dist/subpaths/utils/clamp.js +1 -2
- package/dist/subpaths/utils/clamp01.js +1 -2
- package/dist/subpaths/utils/clampDampFactor.js +1 -2
- package/dist/subpaths/utils/createEaseInOut.js +1 -2
- package/dist/subpaths/utils/createEaseOut.js +1 -2
- package/dist/subpaths/utils/createElement.js +1 -2
- package/dist/subpaths/utils/createRange.js +1 -2
- package/dist/subpaths/utils/damp.js +1 -2
- package/dist/subpaths/utils/debounce.js +1 -2
- package/dist/subpaths/utils/decayOver.js +1 -2
- package/dist/subpaths/utils/deepmerge.js +1 -2
- package/dist/subpaths/utils/easeInCirc.js +1 -2
- package/dist/subpaths/utils/easeInCubic.js +1 -2
- package/dist/subpaths/utils/easeInExpo.js +1 -2
- package/dist/subpaths/utils/easeInOutCirc.js +1 -2
- package/dist/subpaths/utils/easeInOutCubic.js +1 -2
- package/dist/subpaths/utils/easeInOutExpo.js +1 -2
- package/dist/subpaths/utils/easeInOutQuad.js +1 -2
- package/dist/subpaths/utils/easeInOutQuart.js +1 -2
- package/dist/subpaths/utils/easeInOutQuint.js +1 -2
- package/dist/subpaths/utils/easeInOutSine.js +1 -2
- package/dist/subpaths/utils/easeInQuad.js +1 -2
- package/dist/subpaths/utils/easeInQuart.js +1 -2
- package/dist/subpaths/utils/easeInQuint.js +1 -2
- package/dist/subpaths/utils/easeInSine.js +1 -2
- package/dist/subpaths/utils/easeLinear.js +1 -2
- package/dist/subpaths/utils/easeOutCirc.js +1 -2
- package/dist/subpaths/utils/easeOutCubic.js +1 -2
- package/dist/subpaths/utils/easeOutExpo.js +1 -2
- package/dist/subpaths/utils/easeOutQuad.js +1 -2
- package/dist/subpaths/utils/easeOutQuart.js +1 -2
- package/dist/subpaths/utils/easeOutQuint.js +1 -2
- package/dist/subpaths/utils/easeOutSine.js +1 -2
- package/dist/subpaths/utils/enterTransition.js +1 -2
- package/dist/subpaths/utils/fold.js +1 -2
- package/dist/subpaths/utils/getOffsetSizes.js +1 -2
- package/dist/subpaths/utils/historyPush.js +1 -2
- package/dist/subpaths/utils/historyReplace.js +1 -2
- package/dist/subpaths/utils/inertiaDecay.js +1 -2
- package/dist/subpaths/utils/inertiaFinalValue.js +1 -2
- package/dist/subpaths/utils/inertiaStep.js +1 -2
- package/dist/subpaths/utils/inertiaTimeConstant.js +1 -2
- package/dist/subpaths/utils/isBoolean.js +1 -2
- package/dist/subpaths/utils/isDefined.js +1 -2
- package/dist/subpaths/utils/isFunction.js +1 -2
- package/dist/subpaths/utils/isNull.js +1 -2
- package/dist/subpaths/utils/isNumber.js +1 -2
- package/dist/subpaths/utils/isObject.js +1 -2
- package/dist/subpaths/utils/isString.js +1 -2
- package/dist/subpaths/utils/kebabCase.js +1 -2
- package/dist/subpaths/utils/leaveTransition.js +1 -2
- package/dist/subpaths/utils/lerp.js +1 -2
- package/dist/subpaths/utils/loadImage.js +1 -2
- package/dist/subpaths/utils/loadLink.js +1 -2
- package/dist/subpaths/utils/loadScript.js +1 -2
- package/dist/subpaths/utils/lockScroll.js +1 -2
- package/dist/subpaths/utils/lowerCase.js +1 -2
- package/dist/subpaths/utils/map.js +1 -2
- package/dist/subpaths/utils/matrix.js +1 -2
- package/dist/subpaths/utils/mean.js +1 -2
- package/dist/subpaths/utils/memo.js +1 -2
- package/dist/subpaths/utils/noop.js +1 -2
- package/dist/subpaths/utils/noopValue.js +1 -2
- package/dist/subpaths/utils/objectToURLSearchParams.js +1 -2
- package/dist/subpaths/utils/pascalCase.js +1 -2
- package/dist/subpaths/utils/random.js +1 -2
- package/dist/subpaths/utils/randomInt.js +1 -2
- package/dist/subpaths/utils/randomItem.js +1 -2
- package/dist/subpaths/utils/round.js +1 -2
- package/dist/subpaths/utils/saveActiveElement.js +1 -2
- package/dist/subpaths/utils/scrollPosition.js +1 -2
- package/dist/subpaths/utils/scrollTo.js +1 -2
- package/dist/subpaths/utils/selectorFor.js +1 -2
- package/dist/subpaths/utils/setClassesOrStyles.js +1 -2
- package/dist/subpaths/utils/smoothTo.js +1 -2
- package/dist/subpaths/utils/snakeCase.js +1 -2
- package/dist/subpaths/utils/spring.js +1 -2
- package/dist/subpaths/utils/throttle.js +1 -2
- package/dist/subpaths/utils/transform.js +1 -2
- package/dist/subpaths/utils/transition.js +1 -2
- package/dist/subpaths/utils/trapFocus.js +1 -2
- package/dist/subpaths/utils/untrapFocus.js +1 -2
- package/dist/subpaths/utils/upperCase.js +1 -2
- package/dist/subpaths/utils/wait.js +1 -2
- package/dist/subpaths/utils/withLeadingCharacters.js +1 -2
- package/dist/subpaths/utils/withLeadingSlash.js +1 -2
- package/dist/subpaths/utils/withTrailingCharacters.js +1 -2
- package/dist/subpaths/utils/withTrailingSlash.js +1 -2
- package/dist/subpaths/utils/withoutLeadingCharacters.js +1 -2
- package/dist/subpaths/utils/withoutLeadingCharactersRecursive.js +1 -2
- package/dist/subpaths/utils/withoutLeadingSlash.js +1 -2
- package/dist/subpaths/utils/withoutTrailingCharacters.js +1 -2
- package/dist/subpaths/utils/withoutTrailingCharactersRecursive.js +1 -2
- package/dist/subpaths/utils/withoutTrailingSlash.js +1 -2
- package/dist/subpaths/utils/wrap.js +1 -2
- package/dist/subpaths/viewTransition.js +1 -2
- package/dist/subpaths/warn.js +1 -2
- package/dist/subpaths/watchAttributeNamespace.js +1 -2
- package/dist/subpaths/watchAttributes.js +1 -2
- package/dist/subpaths/whenDOMSettled.js +1 -2
- package/dist/subpaths/withDrag.js +1 -2
- package/dist/subpaths/withInView.js +1 -2
- package/dist/subpaths/withKey.js +1 -2
- package/dist/subpaths/withMutation.js +1 -2
- package/dist/subpaths/withPointer.js +1 -2
- package/dist/subpaths/withRaf.js +1 -2
- package/dist/subpaths/withResize.js +1 -2
- package/dist/subpaths/withScroll.js +1 -2
- package/dist/subpaths/withScrollProgress.js +1 -2
- package/dist/subpaths/write.js +1 -2
- package/dist/swap.d.ts +0 -2
- package/dist/swap.js +1 -2
- package/dist/test/index.d.ts +0 -2
- package/dist/test/index.js +1 -2
- package/dist/utils/deepmerge.d.ts +0 -2
- package/dist/utils/deepmerge.js +1 -2
- package/dist/utils/dom.d.ts +0 -2
- package/dist/utils/dom.js +1 -2
- package/dist/utils/easings.d.ts +0 -2
- package/dist/utils/easings.js +1 -2
- package/dist/utils/focus.d.ts +0 -2
- package/dist/utils/focus.js +1 -2
- package/dist/utils/history.d.ts +0 -2
- package/dist/utils/history.js +1 -2
- package/dist/utils/is.d.ts +0 -2
- package/dist/utils/is.js +1 -2
- package/dist/utils/load.d.ts +0 -2
- package/dist/utils/load.js +1 -2
- package/dist/utils/maths.d.ts +0 -2
- package/dist/utils/maths.js +1 -2
- package/dist/utils/memo.d.ts +0 -2
- package/dist/utils/memo.js +1 -2
- package/dist/utils/noop.d.ts +0 -2
- package/dist/utils/noop.js +1 -2
- package/dist/utils/random.d.ts +0 -2
- package/dist/utils/random.js +1 -2
- package/dist/utils/scroll-lock.d.ts +0 -2
- package/dist/utils/scroll-lock.js +1 -2
- package/dist/utils/scrollTo.d.ts +0 -2
- package/dist/utils/scrollTo.js +1 -2
- package/dist/utils/selectors.d.ts +0 -2
- package/dist/utils/selectors.js +1 -2
- package/dist/utils/smoothTo.d.ts +0 -2
- package/dist/utils/smoothTo.js +1 -2
- package/dist/utils/strings.d.ts +0 -2
- package/dist/utils/strings.js +1 -2
- package/dist/utils/timing.d.ts +0 -2
- package/dist/utils/timing.js +1 -2
- package/dist/utils/transform.d.ts +0 -2
- package/dist/utils/transform.js +1 -2
- package/dist/utils/transition.d.ts +0 -2
- package/dist/utils/transition.js +1 -2
- package/dist/viewTransition.d.ts +0 -2
- package/dist/viewTransition.js +1 -2
- package/package.json +6 -3
- package/skills/studiometa-js-toolkit/SKILL.md +87 -0
- package/skills/studiometa-js-toolkit/references/api/configuration.md +169 -0
- package/skills/studiometa-js-toolkit/references/api/context/createContext.md +46 -0
- package/skills/studiometa-js-toolkit/references/api/context/createGroup.md +67 -0
- package/skills/studiometa-js-toolkit/references/api/context/index.md +69 -0
- package/skills/studiometa-js-toolkit/references/api/context/injectContext.md +58 -0
- package/skills/studiometa-js-toolkit/references/api/context/injectContextSync.md +44 -0
- package/skills/studiometa-js-toolkit/references/api/context/provideContext.md +61 -0
- package/skills/studiometa-js-toolkit/references/api/context/provideRootContext.md +42 -0
- package/skills/studiometa-js-toolkit/references/api/context/signal.md +85 -0
- package/skills/studiometa-js-toolkit/references/api/context/subscribeContext.md +71 -0
- package/skills/studiometa-js-toolkit/references/api/decorators/children.md +92 -0
- package/skills/studiometa-js-toolkit/references/api/decorators/component.md +75 -0
- package/skills/studiometa-js-toolkit/references/api/decorators/index.md +41 -0
- package/skills/studiometa-js-toolkit/references/api/decorators/inject.md +73 -0
- package/skills/studiometa-js-toolkit/references/api/decorators/on.md +149 -0
- package/skills/studiometa-js-toolkit/references/api/decorators/provide.md +77 -0
- package/skills/studiometa-js-toolkit/references/api/decorators/read-write.md +93 -0
- package/skills/studiometa-js-toolkit/references/api/diagnostics/DIAGNOSTICS.md +137 -0
- package/skills/studiometa-js-toolkit/references/api/diagnostics/EVENTS.md +45 -0
- package/skills/studiometa-js-toolkit/references/api/diagnostics/index.md +58 -0
- package/skills/studiometa-js-toolkit/references/api/diagnostics/reportDiagnostic.md +67 -0
- package/skills/studiometa-js-toolkit/references/api/diagnostics/warn.md +54 -0
- package/skills/studiometa-js-toolkit/references/api/dom/breakpoints.md +81 -0
- package/skills/studiometa-js-toolkit/references/api/dom/domUpdate.md +70 -0
- package/skills/studiometa-js-toolkit/references/api/dom/emitExtendable.md +68 -0
- package/skills/studiometa-js-toolkit/references/api/dom/index.md +26 -0
- package/skills/studiometa-js-toolkit/references/api/dom/swap.md +105 -0
- package/skills/studiometa-js-toolkit/references/api/dom/watchAttributeNamespace.md +96 -0
- package/skills/studiometa-js-toolkit/references/api/dom/watchAttributes.md +56 -0
- package/skills/studiometa-js-toolkit/references/api/dom/whenDOMSettled.md +41 -0
- package/skills/studiometa-js-toolkit/references/api/html/data-component.md +105 -0
- package/skills/studiometa-js-toolkit/references/api/html/data-mount.md +69 -0
- package/skills/studiometa-js-toolkit/references/api/html/data-option.md +106 -0
- package/skills/studiometa-js-toolkit/references/api/html/data-ref.md +63 -0
- package/skills/studiometa-js-toolkit/references/api/html/index.md +32 -0
- package/skills/studiometa-js-toolkit/references/api/index.md +73 -0
- package/skills/studiometa-js-toolkit/references/api/instance-events.md +75 -0
- package/skills/studiometa-js-toolkit/references/api/instance-methods.md +267 -0
- package/skills/studiometa-js-toolkit/references/api/instance-properties.md +90 -0
- package/skills/studiometa-js-toolkit/references/api/methods-hooks-events.md +149 -0
- package/skills/studiometa-js-toolkit/references/api/methods-hooks-lifecycle.md +90 -0
- package/skills/studiometa-js-toolkit/references/api/methods-hooks-options.md +96 -0
- package/skills/studiometa-js-toolkit/references/api/methods-hooks-services.md +164 -0
- package/skills/studiometa-js-toolkit/references/api/registry/defineManifest.md +57 -0
- package/skills/studiometa-js-toolkit/references/api/registry/fromMetaGlob.md +33 -0
- package/skills/studiometa-js-toolkit/references/api/registry/fromWebpackContext.md +37 -0
- package/skills/studiometa-js-toolkit/references/api/registry/getInstance.md +52 -0
- package/skills/studiometa-js-toolkit/references/api/registry/getInstances.md +48 -0
- package/skills/studiometa-js-toolkit/references/api/registry/getMountedInstances.md +39 -0
- package/skills/studiometa-js-toolkit/references/api/registry/getUnmountedInstances.md +46 -0
- package/skills/studiometa-js-toolkit/references/api/registry/index.md +49 -0
- package/skills/studiometa-js-toolkit/references/api/registry/registerComponent.md +93 -0
- package/skills/studiometa-js-toolkit/references/api/registry/registerComponents.md +37 -0
- package/skills/studiometa-js-toolkit/references/api/registry/registerManifest.md +72 -0
- package/skills/studiometa-js-toolkit/references/api/scheduler/defaultScheduler.md +104 -0
- package/skills/studiometa-js-toolkit/references/api/scheduler/index.md +27 -0
- package/skills/studiometa-js-toolkit/references/api/scheduler/nextFrame.md +31 -0
- package/skills/studiometa-js-toolkit/references/api/scheduler/viewTransition.md +53 -0
- package/skills/studiometa-js-toolkit/references/api/services/createService.md +95 -0
- package/skills/studiometa-js-toolkit/references/api/services/createServiceMixin.md +81 -0
- package/skills/studiometa-js-toolkit/references/api/services/index.md +94 -0
- package/skills/studiometa-js-toolkit/references/api/services/mixins.md +148 -0
- package/skills/studiometa-js-toolkit/references/api/services/perTarget.md +65 -0
- package/skills/studiometa-js-toolkit/references/api/services/toggle.md +80 -0
- package/skills/studiometa-js-toolkit/references/api/services/until.md +44 -0
- package/skills/studiometa-js-toolkit/references/api/services/useBreakpoint.md +42 -0
- package/skills/studiometa-js-toolkit/references/api/services/useDrag.md +119 -0
- package/skills/studiometa-js-toolkit/references/api/services/useInView.md +64 -0
- package/skills/studiometa-js-toolkit/references/api/services/useKey.md +105 -0
- package/skills/studiometa-js-toolkit/references/api/services/useMediaQuery.md +45 -0
- package/skills/studiometa-js-toolkit/references/api/services/useMutation.md +69 -0
- package/skills/studiometa-js-toolkit/references/api/services/usePointer.md +91 -0
- package/skills/studiometa-js-toolkit/references/api/services/usePrefersReducedMotion.md +42 -0
- package/skills/studiometa-js-toolkit/references/api/services/useRaf.md +91 -0
- package/skills/studiometa-js-toolkit/references/api/services/useResize.md +60 -0
- package/skills/studiometa-js-toolkit/references/api/services/useScroll.md +88 -0
- package/skills/studiometa-js-toolkit/references/api/services/useScrollProgress.md +88 -0
- package/skills/studiometa-js-toolkit/references/api/services/useWindowScroll.md +25 -0
- package/skills/studiometa-js-toolkit/references/api/services/useWindowSize.md +25 -0
- package/skills/studiometa-js-toolkit/references/api/storage/createStorage.md +126 -0
- package/skills/studiometa-js-toolkit/references/api/storage/index.md +33 -0
- package/skills/studiometa-js-toolkit/references/api/storage/presets.md +58 -0
- package/skills/studiometa-js-toolkit/references/api/storage/providers.md +109 -0
- package/skills/studiometa-js-toolkit/references/api/test/index.md +214 -0
- package/skills/studiometa-js-toolkit/references/guide/concepts/attribute-grammar.md +93 -0
- package/skills/studiometa-js-toolkit/references/guide/concepts/mutation-engine.md +71 -0
- package/skills/studiometa-js-toolkit/references/guide/concepts/philosophy.md +78 -0
- package/skills/studiometa-js-toolkit/references/guide/concepts/the-registry.md +110 -0
- package/skills/studiometa-js-toolkit/references/guide/going-further/autoloading.md +104 -0
- package/skills/studiometa-js-toolkit/references/guide/going-further/handling-diagnostics.md +104 -0
- package/skills/studiometa-js-toolkit/references/guide/going-further/mount-strategies.md +95 -0
- package/skills/studiometa-js-toolkit/references/guide/going-further/scheduling-work.md +124 -0
- package/skills/studiometa-js-toolkit/references/guide/going-further/sharing-state.md +218 -0
- package/skills/studiometa-js-toolkit/references/guide/going-further/swapping-content.md +92 -0
- package/skills/studiometa-js-toolkit/references/guide/going-further/testing-components.md +137 -0
- package/skills/studiometa-js-toolkit/references/guide/going-further/typing-components.md +190 -0
- package/skills/studiometa-js-toolkit/references/guide/going-further/using-decorators.md +168 -0
- package/skills/studiometa-js-toolkit/references/guide/going-further/using-storage.md +125 -0
- package/skills/studiometa-js-toolkit/references/guide/index.md +163 -0
- package/skills/studiometa-js-toolkit/references/guide/introduction/installation.md +144 -0
- package/skills/studiometa-js-toolkit/references/guide/introduction/lifecycle-hooks.md +174 -0
- package/skills/studiometa-js-toolkit/references/guide/introduction/managing-components.md +229 -0
- package/skills/studiometa-js-toolkit/references/guide/introduction/managing-options.md +235 -0
- package/skills/studiometa-js-toolkit/references/guide/introduction/managing-refs.md +174 -0
- package/skills/studiometa-js-toolkit/references/guide/introduction/using-services.md +218 -0
- package/skills/studiometa-js-toolkit/references/guide/introduction/working-with-events.md +251 -0
- package/skills/studiometa-js-toolkit/references/guide/migration/v3-to-v4.md +373 -0
- package/skills/studiometa-js-toolkit/references/utils/css.md +98 -0
- package/skills/studiometa-js-toolkit/references/utils/dom.md +54 -0
- package/skills/studiometa-js-toolkit/references/utils/easings.md +217 -0
- package/skills/studiometa-js-toolkit/references/utils/focus.md +84 -0
- package/skills/studiometa-js-toolkit/references/utils/history.md +69 -0
- package/skills/studiometa-js-toolkit/references/utils/index.md +53 -0
- package/skills/studiometa-js-toolkit/references/utils/is.md +84 -0
- package/skills/studiometa-js-toolkit/references/utils/load.md +59 -0
- package/skills/studiometa-js-toolkit/references/utils/math.md +129 -0
- package/skills/studiometa-js-toolkit/references/utils/motion.md +218 -0
- package/skills/studiometa-js-toolkit/references/utils/objects.md +68 -0
- package/skills/studiometa-js-toolkit/references/utils/scroll.md +128 -0
- package/skills/studiometa-js-toolkit/references/utils/strings.md +176 -0
- package/skills/studiometa-js-toolkit/references/utils/timing.md +111 -0
- package/skills/studiometa-js-toolkit/references/utils/transitions.md +122 -0
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
# Services
|
|
2
|
+
|
|
3
|
+
A service is a shared source of props that components subscribe to. Every one of them is **lazy and reference-counted**.
|
|
4
|
+
|
|
5
|
+
## The sources
|
|
6
|
+
|
|
7
|
+
| Service | Hook | Target |
|
|
8
|
+
| ----------------------------------------------------------------- | ---------------- | ---------------------------------- |
|
|
9
|
+
| [`useRaf()`](useRaf.md) | `ticked` | — |
|
|
10
|
+
| [`useScroll(target?)`](useScroll.md) | `scrolled` | an element or the window |
|
|
11
|
+
| [`useWindowScroll()`](useWindowScroll.md) | `scrolled` | the named default |
|
|
12
|
+
| [`useScrollProgress(target, options?)`](useScrollProgress.md) | `scrolledInView` | an element |
|
|
13
|
+
| [`useResize(target?)`](useResize.md) | `resized` | an element |
|
|
14
|
+
| [`useWindowSize()`](useWindowSize.md) | `resized` | the named default |
|
|
15
|
+
| [`usePointer(target?)`](usePointer.md) | `moved` | an element, or the viewport |
|
|
16
|
+
| [`useDrag(target, options?)`](useDrag.md) | `dragged` | an `HTMLElement` or `SVGElement` |
|
|
17
|
+
| [`useKey(target?)`](useKey.md) | `keyed` | the document, an element or window |
|
|
18
|
+
| [`useInView(target, init?)`](useInView.md) | `intersected` | an element |
|
|
19
|
+
| [`useMutation(target, init?)`](useMutation.md) | `mutated` | any node |
|
|
20
|
+
| [`useBreakpoint()`](useBreakpoint.md) | — | — |
|
|
21
|
+
| [`useMediaQuery(query)`](useMediaQuery.md) | — | — |
|
|
22
|
+
| [`usePrefersReducedMotion()`](usePrefersReducedMotion.md) | — | the named case |
|
|
23
|
+
|
|
24
|
+
`LoadService` is not ported.
|
|
25
|
+
|
|
26
|
+
## The mixins
|
|
27
|
+
|
|
28
|
+
[`withRaf`, `withScroll`, `withResize`, `withScrollProgress`, `withPointer`, `withDrag`, `withInView`, `withMutation` and `withKey`](mixins.md) bind one subscription per mount cycle under the one method name the service owns.
|
|
29
|
+
|
|
30
|
+
## The combinators
|
|
31
|
+
|
|
32
|
+
- [`toggle(subscribe)`](toggle.md) — a subscription you can switch on and off
|
|
33
|
+
- [`until(service, predicate)`](until.md) — a one-shot wait
|
|
34
|
+
|
|
35
|
+
## Writing your own
|
|
36
|
+
|
|
37
|
+
- [`createService(definition)`](createService.md) — the primitive
|
|
38
|
+
- [`perTarget(create, keyOf?)`](perTarget.md) — one instance per target and per options
|
|
39
|
+
- [`createServiceMixin(definition)`](createServiceMixin.md) — the mixin over it
|
|
40
|
+
|
|
41
|
+
## The service surface
|
|
42
|
+
|
|
43
|
+
```ts
|
|
44
|
+
interface Service<T, R = void> {
|
|
45
|
+
subscribe(callback: (props: T) => R, options?: { immediate?: boolean }): () => void;
|
|
46
|
+
props(): T;
|
|
47
|
+
}
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
`subscribe()` returns the unsubscribe function. **Lazy and reference-counted**: the source starts on the first subscriber and stops on the last, so with no subscriber there is no listener, no observer and no frame.
|
|
51
|
+
|
|
52
|
+
**Every prop field is `readonly`, and the props object belongs to its service.** It is valid for the duration of the call that received it — use `{ ...props }` to keep one.
|
|
53
|
+
|
|
54
|
+
## Asking for the first delivery
|
|
55
|
+
|
|
56
|
+
```js
|
|
57
|
+
useScroll().subscribe(callback, { immediate: true });
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
The sources that have a current value honour it. The ones that do not, do nothing, and each service states which it is through `hasProps()`:
|
|
61
|
+
|
|
62
|
+
| Has a current value between deliveries | Does not |
|
|
63
|
+
| -------------------------------------------------------- | ----------------------------------------------------------------------------------------- |
|
|
64
|
+
| scroll, resize, breakpoint, media query, scroll progress | the frame tick, the pointer before it is seen, a drag outside a gesture, a mutation batch |
|
|
65
|
+
|
|
66
|
+
Only the new subscriber is called, and the first props of a run carry no movement.
|
|
67
|
+
|
|
68
|
+
## One instance per target and per options
|
|
69
|
+
|
|
70
|
+
Keyed in a `WeakMap` by [`perTarget()`](perTarget.md). **The options are read by meaning, not by spelling**: object keys are sorted at every depth and the keys holding `undefined` are dropped, while arrays keep their order.
|
|
71
|
+
|
|
72
|
+
```js
|
|
73
|
+
// The same service.
|
|
74
|
+
useInView(el, { threshold: 0.5, rootMargin: '0px' });
|
|
75
|
+
useInView(el, { rootMargin: '0px', threshold: 0.5 });
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
Only what the platform owns needs a key of its own: `useInView()` gives its root a weak id, and `useMutation()` keeps a canonical init for the DOM contract. **Nothing groups observers across targets.**
|
|
79
|
+
|
|
80
|
+
## Props are flat
|
|
81
|
+
|
|
82
|
+
**One field per axis, and nothing derivable is a field.** `lastX` is `x - deltaX`; `changedX` is `deltaX !== 0`. The grouped objects of v3 — `last`, `delta`, `max`, `progress`, `direction`, `changed` — are removed.
|
|
83
|
+
|
|
84
|
+
`directionX` and `directionY` are `-1 | 0 | 1`: one signed value that multiplies.
|
|
85
|
+
|
|
86
|
+
## No service owns a loop
|
|
87
|
+
|
|
88
|
+
The raf service and an active drag inertia subscribe to `scheduler.tick()`. The scroll service coalesces its events into one `read` per frame. The resize service is a `ResizeObserver`. See [The scheduler](../../guide/going-further/scheduling-work.md).
|
|
89
|
+
|
|
90
|
+
## Failures
|
|
91
|
+
|
|
92
|
+
A subscriber that throws is skipped and reported as `callback.service-failed`. Core dispatches the diagnostic first and calls `reportError()` only when no listener cancelled the event.
|
|
93
|
+
|
|
94
|
+
Publishing is **re-entrant**, so any code that changes state after a publication checks first that the service is still alive.
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
# Service mixins
|
|
2
|
+
|
|
3
|
+
A mixin binds one subscription per mount cycle, under the **one method name the service owns**.
|
|
4
|
+
|
|
5
|
+
| Mixin | Hook | Service |
|
|
6
|
+
| -------------------- | ---------------- | ------------------------------------------------- |
|
|
7
|
+
| `withRaf` | `ticked` | [`useRaf()`](useRaf.md) |
|
|
8
|
+
| `withScroll` | `scrolled` | [`useScroll()`](useScroll.md) |
|
|
9
|
+
| `withResize` | `resized` | [`useResize()`](useResize.md) |
|
|
10
|
+
| `withPointer` | `moved` | [`usePointer()`](usePointer.md) |
|
|
11
|
+
| `withDrag` | `dragged` | [`useDrag()`](useDrag.md) |
|
|
12
|
+
| `withKey` | `keyed` | [`useKey()`](useKey.md) |
|
|
13
|
+
| `withInView` | `intersected` | [`useInView()`](useInView.md) |
|
|
14
|
+
| `withMutation` | `mutated` | [`useMutation()`](useMutation.md) |
|
|
15
|
+
| `withScrollProgress` | `scrolledInView` | [`useScrollProgress()`](useScrollProgress.md) |
|
|
16
|
+
|
|
17
|
+
## Two call forms
|
|
18
|
+
|
|
19
|
+
```ts
|
|
20
|
+
withScroll(BaseClass, options?) // a mixin
|
|
21
|
+
withScroll(options?) // a class decorator
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
```js
|
|
25
|
+
import { Base, withScroll } from '@studiometa/js-toolkit';
|
|
26
|
+
|
|
27
|
+
class Header extends withScroll(Base) {
|
|
28
|
+
static config = { name: 'Header' };
|
|
29
|
+
|
|
30
|
+
scrolled({ y, directionY }) {
|
|
31
|
+
this.$el.classList.toggle('is-hidden', directionY > 0 && y > 100);
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
```ts
|
|
37
|
+
import { Base, component, withScroll } from '@studiometa/js-toolkit';
|
|
38
|
+
|
|
39
|
+
@component({ name: 'Header' })
|
|
40
|
+
@withScroll()
|
|
41
|
+
class Header extends Base {
|
|
42
|
+
scrolled() {}
|
|
43
|
+
}
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
**The mixin is the primitive**, because it needs no build step. The decorator is sugar over it.
|
|
47
|
+
|
|
48
|
+
## Stacking
|
|
49
|
+
|
|
50
|
+
```js
|
|
51
|
+
import { Base, withRaf, withResize } from '@studiometa/js-toolkit';
|
|
52
|
+
|
|
53
|
+
class Parallax extends withRaf(withResize(Base)) {
|
|
54
|
+
static config = { name: 'Parallax' };
|
|
55
|
+
|
|
56
|
+
resized({ height }) {}
|
|
57
|
+
|
|
58
|
+
ticked({ delta }) {}
|
|
59
|
+
}
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
`$services` keys accumulate through the intersection, so `$services.ticked` and `$services.resized` both complete.
|
|
63
|
+
|
|
64
|
+
## Options
|
|
65
|
+
|
|
66
|
+
```ts
|
|
67
|
+
interface ServiceMixinOptions<Target, Host = Base> {
|
|
68
|
+
target?: (instance: Host) => Target;
|
|
69
|
+
manual?: boolean;
|
|
70
|
+
immediate?: boolean;
|
|
71
|
+
}
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
**The options of a mixin are not the options of the service.** `target`, `manual` and `immediate` describe the subscription; they are removed before the `use*()` call and they are absent from its options type. A service's own options go in the same object and are forwarded:
|
|
75
|
+
|
|
76
|
+
```js
|
|
77
|
+
withDrag(Base, { axis: 'x', inertia: false, immediate: true, manual: true });
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
### `target`
|
|
81
|
+
|
|
82
|
+
```js
|
|
83
|
+
withScroll(Base, { target: (instance) => instance.$refs.scroller });
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
A resolver that comes back with `undefined` or `null` reports `service.missing-target` and starts **no** subscription — because a renamed ref arrives here as nothing, and a service with a default target would otherwise observe the wrong thing and look like it worked.
|
|
87
|
+
|
|
88
|
+
A service whose **own** default target is nothing, like `withRaf`, is untouched: that is its contract, and only a caller's resolver can be wrong about it.
|
|
89
|
+
|
|
90
|
+
**The resolver is typed against the host as declared**
|
|
91
|
+
|
|
92
|
+
A mixin is applied while the extends clause of its class is still being evaluated, so `withDrag(Base, …)` types its resolver against `Base`. A component reaching further names the shape it needs — `(instance as Base & { readonly target: HTMLElement }).target` — which is an assertion rather than a check. v3 wrote the same line as an `@ts-expect-error`. That is why core checks the result at runtime.
|
|
93
|
+
|
|
94
|
+
### `manual`
|
|
95
|
+
|
|
96
|
+
Declares the hook without running it, and `$services.<hook>` becomes the switch:
|
|
97
|
+
|
|
98
|
+
```js
|
|
99
|
+
import { Base, withRaf } from '@studiometa/js-toolkit';
|
|
100
|
+
|
|
101
|
+
class SliderItem extends withRaf(Base, { manual: true }) {
|
|
102
|
+
static config = { name: 'SliderItem' };
|
|
103
|
+
|
|
104
|
+
ticked({ delta }) {}
|
|
105
|
+
|
|
106
|
+
onSelected() {
|
|
107
|
+
this.$services.ticked.start();
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
onSettled() {
|
|
111
|
+
this.$services.ticked.stop();
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
Each handle is a [`Toggle`](toggle.md): `start()` is idempotent and `stop()` is safe to repeat.
|
|
117
|
+
|
|
118
|
+
### `immediate`
|
|
119
|
+
|
|
120
|
+
Asks for the first delivery at subscribe time. `withInView` defaults it to `true`.
|
|
121
|
+
|
|
122
|
+
## It never occupies a lifecycle hook
|
|
123
|
+
|
|
124
|
+
`mounted()` and `unmounted()` belong to the component author, so **nothing has to be chained**. A class that mixes a service in and writes its own `mounted()` without `super.mounted()` still subscribes: the framework's own `$mount()`/`$unmount()` pair carries the subscription.
|
|
125
|
+
|
|
126
|
+
- The subscription starts **once the whole of `mounted()` has run** — including an `immediate` first delivery, which therefore reaches a component that is fully set up.
|
|
127
|
+
- It is released **before `unmounted()`**, exactly where the mount cleanup used to release it.
|
|
128
|
+
- `$unmount()` releases unconditionally, so a manual subscription started outside a mount cycle is released too.
|
|
129
|
+
|
|
130
|
+
**A userland mixin still chains**
|
|
131
|
+
|
|
132
|
+
The rule is about what the mixin **overrides**, not who wrote it. A mixin that puts its work in `mounted()` needs its subclasses to call `super.mounted()` — and the way not to need that is to override `$mount()`. See [`createServiceMixin()`](createServiceMixin.md).
|
|
133
|
+
|
|
134
|
+
## One hook per class, and that is the limit
|
|
135
|
+
|
|
136
|
+
A mixin binds one subscription, under one name, for each mount cycle. There is **no `hook` option**.
|
|
137
|
+
|
|
138
|
+
A component whose subscriptions are **one per markup declaration** — one per attribute, with its own modifiers and its own threshold, known only when the element is read — has no method to name and no fixed count. It subscribes itself:
|
|
139
|
+
|
|
140
|
+
```js
|
|
141
|
+
mounted() {
|
|
142
|
+
return useScroll(this.$refs.panel).subscribe((props) => {});
|
|
143
|
+
}
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
`on<Event>` has the same shape: a handler name belongs to the class, while a set of events can be data. Both sugars are keyed on a name a class declares, and both leave the same escape open — bind it yourself, own the cleanup.
|
|
147
|
+
|
|
148
|
+
Neither limit is about a build step: `withRaf(Base)` is an ordinary call, and the name is fixed by how the class is written.
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
# perTarget
|
|
2
|
+
|
|
3
|
+
```ts
|
|
4
|
+
perTarget<Target extends WeakKey, Args extends unknown[], T, R = void>(
|
|
5
|
+
create: (target: Target, ...args: Args) => Service<T, R>,
|
|
6
|
+
keyOf?: (...args: Args) => string,
|
|
7
|
+
): (target: Target, ...args: Args) => Service<T, R>
|
|
8
|
+
```
|
|
9
|
+
|
|
10
|
+
Gives a service factory the **one instance per target and per options** caching every built-in service has.
|
|
11
|
+
|
|
12
|
+
## Usage
|
|
13
|
+
|
|
14
|
+
```ts
|
|
15
|
+
import { createService, perTarget } from '@studiometa/js-toolkit';
|
|
16
|
+
|
|
17
|
+
interface FocusProps {
|
|
18
|
+
readonly hasFocus: boolean;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
const useFocus = perTarget((target: Element) =>
|
|
22
|
+
createService<FocusProps>({
|
|
23
|
+
props: () => ({ hasFocus: target.contains(document.activeElement) }),
|
|
24
|
+
start(emit) {
|
|
25
|
+
const publish = () => emit({ hasFocus: target.contains(document.activeElement) });
|
|
26
|
+
document.addEventListener('focusin', publish);
|
|
27
|
+
document.addEventListener('focusout', publish);
|
|
28
|
+
return () => {
|
|
29
|
+
document.removeEventListener('focusin', publish);
|
|
30
|
+
document.removeEventListener('focusout', publish);
|
|
31
|
+
};
|
|
32
|
+
},
|
|
33
|
+
}),
|
|
34
|
+
);
|
|
35
|
+
|
|
36
|
+
// The same service, twice.
|
|
37
|
+
useFocus(document.body);
|
|
38
|
+
useFocus(document.body);
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
## The key is read by meaning, not by spelling
|
|
42
|
+
|
|
43
|
+
`perTarget()` **sorts object keys at every depth and drops the keys holding `undefined`**. Arrays keep their order.
|
|
44
|
+
|
|
45
|
+
```js
|
|
46
|
+
// One service.
|
|
47
|
+
useInView(el, { threshold: 0.5, rootMargin: '0px' });
|
|
48
|
+
useInView(el, { rootMargin: '0px', threshold: 0.5 });
|
|
49
|
+
useInView(el, { threshold: 0.5, rootMargin: '0px', root: undefined });
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
That is deliberate: two callers who mean the same thing should share a source, and an options object written in a different order means the same thing.
|
|
53
|
+
|
|
54
|
+
## When you need a `keyOf`
|
|
55
|
+
|
|
56
|
+
Only what the **platform** owns:
|
|
57
|
+
|
|
58
|
+
- `useInView()` gives an object root a stable weak identity, because an element cannot be serialized into a key.
|
|
59
|
+
- `useMutation()` keeps a canonical init, because the DOM's own rules about which fields imply which are not a plain sort.
|
|
60
|
+
|
|
61
|
+
Everything else is covered by the default.
|
|
62
|
+
|
|
63
|
+
## The target is a `WeakKey`
|
|
64
|
+
|
|
65
|
+
The cache is a `WeakMap`, so a target that is collected takes its entry with it. Nothing has to be cleaned up, and **nothing groups observers across targets**.
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
# toggle
|
|
2
|
+
|
|
3
|
+
```ts
|
|
4
|
+
toggle(subscribe: () => Unsubscribe): Toggle
|
|
5
|
+
```
|
|
6
|
+
|
|
7
|
+
Turns anything that returns its own unsubscribe function into a switch.
|
|
8
|
+
|
|
9
|
+
```ts
|
|
10
|
+
interface Toggle {
|
|
11
|
+
readonly isActive: boolean;
|
|
12
|
+
start: () => void;
|
|
13
|
+
stop: () => void;
|
|
14
|
+
}
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
`start` and `stop` are **bound**, so each is a callback as it is.
|
|
18
|
+
|
|
19
|
+
## Usage
|
|
20
|
+
|
|
21
|
+
```js
|
|
22
|
+
import { Base, toggle, useRaf } from '@studiometa/js-toolkit';
|
|
23
|
+
|
|
24
|
+
class SliderItem extends Base {
|
|
25
|
+
static config = { name: 'SliderItem' };
|
|
26
|
+
|
|
27
|
+
#frame = toggle(() => useRaf().subscribe(({ delta }) => this.follow(delta)));
|
|
28
|
+
|
|
29
|
+
mounted() {
|
|
30
|
+
// `stop` is bound, so it is a cleanup as it is.
|
|
31
|
+
return this.#frame.stop;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
follow(delta) {}
|
|
35
|
+
|
|
36
|
+
onIndexChange() {
|
|
37
|
+
this.#frame.start();
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
onSettled() {
|
|
41
|
+
this.#frame.stop();
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
`start()` is idempotent and `stop()` is safe to repeat.
|
|
47
|
+
|
|
48
|
+
## What it works on
|
|
49
|
+
|
|
50
|
+
Anything whose subscribe call returns a release:
|
|
51
|
+
|
|
52
|
+
```js
|
|
53
|
+
import { signal, toggle, useScroll } from '@studiometa/js-toolkit';
|
|
54
|
+
|
|
55
|
+
const count = signal(0);
|
|
56
|
+
const el = document.body;
|
|
57
|
+
|
|
58
|
+
// a service
|
|
59
|
+
toggle(() => useScroll().subscribe(() => {}));
|
|
60
|
+
|
|
61
|
+
// a signal
|
|
62
|
+
toggle(() => count.subscribe(() => {}));
|
|
63
|
+
|
|
64
|
+
// a bare listener
|
|
65
|
+
toggle(() => {
|
|
66
|
+
const handler = () => {};
|
|
67
|
+
el.addEventListener('click', handler);
|
|
68
|
+
return () => el.removeEventListener('click', handler);
|
|
69
|
+
});
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
It knows nothing about `Base`, so it works outside a component too.
|
|
73
|
+
|
|
74
|
+
## Why it exists
|
|
75
|
+
|
|
76
|
+
The scheduler has no permanent rAF loop: it requests the next frame while a tick subscriber stays. So **a component that needs the loop for part of a cycle should not hold a subscription for the whole cycle** — and hand-rolling "unsubscribe if subscribed, and remember which" at every call site is the bug this removes.
|
|
77
|
+
|
|
78
|
+
`{ manual: true }` on a service mixin gives you the same object under `$services.<hook>`. See [Mixins](mixins.md#manual).
|
|
79
|
+
|
|
80
|
+
`smoothTo()` is a `toggle()` over `useRaf()`: one subscription however many times the target is set, started when the value has somewhere to go, released when it arrives.
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
# until
|
|
2
|
+
|
|
3
|
+
```ts
|
|
4
|
+
until<T>(service: Service<T>, predicate: (props: T) => boolean): Promise<T>
|
|
5
|
+
```
|
|
6
|
+
|
|
7
|
+
A one-shot wait on a service.
|
|
8
|
+
|
|
9
|
+
## Usage
|
|
10
|
+
|
|
11
|
+
```ts
|
|
12
|
+
import { until, useScroll } from '@studiometa/js-toolkit';
|
|
13
|
+
|
|
14
|
+
async function afterScroll() {
|
|
15
|
+
const props = await until(useScroll(), ({ isScrolling }) => !isScrolling);
|
|
16
|
+
return props.y;
|
|
17
|
+
}
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
**Parameters**
|
|
21
|
+
|
|
22
|
+
- `service` — a `Service<T>`, and nothing else.
|
|
23
|
+
- `predicate` — called with each update.
|
|
24
|
+
|
|
25
|
+
**Return value**
|
|
26
|
+
|
|
27
|
+
- a promise resolving with **a copy of the props** on the first update that matches.
|
|
28
|
+
|
|
29
|
+
## The contract
|
|
30
|
+
|
|
31
|
+
- It **releases the subscription before it resolves**, so an awaiting caller never holds a source open.
|
|
32
|
+
- It resolves with a copy, because the props object belongs to its service and is only valid for the duration of the call that received it.
|
|
33
|
+
- **It resolves on the current props when they already match**, through the service's `hasProps()` — so `await until(useScroll(), ({ isScrolling }) => !isScrolling)` on a still page returns immediately rather than waiting for a scroll that never comes.
|
|
34
|
+
|
|
35
|
+
## When to reach for it
|
|
36
|
+
|
|
37
|
+
When the code reads as a step in a sequence rather than a reaction:
|
|
38
|
+
|
|
39
|
+
```js
|
|
40
|
+
await until(useScroll(), ({ isScrolling }) => !isScrolling);
|
|
41
|
+
this.measure();
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
For a reaction, subscribe. For an ongoing condition, subscribe and act on each delivery.
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
# useBreakpoint
|
|
2
|
+
|
|
3
|
+
```ts
|
|
4
|
+
useBreakpoint(): Service<{ readonly name: string }>
|
|
5
|
+
```
|
|
6
|
+
|
|
7
|
+
The active breakpoint name, as a service. It is its **own source** — it is not derived from a resize.
|
|
8
|
+
|
|
9
|
+
## Usage
|
|
10
|
+
|
|
11
|
+
```js
|
|
12
|
+
import { useBreakpoint } from '@studiometa/js-toolkit';
|
|
13
|
+
|
|
14
|
+
const unsubscribe = useBreakpoint().subscribe(({ name }) => {
|
|
15
|
+
document.documentElement.dataset.breakpoint = name;
|
|
16
|
+
});
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
## Why it is not part of the resize service
|
|
20
|
+
|
|
21
|
+
It is backed by `matchMedia` `change` listeners, so it emits **on crossings** and it reports a change of the reader's font size — which a `ResizeObserver` on a box cannot see.
|
|
22
|
+
|
|
23
|
+
The values are in `rem`, and in a media query `rem` resolves against the **initial** font size, not against the root element.
|
|
24
|
+
|
|
25
|
+
`ResizeProps` therefore dropped `breakpoints` and `activeBreakpoints`: they were a different measurement wearing the same shape.
|
|
26
|
+
|
|
27
|
+
## The named set
|
|
28
|
+
|
|
29
|
+
See [Breakpoints](../dom/breakpoints.md) for `BREAKPOINTS`, `getBreakpoints()` and `setBreakpoints()`.
|
|
30
|
+
|
|
31
|
+
`setBreakpoints()` replaces the set and emits at once. The `MediaQueryList` objects are built once, and the active name is memoised for the length of one task — `setBreakpoints()` and the `change` handler of a running service clear that memo immediately.
|
|
32
|
+
|
|
33
|
+
## Who else uses it
|
|
34
|
+
|
|
35
|
+
- Every responsive **option** resolves through the same active-name read, but a `matchMedia` subscription only opens for a component that declares [`option<Name>Changed()`](../methods-hooks-options.md).
|
|
36
|
+
- Connected elements with a scoped `data-component` declaration share **one** reference-counted subscription to this service. A page with plain declarations opens none.
|
|
37
|
+
|
|
38
|
+
The breakpoint service is part of the core graph on every page. What it costs is a subscription, and a page that asks for nothing opens none.
|
|
39
|
+
|
|
40
|
+
## `{ immediate: true }` works here
|
|
41
|
+
|
|
42
|
+
There is always a current breakpoint.
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
# useDrag
|
|
2
|
+
|
|
3
|
+
```ts
|
|
4
|
+
useDrag(target: HTMLElement | SVGElement, options?: DragOptions): Service<DragProps>
|
|
5
|
+
```
|
|
6
|
+
|
|
7
|
+
A drag gesture, with an optional inertia phase.
|
|
8
|
+
|
|
9
|
+
## Props
|
|
10
|
+
|
|
11
|
+
```ts
|
|
12
|
+
interface DragProps {
|
|
13
|
+
readonly mode: DragMode;
|
|
14
|
+
readonly x: number;
|
|
15
|
+
readonly y: number;
|
|
16
|
+
readonly deltaX: number;
|
|
17
|
+
readonly deltaY: number;
|
|
18
|
+
readonly originX: number;
|
|
19
|
+
readonly originY: number;
|
|
20
|
+
readonly distanceX: number;
|
|
21
|
+
readonly distanceY: number;
|
|
22
|
+
readonly finalX: number;
|
|
23
|
+
readonly finalY: number;
|
|
24
|
+
}
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
`isGrabbing`, `hasInertia` and `target` are removed, and `props.MODES` with them.
|
|
28
|
+
|
|
29
|
+
## The modes
|
|
30
|
+
|
|
31
|
+
```js
|
|
32
|
+
import { DRAG_MODES } from '@studiometa/js-toolkit';
|
|
33
|
+
|
|
34
|
+
DRAG_MODES.IDLE; // 'idle'
|
|
35
|
+
DRAG_MODES.START; // 'start'
|
|
36
|
+
DRAG_MODES.DRAG; // 'drag'
|
|
37
|
+
DRAG_MODES.DROP; // 'drop'
|
|
38
|
+
DRAG_MODES.INERTIA; // 'inertia'
|
|
39
|
+
DRAG_MODES.STOP; // 'stop'
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
`idle` is new in v4. `DragMode` is derived from the frozen object — that is the pattern for every closed set of strings in the framework.
|
|
43
|
+
|
|
44
|
+
## Options
|
|
45
|
+
|
|
46
|
+
```ts
|
|
47
|
+
interface DragOptions {
|
|
48
|
+
axis?: 'x' | 'y' | 'both';
|
|
49
|
+
dampFactor?: number;
|
|
50
|
+
dragThreshold?: number;
|
|
51
|
+
inertia?: boolean;
|
|
52
|
+
}
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
The v3 spelling `dragTreshold` is fixed.
|
|
56
|
+
|
|
57
|
+
## Usage
|
|
58
|
+
|
|
59
|
+
```js
|
|
60
|
+
import { Base, DRAG_MODES, useDrag } from '@studiometa/js-toolkit';
|
|
61
|
+
|
|
62
|
+
class Slide extends Base {
|
|
63
|
+
static config = { name: 'Slide' };
|
|
64
|
+
|
|
65
|
+
mounted() {
|
|
66
|
+
return useDrag(this.$el, { axis: 'x' }).subscribe(({ mode, x, finalX }) => {
|
|
67
|
+
if (mode === DRAG_MODES.DROP) {
|
|
68
|
+
this.settle(finalX);
|
|
69
|
+
return;
|
|
70
|
+
}
|
|
71
|
+
this.$el.style.transform = `translateX(${x}px)`;
|
|
72
|
+
});
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
settle(x) {}
|
|
76
|
+
}
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
## A gesture the browser can steal is not a gesture
|
|
80
|
+
|
|
81
|
+
`useDrag` owns both axes by default and writes `touch-action: none`.
|
|
82
|
+
|
|
83
|
+
| `axis` | writes | and |
|
|
84
|
+
| -------- | ------- | ----------------------------- |
|
|
85
|
+
| `'both'` | `none` | — |
|
|
86
|
+
| `'x'` | `pan-y` | every Y movement prop is zero |
|
|
87
|
+
| `'y'` | `pan-x` | every X movement prop is zero |
|
|
88
|
+
|
|
89
|
+
It writes **only when the computed value is `auto`**, and it restores the previous inline value when the last service of those options leaves. Services on one target share that ownership.
|
|
90
|
+
|
|
91
|
+
The click that ends a drag is suppressed from a flag that movement on the owned axis arms and the next `pointerdown` disarms — and only for a **trusted** click with a non-zero `detail`.
|
|
92
|
+
|
|
93
|
+
## Inertia
|
|
94
|
+
|
|
95
|
+
With `{ inertia: false }` a drag publishes the **exact projected destination** at `drop`, then goes through `stop` to `idle` with no tick subscription at all.
|
|
96
|
+
|
|
97
|
+
With inertia, the coast subscribes to `scheduler.tick()`. Decay is expressed in time, not in frames:
|
|
98
|
+
|
|
99
|
+
- `INERTIA_FRAME` (16.67 ms) is the reference of every factor;
|
|
100
|
+
- `inertiaStep()` integrates the decay across the step, so any sequence of frames sums to `velocity · τ` exactly;
|
|
101
|
+
- the settle position is `value + velocity · τ` with `τ = INERTIA_FRAME / ln(1 / damp)`.
|
|
102
|
+
|
|
103
|
+
The velocity is sampled as a distance over the interval between events, smoothed, with the interval clamped at both ends. **At the drop, the velocity is decayed by the idle time through the same law** — so a pointer that stopped moving before release does not fling.
|
|
104
|
+
|
|
105
|
+
## Keying
|
|
106
|
+
|
|
107
|
+
`useDrag()` keys its axis, inertia, damping and threshold, so two callers asking for the same gesture on the same element share one service. See [`perTarget()`](perTarget.md).
|
|
108
|
+
|
|
109
|
+
## `{ immediate: true }`
|
|
110
|
+
|
|
111
|
+
Does nothing outside a gesture: there is no current value between gestures.
|
|
112
|
+
|
|
113
|
+
## Mixin
|
|
114
|
+
|
|
115
|
+
```js
|
|
116
|
+
class Slide extends withDrag(Base, { axis: 'x', inertia: false }) {
|
|
117
|
+
dragged({ mode, x }) {}
|
|
118
|
+
}
|
|
119
|
+
```
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
# useInView
|
|
2
|
+
|
|
3
|
+
```ts
|
|
4
|
+
useInView(target: Element, init?: IntersectionObserverInit): Service<InViewProps>
|
|
5
|
+
```
|
|
6
|
+
|
|
7
|
+
An `IntersectionObserver` as a service.
|
|
8
|
+
|
|
9
|
+
## Props
|
|
10
|
+
|
|
11
|
+
```ts
|
|
12
|
+
interface InViewProps {
|
|
13
|
+
readonly isInView: boolean;
|
|
14
|
+
readonly entry: IntersectionObserverEntry | null;
|
|
15
|
+
}
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
## Usage
|
|
19
|
+
|
|
20
|
+
```js
|
|
21
|
+
import { Base, useInView } from '@studiometa/js-toolkit';
|
|
22
|
+
|
|
23
|
+
class Reveal extends Base {
|
|
24
|
+
static config = { name: 'Reveal' };
|
|
25
|
+
|
|
26
|
+
mounted() {
|
|
27
|
+
return useInView(this.$el, { threshold: 0.25 }).subscribe(({ isInView }) => {
|
|
28
|
+
this.$el.classList.toggle('is-in-view', isInView);
|
|
29
|
+
});
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
## Keying
|
|
35
|
+
|
|
36
|
+
`useInView()` keys **every `IntersectionObserverInit` field**, and gives an object root a stable weak identity so two callers passing the same root element share one service.
|
|
37
|
+
|
|
38
|
+
The options are read by meaning, not by spelling: keys are sorted at every depth and the ones holding `undefined` are dropped, so `{ threshold: 0.5, rootMargin: '0px' }` and `{ rootMargin: '0px', threshold: 0.5 }` are the same service.
|
|
39
|
+
|
|
40
|
+
**Nothing groups observers across targets.**
|
|
41
|
+
|
|
42
|
+
## `{ immediate: true }` is the default here
|
|
43
|
+
|
|
44
|
+
`withInView` defaults it to `true`, because "am I in view" has a current answer and a consumer that has to wait for the first crossing to find out would be wrong on load.
|
|
45
|
+
|
|
46
|
+
## This is not a mount strategy
|
|
47
|
+
|
|
48
|
+
`useInView` and `withInView` observe a component that is **already mounted**. They do not replace `data-mount="visible"` or `data-mount="in-view"`, which decide whether the instance exists at all.
|
|
49
|
+
|
|
50
|
+
| Want | Use |
|
|
51
|
+
| ---------------------------------------------- | ---------------------------- |
|
|
52
|
+
| the component to exist only once seen | `data-mount="visible"` |
|
|
53
|
+
| the component to come and go with the viewport | `data-mount="in-view"` |
|
|
54
|
+
| a mounted component to react to crossings | `useInView()` / `withInView` |
|
|
55
|
+
|
|
56
|
+
See [Mount strategies](../../guide/going-further/mount-strategies.md).
|
|
57
|
+
|
|
58
|
+
## Mixin
|
|
59
|
+
|
|
60
|
+
```js
|
|
61
|
+
class Reveal extends withInView(Base, { threshold: 0.25 }) {
|
|
62
|
+
intersected({ isInView }) {}
|
|
63
|
+
}
|
|
64
|
+
```
|