@studiometa/js-toolkit 3.0.2 → 3.0.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.
@@ -5,10 +5,9 @@ import {
5
5
  clamp01,
6
6
  getOffsetSizes,
7
7
  isFunction,
8
- useScheduler
8
+ domScheduler as scheduler
9
9
  } from "../../utils/index.js";
10
10
  import { normalizeOffset, getEdges } from "./utils.js";
11
- const scheduler = useScheduler(["update", "render"]);
12
11
  function updateProgress(props, axis, progressKey, currentKey) {
13
12
  props[progressKey][axis] = props.start[axis] === props.end[axis] ? 0 : clamp01(
14
13
  (props[currentKey][axis] - props.start[axis]) / (props.end[axis] - props.start[axis])
@@ -122,10 +121,10 @@ function withScrolledInView(BaseClass, options = {}) {
122
121
  constructor(element) {
123
122
  super(element);
124
123
  const render = () => {
125
- scheduler.update(() => {
124
+ scheduler.read(() => {
126
125
  const renderFn = this.__callMethod("scrolledInView", this.props);
127
126
  if (isFunction(renderFn)) {
128
- scheduler.render(() => {
127
+ scheduler.write(() => {
129
128
  renderFn(this.__props);
130
129
  });
131
130
  }
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../packages/js-toolkit/decorators/withScrolledInView/withScrolledInView.ts"],
4
- "sourcesContent": ["import type { BaseInterface, BaseDecorator } from '../../Base/types.js';\nimport type { Base, BaseProps, BaseConfig } from '../../Base/index.js';\nimport type {\n RafServiceProps,\n ScrollServiceProps,\n ResizeServiceProps,\n} from '../../services/index.js';\nimport { withMountWhenInView } from '../withMountWhenInView.js';\nimport {\n damp,\n clamp,\n clamp01,\n getOffsetSizes,\n isFunction,\n useScheduler,\n} from '../../utils/index.js';\nimport { normalizeOffset, getEdges } from './utils.js';\n\nconst scheduler = useScheduler(['update', 'render']);\n\nexport interface WithScrolledInViewProps extends BaseProps {\n $options: {\n dampFactor: number;\n dampPrecision: number;\n offset: string;\n };\n}\n\nexport type ScrollInViewProps = {\n start: {\n x: number;\n y: number;\n };\n end: {\n x: number;\n y: number;\n };\n current: {\n x: number;\n y: number;\n };\n dampedCurrent: {\n x: number;\n y: number;\n };\n progress: {\n x: number;\n y: number;\n };\n dampedProgress: {\n x: number;\n y: number;\n };\n};\n\nexport type WithScrolledInViewOptions = IntersectionObserverInit & {\n useOffsetSizes?: boolean;\n};\n\nfunction updateProgress(\n props: ScrollInViewProps,\n axis: 'x' | 'y',\n progressKey: 'progress' | 'dampedProgress',\n currentKey: 'current' | 'dampedCurrent',\n) {\n props[progressKey][axis] =\n props.start[axis] === props.end[axis]\n ? 0\n : clamp01(\n (props[currentKey][axis] - props.start[axis]) / (props.end[axis] - props.start[axis]),\n );\n}\n\n/**\n * Update props on tick.\n */\nfunction updateProps(\n props: ScrollInViewProps,\n dampFactor: number,\n dampPrecision: number,\n axis: 'x' | 'y' = 'x',\n): void {\n props.current[axis] = clamp(\n axis === 'x' ? window.scrollX : window.scrollY,\n props.start[axis],\n props.end[axis],\n );\n props.dampedCurrent[axis] = damp(\n props.current[axis],\n props.dampedCurrent[axis],\n dampFactor,\n dampPrecision,\n );\n updateProgress(props, axis, 'progress', 'current');\n updateProgress(props, axis, 'dampedProgress', 'dampedCurrent');\n}\n\nexport interface WithScrolledInViewInterface extends BaseInterface {\n /**\n * @type {ScrollInViewProps}\n */\n readonly props: ScrollInViewProps;\n dampFactor?: number;\n dampPrecision?: number;\n mounted(): void;\n resized(props: ResizeServiceProps): void;\n scrolled(props: ScrollServiceProps): void;\n ticked(props: RafServiceProps): void;\n destroyed(): void;\n}\n\n/**\n * Add scrolled in view capabilities to a component.\n */\nexport function withScrolledInView<S extends Base = Base>(\n BaseClass: typeof Base,\n options: WithScrolledInViewOptions = {},\n): BaseDecorator<WithScrolledInViewInterface, S, WithScrolledInViewProps> {\n /**\n * Class.\n */\n class WithScrolledInView<T extends BaseProps = BaseProps> extends withMountWhenInView(\n BaseClass,\n options,\n )<T & WithScrolledInViewProps> {\n /**\n * Config.\n */\n static config: BaseConfig = {\n ...BaseClass.config,\n emits: ['scrolledInView'],\n options: {\n dampFactor: {\n type: Number,\n default: 0.1,\n },\n dampPrecision: {\n type: Number,\n default: 0.001,\n },\n offset: {\n type: String,\n default: 'start end / end start',\n },\n },\n };\n\n __props: ScrollInViewProps = {\n start: {\n x: 0,\n y: 0,\n },\n end: {\n x: 0,\n y: 0,\n },\n current: {\n x: 0,\n y: 0,\n },\n dampedCurrent: {\n x: 0,\n y: 0,\n },\n progress: {\n x: 0,\n y: 0,\n },\n dampedProgress: {\n x: 0,\n y: 0,\n },\n };\n\n shouldEvaluateProps = true;\n\n get props() {\n if (this.shouldEvaluateProps) {\n this.shouldEvaluateProps = false;\n const targetSizes = options.useOffsetSizes\n ? getOffsetSizes(this.$el)\n : this.$el.getBoundingClientRect();\n\n targetSizes.y += window.scrollY;\n targetSizes.x += window.scrollX;\n\n const containerSizes = {\n x: 0,\n y: 0,\n height: window.innerHeight,\n width: window.innerWidth,\n };\n\n const offset = normalizeOffset(this.$options.offset);\n\n // Y axis\n const [yStart, yEnd] = getEdges('y', targetSizes, containerSizes, offset);\n const yCurrent = clamp(window.scrollY, yStart, yEnd);\n const yProgress = yStart === yEnd ? 0 : clamp01((yCurrent - yStart) / (yEnd - yStart));\n // X axis\n const [xStart, xEnd] = getEdges('x', targetSizes, containerSizes, offset);\n const xCurrent = clamp(window.scrollX, xStart, xEnd);\n const xProgress = xStart === xEnd ? 0 : clamp01((xCurrent - xStart) / (xEnd - xStart));\n\n this.__props.start.x = xStart;\n this.__props.start.y = yStart;\n this.__props.end.x = xEnd;\n this.__props.end.y = yEnd;\n this.__props.current.x = xCurrent;\n this.__props.current.y = yCurrent;\n this.__props.dampedCurrent.x = xCurrent;\n this.__props.dampedCurrent.y = yCurrent;\n this.__props.progress.x = xProgress;\n this.__props.progress.y = yProgress;\n this.__props.dampedProgress.x = xProgress;\n this.__props.dampedProgress.y = yProgress;\n }\n\n return this.__props;\n }\n\n /**\n * Bind listeners.\n */\n constructor(element: HTMLElement) {\n super(element);\n\n const render = () => {\n scheduler.update(() => {\n // @ts-ignore\n const renderFn = this.__callMethod('scrolledInView', this.props);\n if (isFunction(renderFn)) {\n scheduler.render(() => {\n renderFn(this.__props);\n });\n }\n });\n };\n\n const delegate = {\n handleEvent(event: CustomEvent) {\n delegate[event.type](event.detail[0]);\n },\n resized: () => {\n this.shouldEvaluateProps = true;\n render();\n },\n scrolled: (props: ScrollServiceProps) => {\n if (props.changed.y || props.changed.x) {\n this.$services.enable('ticked');\n }\n },\n mounted: () => render(),\n ticked: () => {\n const { dampFactor, dampPrecision } = this.$options;\n updateProps(this.__props, dampFactor, dampPrecision, 'x');\n updateProps(this.__props, dampFactor, dampPrecision, 'y');\n\n if (\n this.__props.dampedCurrent.x === this.__props.current.x &&\n this.__props.dampedCurrent.y === this.__props.current.y\n ) {\n this.$services.disable('ticked');\n }\n\n render();\n },\n };\n\n this.$on('before-mounted', () => {\n this.$on('mounted', delegate);\n this.$on('resized', delegate);\n this.$on('scrolled', delegate);\n this.$on('ticked', delegate);\n });\n\n this.$on('destroyed', () => {\n this.$off('mounted', delegate);\n this.$off('resized', delegate);\n this.$off('scrolled', delegate);\n this.$off('ticked', delegate);\n\n // Clamp damped values to their final value and trigger one last render on destroy\n this.__props.dampedCurrent.x = this.__props.current.x;\n this.__props.dampedCurrent.y = this.__props.current.y;\n this.__props.dampedProgress.x = this.__props.progress.x;\n this.__props.dampedProgress.y = this.__props.progress.y;\n render();\n });\n }\n }\n\n // @ts-ignore\n return WithScrolledInView;\n}\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["import type { BaseInterface, BaseDecorator } from '../../Base/types.js';\nimport type { Base, BaseProps, BaseConfig } from '../../Base/index.js';\nimport type {\n RafServiceProps,\n ScrollServiceProps,\n ResizeServiceProps,\n} from '../../services/index.js';\nimport { withMountWhenInView } from '../withMountWhenInView.js';\nimport {\n damp,\n clamp,\n clamp01,\n getOffsetSizes,\n isFunction,\n domScheduler as scheduler,\n} from '../../utils/index.js';\nimport { normalizeOffset, getEdges } from './utils.js';\n\nexport interface WithScrolledInViewProps extends BaseProps {\n $options: {\n dampFactor: number;\n dampPrecision: number;\n offset: string;\n };\n}\n\nexport type ScrollInViewProps = {\n start: {\n x: number;\n y: number;\n };\n end: {\n x: number;\n y: number;\n };\n current: {\n x: number;\n y: number;\n };\n dampedCurrent: {\n x: number;\n y: number;\n };\n progress: {\n x: number;\n y: number;\n };\n dampedProgress: {\n x: number;\n y: number;\n };\n};\n\nexport type WithScrolledInViewOptions = IntersectionObserverInit & {\n useOffsetSizes?: boolean;\n};\n\nfunction updateProgress(\n props: ScrollInViewProps,\n axis: 'x' | 'y',\n progressKey: 'progress' | 'dampedProgress',\n currentKey: 'current' | 'dampedCurrent',\n) {\n props[progressKey][axis] =\n props.start[axis] === props.end[axis]\n ? 0\n : clamp01(\n (props[currentKey][axis] - props.start[axis]) / (props.end[axis] - props.start[axis]),\n );\n}\n\n/**\n * Update props on tick.\n */\nfunction updateProps(\n props: ScrollInViewProps,\n dampFactor: number,\n dampPrecision: number,\n axis: 'x' | 'y' = 'x',\n): void {\n props.current[axis] = clamp(\n axis === 'x' ? window.scrollX : window.scrollY,\n props.start[axis],\n props.end[axis],\n );\n props.dampedCurrent[axis] = damp(\n props.current[axis],\n props.dampedCurrent[axis],\n dampFactor,\n dampPrecision,\n );\n updateProgress(props, axis, 'progress', 'current');\n updateProgress(props, axis, 'dampedProgress', 'dampedCurrent');\n}\n\nexport interface WithScrolledInViewInterface extends BaseInterface {\n /**\n * @type {ScrollInViewProps}\n */\n readonly props: ScrollInViewProps;\n dampFactor?: number;\n dampPrecision?: number;\n mounted(): void;\n resized(props: ResizeServiceProps): void;\n scrolled(props: ScrollServiceProps): void;\n ticked(props: RafServiceProps): void;\n destroyed(): void;\n}\n\n/**\n * Add scrolled in view capabilities to a component.\n */\nexport function withScrolledInView<S extends Base = Base>(\n BaseClass: typeof Base,\n options: WithScrolledInViewOptions = {},\n): BaseDecorator<WithScrolledInViewInterface, S, WithScrolledInViewProps> {\n /**\n * Class.\n */\n class WithScrolledInView<T extends BaseProps = BaseProps> extends withMountWhenInView(\n BaseClass,\n options,\n )<T & WithScrolledInViewProps> {\n /**\n * Config.\n */\n static config: BaseConfig = {\n ...BaseClass.config,\n emits: ['scrolledInView'],\n options: {\n dampFactor: {\n type: Number,\n default: 0.1,\n },\n dampPrecision: {\n type: Number,\n default: 0.001,\n },\n offset: {\n type: String,\n default: 'start end / end start',\n },\n },\n };\n\n __props: ScrollInViewProps = {\n start: {\n x: 0,\n y: 0,\n },\n end: {\n x: 0,\n y: 0,\n },\n current: {\n x: 0,\n y: 0,\n },\n dampedCurrent: {\n x: 0,\n y: 0,\n },\n progress: {\n x: 0,\n y: 0,\n },\n dampedProgress: {\n x: 0,\n y: 0,\n },\n };\n\n shouldEvaluateProps = true;\n\n get props() {\n if (this.shouldEvaluateProps) {\n this.shouldEvaluateProps = false;\n const targetSizes = options.useOffsetSizes\n ? getOffsetSizes(this.$el)\n : this.$el.getBoundingClientRect();\n\n targetSizes.y += window.scrollY;\n targetSizes.x += window.scrollX;\n\n const containerSizes = {\n x: 0,\n y: 0,\n height: window.innerHeight,\n width: window.innerWidth,\n };\n\n const offset = normalizeOffset(this.$options.offset);\n\n // Y axis\n const [yStart, yEnd] = getEdges('y', targetSizes, containerSizes, offset);\n const yCurrent = clamp(window.scrollY, yStart, yEnd);\n const yProgress = yStart === yEnd ? 0 : clamp01((yCurrent - yStart) / (yEnd - yStart));\n // X axis\n const [xStart, xEnd] = getEdges('x', targetSizes, containerSizes, offset);\n const xCurrent = clamp(window.scrollX, xStart, xEnd);\n const xProgress = xStart === xEnd ? 0 : clamp01((xCurrent - xStart) / (xEnd - xStart));\n\n this.__props.start.x = xStart;\n this.__props.start.y = yStart;\n this.__props.end.x = xEnd;\n this.__props.end.y = yEnd;\n this.__props.current.x = xCurrent;\n this.__props.current.y = yCurrent;\n this.__props.dampedCurrent.x = xCurrent;\n this.__props.dampedCurrent.y = yCurrent;\n this.__props.progress.x = xProgress;\n this.__props.progress.y = yProgress;\n this.__props.dampedProgress.x = xProgress;\n this.__props.dampedProgress.y = yProgress;\n }\n\n return this.__props;\n }\n\n /**\n * Bind listeners.\n */\n constructor(element: HTMLElement) {\n super(element);\n\n const render = () => {\n scheduler.read(() => {\n // @ts-ignore\n const renderFn = this.__callMethod('scrolledInView', this.props);\n if (isFunction(renderFn)) {\n scheduler.write(() => {\n renderFn(this.__props);\n });\n }\n });\n };\n\n const delegate = {\n handleEvent(event: CustomEvent) {\n delegate[event.type](event.detail[0]);\n },\n resized: () => {\n this.shouldEvaluateProps = true;\n render();\n },\n scrolled: (props: ScrollServiceProps) => {\n if (props.changed.y || props.changed.x) {\n this.$services.enable('ticked');\n }\n },\n mounted: () => render(),\n ticked: () => {\n const { dampFactor, dampPrecision } = this.$options;\n updateProps(this.__props, dampFactor, dampPrecision, 'x');\n updateProps(this.__props, dampFactor, dampPrecision, 'y');\n\n if (\n this.__props.dampedCurrent.x === this.__props.current.x &&\n this.__props.dampedCurrent.y === this.__props.current.y\n ) {\n this.$services.disable('ticked');\n }\n\n render();\n },\n };\n\n this.$on('before-mounted', () => {\n this.$on('mounted', delegate);\n this.$on('resized', delegate);\n this.$on('scrolled', delegate);\n this.$on('ticked', delegate);\n });\n\n this.$on('destroyed', () => {\n this.$off('mounted', delegate);\n this.$off('resized', delegate);\n this.$off('scrolled', delegate);\n this.$off('ticked', delegate);\n\n // Clamp damped values to their final value and trigger one last render on destroy\n this.__props.dampedCurrent.x = this.__props.current.x;\n this.__props.dampedCurrent.y = this.__props.current.y;\n this.__props.dampedProgress.x = this.__props.progress.x;\n this.__props.dampedProgress.y = this.__props.progress.y;\n render();\n });\n }\n }\n\n // @ts-ignore\n return WithScrolledInView;\n}\n"],
5
+ "mappings": "AAOA,SAAS,2BAA2B;AACpC;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA,gBAAgB;AAAA,OACX;AACP,SAAS,iBAAiB,gBAAgB;AAyC1C,SAAS,eACP,OACA,MACA,aACA,YACA;AACA,QAAM,WAAW,EAAE,IAAI,IACrB,MAAM,MAAM,IAAI,MAAM,MAAM,IAAI,IAAI,IAChC,IACA;AAAA,KACG,MAAM,UAAU,EAAE,IAAI,IAAI,MAAM,MAAM,IAAI,MAAM,MAAM,IAAI,IAAI,IAAI,MAAM,MAAM,IAAI;AAAA,EACrF;AACR;AAKA,SAAS,YACP,OACA,YACA,eACA,OAAkB,KACZ;AACN,QAAM,QAAQ,IAAI,IAAI;AAAA,IACpB,SAAS,MAAM,OAAO,UAAU,OAAO;AAAA,IACvC,MAAM,MAAM,IAAI;AAAA,IAChB,MAAM,IAAI,IAAI;AAAA,EAChB;AACA,QAAM,cAAc,IAAI,IAAI;AAAA,IAC1B,MAAM,QAAQ,IAAI;AAAA,IAClB,MAAM,cAAc,IAAI;AAAA,IACxB;AAAA,IACA;AAAA,EACF;AACA,iBAAe,OAAO,MAAM,YAAY,SAAS;AACjD,iBAAe,OAAO,MAAM,kBAAkB,eAAe;AAC/D;AAmBO,SAAS,mBACd,WACA,UAAqC,CAAC,GACkC;AAAA,EAIxE,MAAM,2BAA4D;AAAA,IAChE;AAAA,IACA;AAAA,EACF,EAA+B;AAAA;AAAA;AAAA;AAAA,IAI7B,OAAO,SAAqB;AAAA,MAC1B,GAAG,UAAU;AAAA,MACb,OAAO,CAAC,gBAAgB;AAAA,MACxB,SAAS;AAAA,QACP,YAAY;AAAA,UACV,MAAM;AAAA,UACN,SAAS;AAAA,QACX;AAAA,QACA,eAAe;AAAA,UACb,MAAM;AAAA,UACN,SAAS;AAAA,QACX;AAAA,QACA,QAAQ;AAAA,UACN,MAAM;AAAA,UACN,SAAS;AAAA,QACX;AAAA,MACF;AAAA,IACF;AAAA,IAEA,UAA6B;AAAA,MAC3B,OAAO;AAAA,QACL,GAAG;AAAA,QACH,GAAG;AAAA,MACL;AAAA,MACA,KAAK;AAAA,QACH,GAAG;AAAA,QACH,GAAG;AAAA,MACL;AAAA,MACA,SAAS;AAAA,QACP,GAAG;AAAA,QACH,GAAG;AAAA,MACL;AAAA,MACA,eAAe;AAAA,QACb,GAAG;AAAA,QACH,GAAG;AAAA,MACL;AAAA,MACA,UAAU;AAAA,QACR,GAAG;AAAA,QACH,GAAG;AAAA,MACL;AAAA,MACA,gBAAgB;AAAA,QACd,GAAG;AAAA,QACH,GAAG;AAAA,MACL;AAAA,IACF;AAAA,IAEA,sBAAsB;AAAA,IAEtB,IAAI,QAAQ;AACV,UAAI,KAAK,qBAAqB;AAC5B,aAAK,sBAAsB;AAC3B,cAAM,cAAc,QAAQ,iBACxB,eAAe,KAAK,GAAG,IACvB,KAAK,IAAI,sBAAsB;AAEnC,oBAAY,KAAK,OAAO;AACxB,oBAAY,KAAK,OAAO;AAExB,cAAM,iBAAiB;AAAA,UACrB,GAAG;AAAA,UACH,GAAG;AAAA,UACH,QAAQ,OAAO;AAAA,UACf,OAAO,OAAO;AAAA,QAChB;AAEA,cAAM,SAAS,gBAAgB,KAAK,SAAS,MAAM;AAGnD,cAAM,CAAC,QAAQ,IAAI,IAAI,SAAS,KAAK,aAAa,gBAAgB,MAAM;AACxE,cAAM,WAAW,MAAM,OAAO,SAAS,QAAQ,IAAI;AACnD,cAAM,YAAY,WAAW,OAAO,IAAI,SAAS,WAAW,WAAW,OAAO,OAAO;AAErF,cAAM,CAAC,QAAQ,IAAI,IAAI,SAAS,KAAK,aAAa,gBAAgB,MAAM;AACxE,cAAM,WAAW,MAAM,OAAO,SAAS,QAAQ,IAAI;AACnD,cAAM,YAAY,WAAW,OAAO,IAAI,SAAS,WAAW,WAAW,OAAO,OAAO;AAErF,aAAK,QAAQ,MAAM,IAAI;AACvB,aAAK,QAAQ,MAAM,IAAI;AACvB,aAAK,QAAQ,IAAI,IAAI;AACrB,aAAK,QAAQ,IAAI,IAAI;AACrB,aAAK,QAAQ,QAAQ,IAAI;AACzB,aAAK,QAAQ,QAAQ,IAAI;AACzB,aAAK,QAAQ,cAAc,IAAI;AAC/B,aAAK,QAAQ,cAAc,IAAI;AAC/B,aAAK,QAAQ,SAAS,IAAI;AAC1B,aAAK,QAAQ,SAAS,IAAI;AAC1B,aAAK,QAAQ,eAAe,IAAI;AAChC,aAAK,QAAQ,eAAe,IAAI;AAAA,MAClC;AAEA,aAAO,KAAK;AAAA,IACd;AAAA;AAAA;AAAA;AAAA,IAKA,YAAY,SAAsB;AAChC,YAAM,OAAO;AAEb,YAAM,SAAS,MAAM;AACnB,kBAAU,KAAK,MAAM;AAEnB,gBAAM,WAAW,KAAK,aAAa,kBAAkB,KAAK,KAAK;AAC/D,cAAI,WAAW,QAAQ,GAAG;AACxB,sBAAU,MAAM,MAAM;AACpB,uBAAS,KAAK,OAAO;AAAA,YACvB,CAAC;AAAA,UACH;AAAA,QACF,CAAC;AAAA,MACH;AAEA,YAAM,WAAW;AAAA,QACf,YAAY,OAAoB;AAC9B,mBAAS,MAAM,IAAI,EAAE,MAAM,OAAO,CAAC,CAAC;AAAA,QACtC;AAAA,QACA,SAAS,MAAM;AACb,eAAK,sBAAsB;AAC3B,iBAAO;AAAA,QACT;AAAA,QACA,UAAU,CAAC,UAA8B;AACvC,cAAI,MAAM,QAAQ,KAAK,MAAM,QAAQ,GAAG;AACtC,iBAAK,UAAU,OAAO,QAAQ;AAAA,UAChC;AAAA,QACF;AAAA,QACA,SAAS,MAAM,OAAO;AAAA,QACtB,QAAQ,MAAM;AACZ,gBAAM,EAAE,YAAY,cAAc,IAAI,KAAK;AAC3C,sBAAY,KAAK,SAAS,YAAY,eAAe,GAAG;AACxD,sBAAY,KAAK,SAAS,YAAY,eAAe,GAAG;AAExD,cACE,KAAK,QAAQ,cAAc,MAAM,KAAK,QAAQ,QAAQ,KACtD,KAAK,QAAQ,cAAc,MAAM,KAAK,QAAQ,QAAQ,GACtD;AACA,iBAAK,UAAU,QAAQ,QAAQ;AAAA,UACjC;AAEA,iBAAO;AAAA,QACT;AAAA,MACF;AAEA,WAAK,IAAI,kBAAkB,MAAM;AAC/B,aAAK,IAAI,WAAW,QAAQ;AAC5B,aAAK,IAAI,WAAW,QAAQ;AAC5B,aAAK,IAAI,YAAY,QAAQ;AAC7B,aAAK,IAAI,UAAU,QAAQ;AAAA,MAC7B,CAAC;AAED,WAAK,IAAI,aAAa,MAAM;AAC1B,aAAK,KAAK,WAAW,QAAQ;AAC7B,aAAK,KAAK,WAAW,QAAQ;AAC7B,aAAK,KAAK,YAAY,QAAQ;AAC9B,aAAK,KAAK,UAAU,QAAQ;AAG5B,aAAK,QAAQ,cAAc,IAAI,KAAK,QAAQ,QAAQ;AACpD,aAAK,QAAQ,cAAc,IAAI,KAAK,QAAQ,QAAQ;AACpD,aAAK,QAAQ,eAAe,IAAI,KAAK,QAAQ,SAAS;AACtD,aAAK,QAAQ,eAAe,IAAI,KAAK,QAAQ,SAAS;AACtD,eAAO;AAAA,MACT,CAAC;AAAA,IACH;AAAA,EACF;AAGA,SAAO;AACT;",
6
6
  "names": []
7
7
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@studiometa/js-toolkit",
3
- "version": "3.0.2",
3
+ "version": "3.0.3",
4
4
  "description": "A set of useful little bits of JavaScript to boost your project! 🚀",
5
5
  "publishConfig": {
6
6
  "access": "public"
@@ -1,11 +1,20 @@
1
1
  import type { ServiceConfig, ServiceInterface } from './AbstractService.js';
2
2
  import { AbstractService } from './AbstractService.js';
3
- type DragLifecycle = 'start' | 'drag' | 'drop' | 'inertia' | 'stop';
4
3
  export interface DragServiceProps {
5
4
  /**
6
5
  * The current mode of the dragging logic.
7
6
  */
8
- mode: DragLifecycle;
7
+ mode: 'start' | 'drag' | 'drop' | 'inertia' | 'stop';
8
+ /**
9
+ * Available modes.
10
+ */
11
+ MODES: {
12
+ START: 'start';
13
+ DRAG: 'drag';
14
+ DROP: 'drop';
15
+ INERTIA: 'inertia';
16
+ STOP: 'stop';
17
+ };
9
18
  /**
10
19
  * The target element of the drag.
11
20
  */
@@ -61,7 +70,6 @@ export interface DragServiceOptions {
61
70
  dragTreshold?: number;
62
71
  }
63
72
  export declare class DragService extends AbstractService<DragServiceProps> {
64
- static MODES: Record<Uppercase<DragLifecycle>, DragLifecycle>;
65
73
  static config: ServiceConfig;
66
74
  id: string;
67
75
  dampFactor: number;
@@ -121,4 +129,3 @@ export declare class DragService extends AbstractService<DragServiceProps> {
121
129
  * ```
122
130
  */
123
131
  export declare function useDrag(target: HTMLElement, options: DragServiceOptions): DragServiceInterface;
124
- export {};
@@ -5,13 +5,6 @@ import { inertiaFinalValue } from "../utils/math/index.js";
5
5
  import { PASSIVE_EVENT_OPTIONS, CAPTURE_EVENT_OPTIONS } from "./utils.js";
6
6
  let count = 0;
7
7
  class DragService extends AbstractService {
8
- static MODES = {
9
- START: "start",
10
- DRAG: "drag",
11
- DROP: "drop",
12
- INERTIA: "inertia",
13
- STOP: "stop"
14
- };
15
8
  static config = [
16
9
  [
17
10
  (instance) => instance.props.target,
@@ -36,6 +29,13 @@ class DragService extends AbstractService {
36
29
  props = {
37
30
  target: null,
38
31
  mode: void 0,
32
+ MODES: {
33
+ START: "start",
34
+ DRAG: "drag",
35
+ DROP: "drop",
36
+ INERTIA: "inertia",
37
+ STOP: "stop"
38
+ },
39
39
  isGrabbing: false,
40
40
  hasInertia: false,
41
41
  x: 0,
@@ -96,7 +96,7 @@ class DragService extends AbstractService {
96
96
  props.y = props.origin.y = props.final.y = y;
97
97
  props.delta.x = props.distance.x = 0;
98
98
  props.delta.y = props.distance.y = 0;
99
- props.mode = DragService.MODES.START;
99
+ props.mode = props.MODES.START;
100
100
  props.isGrabbing = true;
101
101
  this.trigger(props);
102
102
  document.addEventListener("touchmove", this, PASSIVE_EVENT_OPTIONS);
@@ -113,7 +113,7 @@ class DragService extends AbstractService {
113
113
  document.removeEventListener("touchmove", this);
114
114
  document.removeEventListener("mousemove", this);
115
115
  props.isGrabbing = false;
116
- props.mode = DragService.MODES.DROP;
116
+ props.mode = props.MODES.DROP;
117
117
  props.hasInertia = true;
118
118
  props.final.x = inertiaFinalValue(props.x, props.delta.x, dampFactor);
119
119
  props.final.y = inertiaFinalValue(props.y, props.delta.y, dampFactor);
@@ -133,7 +133,7 @@ class DragService extends AbstractService {
133
133
  useRaf().remove(id);
134
134
  props.isGrabbing = false;
135
135
  props.hasInertia = false;
136
- props.mode = DragService.MODES.STOP;
136
+ props.mode = props.MODES.STOP;
137
137
  this.trigger(props);
138
138
  }
139
139
  /**
@@ -148,7 +148,7 @@ class DragService extends AbstractService {
148
148
  props.distance.y = props.y - props.origin.y;
149
149
  props.delta.x *= dampFactor;
150
150
  props.delta.y *= dampFactor;
151
- props.mode = DragService.MODES.INERTIA;
151
+ props.mode = props.MODES.INERTIA;
152
152
  this.trigger(props);
153
153
  if (Math.abs(props.delta.x) < 0.1 && Math.abs(props.delta.y) < 0.1) {
154
154
  this.stop();
@@ -173,7 +173,7 @@ class DragService extends AbstractService {
173
173
  props.final.y = position.y;
174
174
  props.distance.x = props.x - props.origin.x;
175
175
  props.distance.y = props.y - props.origin.y;
176
- props.mode = DragService.MODES.DRAG;
176
+ props.mode = props.MODES.DRAG;
177
177
  this.trigger(props);
178
178
  this.previousEvent = event;
179
179
  }
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../packages/js-toolkit/services/DragService.ts"],
4
- "sourcesContent": ["import type { ServiceConfig, ServiceInterface } from './AbstractService.js';\nimport { AbstractService } from './AbstractService.js';\nimport { useRaf } from './RafService.js';\nimport { isDefined } from '../utils/is.js';\nimport { inertiaFinalValue } from '../utils/math/index.js';\nimport { PASSIVE_EVENT_OPTIONS, CAPTURE_EVENT_OPTIONS } from './utils.js';\n\ntype DragLifecycle = 'start' | 'drag' | 'drop' | 'inertia' | 'stop';\n\nexport interface DragServiceProps {\n /**\n * The current mode of the dragging logic.\n */\n mode: DragLifecycle;\n /**\n * The target element of the drag.\n */\n target: HTMLElement;\n /**\n * Whether the user is currently grabbing the targeted element or not.\n */\n isGrabbing: boolean;\n /**\n * Whether the drag currently has inertia or not.\n */\n hasInertia: boolean;\n /**\n * The current horizontal position in the viewport.\n */\n x: number;\n /**\n * The current vertical position in the viewport.\n */\n y: number;\n /**\n * The delta between the current position and the last.\n */\n delta: { x: number; y: number };\n /**\n * The initial position where the dragging started.\n */\n origin: { x: number; y: number };\n /**\n * The distance from the current position to the origin.\n */\n distance: { x: number; y: number };\n /**\n * The final position that will be reached at the end of the inertia.\n */\n final: { x: number; y: number };\n}\n\nexport type DragServiceInterface = ServiceInterface<DragServiceProps>;\n\nexport interface DragServiceOptions {\n dampFactor?: number;\n dragTreshold?: number;\n}\n\nlet count = 0;\n\nexport class DragService extends AbstractService<DragServiceProps> {\n static MODES: Record<Uppercase<DragLifecycle>, DragLifecycle> = {\n START: 'start',\n DRAG: 'drag',\n DROP: 'drop',\n INERTIA: 'inertia',\n STOP: 'stop',\n };\n\n static config: ServiceConfig = [\n [\n (instance) => (instance as DragService).props.target,\n [\n ['dragstart', CAPTURE_EVENT_OPTIONS],\n ['click', CAPTURE_EVENT_OPTIONS],\n ['pointerdown', PASSIVE_EVENT_OPTIONS],\n ],\n ],\n [\n () => window,\n [\n ['pointerup', PASSIVE_EVENT_OPTIONS],\n ['touchend', PASSIVE_EVENT_OPTIONS],\n ],\n ],\n ];\n\n id: string;\n\n dampFactor = 0.85;\n\n dragTreshold = 10;\n\n previousEvent: TouchEvent | MouseEvent = null;\n\n props = {\n target: null,\n mode: undefined,\n isGrabbing: false,\n hasInertia: false,\n x: 0,\n y: 0,\n delta: {\n x: 0,\n y: 0,\n },\n origin: {\n x: 0,\n y: 0,\n },\n distance: {\n x: 0,\n y: 0,\n },\n final: {\n x: 0,\n y: 0,\n },\n } as DragServiceProps;\n\n /**\n * Test if we should allow click on links and buttons.\n */\n get shouldPreventClick(): boolean {\n return (\n Math.abs(this.props.distance.x) > this.dragTreshold ||\n Math.abs(this.props.distance.y) > this.dragTreshold\n );\n }\n\n constructor(\n target: HTMLElement,\n { dampFactor = 0.85, dragTreshold = 10 }: DragServiceOptions = {},\n ) {\n super();\n count += 1;\n this.id = `drag-${count}`;\n this.dampFactor = dampFactor;\n this.dragTreshold = dragTreshold;\n this.props.target = target;\n }\n\n /**\n * Get the client value for the given axis.\n */\n getEventPosition(event: MouseEvent | TouchEvent): { x: number; y: number } {\n // @ts-ignore\n const eventOrTouch = isDefined(event.touches) ? event.touches[0] : event;\n return {\n x: eventOrTouch.clientX,\n y: eventOrTouch.clientY,\n };\n }\n\n /**\n * Start the drag.\n *\n * @param {number} x The initial horizontal position.\n * @param {number} y The initial vertical position.\n */\n start(x: number, y: number) {\n const { props } = this;\n if (props.isGrabbing) {\n return;\n }\n\n // Reset state\n props.x = props.origin.x = props.final.x = x;\n props.y = props.origin.y = props.final.y = y;\n props.delta.x = props.distance.x = 0;\n props.delta.y = props.distance.y = 0;\n props.mode = DragService.MODES.START;\n props.isGrabbing = true;\n\n this.trigger(props);\n\n document.addEventListener('touchmove', this, PASSIVE_EVENT_OPTIONS);\n document.addEventListener('mousemove', this, PASSIVE_EVENT_OPTIONS);\n }\n\n /**\n * Stop the drag, or drop.\n */\n drop() {\n const { props, dampFactor, id } = this;\n\n if (!props.isGrabbing) {\n return;\n }\n\n document.removeEventListener('touchmove', this);\n document.removeEventListener('mousemove', this);\n\n props.isGrabbing = false;\n props.mode = DragService.MODES.DROP;\n props.hasInertia = true;\n props.final.x = inertiaFinalValue(props.x, props.delta.x, dampFactor);\n props.final.y = inertiaFinalValue(props.y, props.delta.y, dampFactor);\n this.previousEvent = null;\n\n this.trigger(props);\n\n setTimeout(() => {\n const raf = useRaf();\n raf.remove(id);\n raf.add(id, () => this.rafHandler());\n });\n }\n\n /**\n * Stop the drag.\n */\n stop() {\n const { props, id } = this;\n\n useRaf().remove(id);\n props.isGrabbing = false;\n props.hasInertia = false;\n props.mode = DragService.MODES.STOP;\n\n this.trigger(props);\n }\n\n /**\n * Raf service handler.\n */\n rafHandler() {\n const { props, dampFactor } = this;\n\n if (!props.isGrabbing) {\n props.x += props.delta.x;\n props.y += props.delta.y;\n props.distance.x = props.x - props.origin.x;\n props.distance.y = props.y - props.origin.y;\n props.delta.x *= dampFactor;\n props.delta.y *= dampFactor;\n props.mode = DragService.MODES.INERTIA;\n\n this.trigger(props);\n\n if (Math.abs(props.delta.x) < 0.1 && Math.abs(props.delta.y) < 0.1) {\n this.stop();\n }\n }\n }\n\n /**\n * Pointer service handler.\n */\n drag(event: TouchEvent | MouseEvent) {\n const { props } = this;\n\n if (props.isGrabbing) {\n const position = this.getEventPosition(event);\n props.x = position.x;\n props.y = position.y;\n\n if (this.previousEvent) {\n const previousPosition = this.getEventPosition(this.previousEvent);\n props.delta.x = position.x - previousPosition.x;\n props.delta.y = position.y - previousPosition.y;\n }\n\n props.final.x = position.x;\n props.final.y = position.y;\n props.distance.x = props.x - props.origin.x;\n props.distance.y = props.y - props.origin.y;\n props.mode = DragService.MODES.DRAG;\n\n this.trigger(props);\n this.previousEvent = event;\n }\n }\n\n /**\n * Handle any event.\n */\n handleEvent(event: MouseEvent | PointerEvent | DragEvent) {\n switch (event.type) {\n case 'dragstart':\n event.preventDefault();\n break;\n case 'click':\n if (this.shouldPreventClick) {\n event.stopImmediatePropagation();\n event.stopPropagation();\n event.preventDefault();\n }\n break;\n case 'pointerup':\n case 'touchend':\n this.drop();\n break;\n case 'touchmove':\n case 'mousemove':\n this.drag(event);\n break;\n default:\n if (event.button === 0) {\n this.start(event.x, event.y);\n }\n }\n }\n}\n\n/**\n * Use the drag service.\n *\n * ```js\n * import { useDrag } from '@studiometa/js-toolkit/services';\n * const element = document.querySelector('.draggable');\n * const { add, remove, props } = useDrag(element, { dampFactor: 0.5 });\n * add('key', (props) => console.log(props));\n * remove('key');\n * props();\n * ```\n */\nexport function useDrag(target: HTMLElement, options: DragServiceOptions): DragServiceInterface {\n return DragService.getInstance<DragServiceInterface>(\n [target, JSON.stringify(options)],\n target,\n options,\n );\n}\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["import type { ServiceConfig, ServiceInterface } from './AbstractService.js';\nimport { AbstractService } from './AbstractService.js';\nimport { useRaf } from './RafService.js';\nimport { isDefined } from '../utils/is.js';\nimport { inertiaFinalValue } from '../utils/math/index.js';\nimport { PASSIVE_EVENT_OPTIONS, CAPTURE_EVENT_OPTIONS } from './utils.js';\n\nexport interface DragServiceProps {\n /**\n * The current mode of the dragging logic.\n */\n mode: 'start' | 'drag' | 'drop' | 'inertia' | 'stop';\n /**\n * Available modes.\n */\n MODES: {\n START: 'start';\n DRAG: 'drag';\n DROP: 'drop';\n INERTIA: 'inertia';\n STOP: 'stop';\n };\n /**\n * The target element of the drag.\n */\n target: HTMLElement;\n /**\n * Whether the user is currently grabbing the targeted element or not.\n */\n isGrabbing: boolean;\n /**\n * Whether the drag currently has inertia or not.\n */\n hasInertia: boolean;\n /**\n * The current horizontal position in the viewport.\n */\n x: number;\n /**\n * The current vertical position in the viewport.\n */\n y: number;\n /**\n * The delta between the current position and the last.\n */\n delta: { x: number; y: number };\n /**\n * The initial position where the dragging started.\n */\n origin: { x: number; y: number };\n /**\n * The distance from the current position to the origin.\n */\n distance: { x: number; y: number };\n /**\n * The final position that will be reached at the end of the inertia.\n */\n final: { x: number; y: number };\n}\n\nexport type DragServiceInterface = ServiceInterface<DragServiceProps>;\n\nexport interface DragServiceOptions {\n dampFactor?: number;\n dragTreshold?: number;\n}\n\nlet count = 0;\n\nexport class DragService extends AbstractService<DragServiceProps> {\n static config: ServiceConfig = [\n [\n (instance) => (instance as DragService).props.target,\n [\n ['dragstart', CAPTURE_EVENT_OPTIONS],\n ['click', CAPTURE_EVENT_OPTIONS],\n ['pointerdown', PASSIVE_EVENT_OPTIONS],\n ],\n ],\n [\n () => window,\n [\n ['pointerup', PASSIVE_EVENT_OPTIONS],\n ['touchend', PASSIVE_EVENT_OPTIONS],\n ],\n ],\n ];\n\n id: string;\n\n dampFactor = 0.85;\n\n dragTreshold = 10;\n\n previousEvent: TouchEvent | MouseEvent = null;\n\n props = {\n target: null,\n mode: undefined,\n MODES: {\n START: 'start',\n DRAG: 'drag',\n DROP: 'drop',\n INERTIA: 'inertia',\n STOP: 'stop',\n },\n isGrabbing: false,\n hasInertia: false,\n x: 0,\n y: 0,\n delta: {\n x: 0,\n y: 0,\n },\n origin: {\n x: 0,\n y: 0,\n },\n distance: {\n x: 0,\n y: 0,\n },\n final: {\n x: 0,\n y: 0,\n },\n } as DragServiceProps;\n\n /**\n * Test if we should allow click on links and buttons.\n */\n get shouldPreventClick(): boolean {\n return (\n Math.abs(this.props.distance.x) > this.dragTreshold ||\n Math.abs(this.props.distance.y) > this.dragTreshold\n );\n }\n\n constructor(\n target: HTMLElement,\n { dampFactor = 0.85, dragTreshold = 10 }: DragServiceOptions = {},\n ) {\n super();\n count += 1;\n this.id = `drag-${count}`;\n this.dampFactor = dampFactor;\n this.dragTreshold = dragTreshold;\n this.props.target = target;\n }\n\n /**\n * Get the client value for the given axis.\n */\n getEventPosition(event: MouseEvent | TouchEvent): { x: number; y: number } {\n // @ts-ignore\n const eventOrTouch = isDefined(event.touches) ? event.touches[0] : event;\n return {\n x: eventOrTouch.clientX,\n y: eventOrTouch.clientY,\n };\n }\n\n /**\n * Start the drag.\n *\n * @param {number} x The initial horizontal position.\n * @param {number} y The initial vertical position.\n */\n start(x: number, y: number) {\n const { props } = this;\n if (props.isGrabbing) {\n return;\n }\n\n // Reset state\n props.x = props.origin.x = props.final.x = x;\n props.y = props.origin.y = props.final.y = y;\n props.delta.x = props.distance.x = 0;\n props.delta.y = props.distance.y = 0;\n props.mode = props.MODES.START;\n props.isGrabbing = true;\n\n this.trigger(props);\n\n document.addEventListener('touchmove', this, PASSIVE_EVENT_OPTIONS);\n document.addEventListener('mousemove', this, PASSIVE_EVENT_OPTIONS);\n }\n\n /**\n * Stop the drag, or drop.\n */\n drop() {\n const { props, dampFactor, id } = this;\n\n if (!props.isGrabbing) {\n return;\n }\n\n document.removeEventListener('touchmove', this);\n document.removeEventListener('mousemove', this);\n\n props.isGrabbing = false;\n props.mode = props.MODES.DROP;\n props.hasInertia = true;\n props.final.x = inertiaFinalValue(props.x, props.delta.x, dampFactor);\n props.final.y = inertiaFinalValue(props.y, props.delta.y, dampFactor);\n this.previousEvent = null;\n\n this.trigger(props);\n\n setTimeout(() => {\n const raf = useRaf();\n raf.remove(id);\n raf.add(id, () => this.rafHandler());\n });\n }\n\n /**\n * Stop the drag.\n */\n stop() {\n const { props, id } = this;\n\n useRaf().remove(id);\n props.isGrabbing = false;\n props.hasInertia = false;\n props.mode = props.MODES.STOP;\n\n this.trigger(props);\n }\n\n /**\n * Raf service handler.\n */\n rafHandler() {\n const { props, dampFactor } = this;\n\n if (!props.isGrabbing) {\n props.x += props.delta.x;\n props.y += props.delta.y;\n props.distance.x = props.x - props.origin.x;\n props.distance.y = props.y - props.origin.y;\n props.delta.x *= dampFactor;\n props.delta.y *= dampFactor;\n props.mode = props.MODES.INERTIA;\n\n this.trigger(props);\n\n if (Math.abs(props.delta.x) < 0.1 && Math.abs(props.delta.y) < 0.1) {\n this.stop();\n }\n }\n }\n\n /**\n * Pointer service handler.\n */\n drag(event: TouchEvent | MouseEvent) {\n const { props } = this;\n\n if (props.isGrabbing) {\n const position = this.getEventPosition(event);\n props.x = position.x;\n props.y = position.y;\n\n if (this.previousEvent) {\n const previousPosition = this.getEventPosition(this.previousEvent);\n props.delta.x = position.x - previousPosition.x;\n props.delta.y = position.y - previousPosition.y;\n }\n\n props.final.x = position.x;\n props.final.y = position.y;\n props.distance.x = props.x - props.origin.x;\n props.distance.y = props.y - props.origin.y;\n props.mode = props.MODES.DRAG;\n\n this.trigger(props);\n this.previousEvent = event;\n }\n }\n\n /**\n * Handle any event.\n */\n handleEvent(event: MouseEvent | PointerEvent | DragEvent) {\n switch (event.type) {\n case 'dragstart':\n event.preventDefault();\n break;\n case 'click':\n if (this.shouldPreventClick) {\n event.stopImmediatePropagation();\n event.stopPropagation();\n event.preventDefault();\n }\n break;\n case 'pointerup':\n case 'touchend':\n this.drop();\n break;\n case 'touchmove':\n case 'mousemove':\n this.drag(event);\n break;\n default:\n if (event.button === 0) {\n this.start(event.x, event.y);\n }\n }\n }\n}\n\n/**\n * Use the drag service.\n *\n * ```js\n * import { useDrag } from '@studiometa/js-toolkit/services';\n * const element = document.querySelector('.draggable');\n * const { add, remove, props } = useDrag(element, { dampFactor: 0.5 });\n * add('key', (props) => console.log(props));\n * remove('key');\n * props();\n * ```\n */\nexport function useDrag(target: HTMLElement, options: DragServiceOptions): DragServiceInterface {\n return DragService.getInstance<DragServiceInterface>(\n [target, JSON.stringify(options)],\n target,\n options,\n );\n}\n"],
5
+ "mappings": "AACA,SAAS,uBAAuB;AAChC,SAAS,cAAc;AACvB,SAAS,iBAAiB;AAC1B,SAAS,yBAAyB;AAClC,SAAS,uBAAuB,6BAA6B;AA8D7D,IAAI,QAAQ;AAEL,MAAM,oBAAoB,gBAAkC;AAAA,EACjE,OAAO,SAAwB;AAAA,IAC7B;AAAA,MACE,CAAC,aAAc,SAAyB,MAAM;AAAA,MAC9C;AAAA,QACE,CAAC,aAAa,qBAAqB;AAAA,QACnC,CAAC,SAAS,qBAAqB;AAAA,QAC/B,CAAC,eAAe,qBAAqB;AAAA,MACvC;AAAA,IACF;AAAA,IACA;AAAA,MACE,MAAM;AAAA,MACN;AAAA,QACE,CAAC,aAAa,qBAAqB;AAAA,QACnC,CAAC,YAAY,qBAAqB;AAAA,MACpC;AAAA,IACF;AAAA,EACF;AAAA,EAEA;AAAA,EAEA,aAAa;AAAA,EAEb,eAAe;AAAA,EAEf,gBAAyC;AAAA,EAEzC,QAAQ;AAAA,IACN,QAAQ;AAAA,IACR,MAAM;AAAA,IACN,OAAO;AAAA,MACL,OAAO;AAAA,MACP,MAAM;AAAA,MACN,MAAM;AAAA,MACN,SAAS;AAAA,MACT,MAAM;AAAA,IACR;AAAA,IACA,YAAY;AAAA,IACZ,YAAY;AAAA,IACZ,GAAG;AAAA,IACH,GAAG;AAAA,IACH,OAAO;AAAA,MACL,GAAG;AAAA,MACH,GAAG;AAAA,IACL;AAAA,IACA,QAAQ;AAAA,MACN,GAAG;AAAA,MACH,GAAG;AAAA,IACL;AAAA,IACA,UAAU;AAAA,MACR,GAAG;AAAA,MACH,GAAG;AAAA,IACL;AAAA,IACA,OAAO;AAAA,MACL,GAAG;AAAA,MACH,GAAG;AAAA,IACL;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKA,IAAI,qBAA8B;AAChC,WACE,KAAK,IAAI,KAAK,MAAM,SAAS,CAAC,IAAI,KAAK,gBACvC,KAAK,IAAI,KAAK,MAAM,SAAS,CAAC,IAAI,KAAK;AAAA,EAE3C;AAAA,EAEA,YACE,QACA,EAAE,aAAa,MAAM,eAAe,GAAG,IAAwB,CAAC,GAChE;AACA,UAAM;AACN,aAAS;AACT,SAAK,KAAK,QAAQ,KAAK;AACvB,SAAK,aAAa;AAClB,SAAK,eAAe;AACpB,SAAK,MAAM,SAAS;AAAA,EACtB;AAAA;AAAA;AAAA;AAAA,EAKA,iBAAiB,OAA0D;AAEzE,UAAM,eAAe,UAAU,MAAM,OAAO,IAAI,MAAM,QAAQ,CAAC,IAAI;AACnE,WAAO;AAAA,MACL,GAAG,aAAa;AAAA,MAChB,GAAG,aAAa;AAAA,IAClB;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQA,MAAM,GAAW,GAAW;AAC1B,UAAM,EAAE,MAAM,IAAI;AAClB,QAAI,MAAM,YAAY;AACpB;AAAA,IACF;AAGA,UAAM,IAAI,MAAM,OAAO,IAAI,MAAM,MAAM,IAAI;AAC3C,UAAM,IAAI,MAAM,OAAO,IAAI,MAAM,MAAM,IAAI;AAC3C,UAAM,MAAM,IAAI,MAAM,SAAS,IAAI;AACnC,UAAM,MAAM,IAAI,MAAM,SAAS,IAAI;AACnC,UAAM,OAAO,MAAM,MAAM;AACzB,UAAM,aAAa;AAEnB,SAAK,QAAQ,KAAK;AAElB,aAAS,iBAAiB,aAAa,MAAM,qBAAqB;AAClE,aAAS,iBAAiB,aAAa,MAAM,qBAAqB;AAAA,EACpE;AAAA;AAAA;AAAA;AAAA,EAKA,OAAO;AACL,UAAM,EAAE,OAAO,YAAY,GAAG,IAAI;AAElC,QAAI,CAAC,MAAM,YAAY;AACrB;AAAA,IACF;AAEA,aAAS,oBAAoB,aAAa,IAAI;AAC9C,aAAS,oBAAoB,aAAa,IAAI;AAE9C,UAAM,aAAa;AACnB,UAAM,OAAO,MAAM,MAAM;AACzB,UAAM,aAAa;AACnB,UAAM,MAAM,IAAI,kBAAkB,MAAM,GAAG,MAAM,MAAM,GAAG,UAAU;AACpE,UAAM,MAAM,IAAI,kBAAkB,MAAM,GAAG,MAAM,MAAM,GAAG,UAAU;AACpE,SAAK,gBAAgB;AAErB,SAAK,QAAQ,KAAK;AAElB,eAAW,MAAM;AACf,YAAM,MAAM,OAAO;AACnB,UAAI,OAAO,EAAE;AACb,UAAI,IAAI,IAAI,MAAM,KAAK,WAAW,CAAC;AAAA,IACrC,CAAC;AAAA,EACH;AAAA;AAAA;AAAA;AAAA,EAKA,OAAO;AACL,UAAM,EAAE,OAAO,GAAG,IAAI;AAEtB,WAAO,EAAE,OAAO,EAAE;AAClB,UAAM,aAAa;AACnB,UAAM,aAAa;AACnB,UAAM,OAAO,MAAM,MAAM;AAEzB,SAAK,QAAQ,KAAK;AAAA,EACpB;AAAA;AAAA;AAAA;AAAA,EAKA,aAAa;AACX,UAAM,EAAE,OAAO,WAAW,IAAI;AAE9B,QAAI,CAAC,MAAM,YAAY;AACrB,YAAM,KAAK,MAAM,MAAM;AACvB,YAAM,KAAK,MAAM,MAAM;AACvB,YAAM,SAAS,IAAI,MAAM,IAAI,MAAM,OAAO;AAC1C,YAAM,SAAS,IAAI,MAAM,IAAI,MAAM,OAAO;AAC1C,YAAM,MAAM,KAAK;AACjB,YAAM,MAAM,KAAK;AACjB,YAAM,OAAO,MAAM,MAAM;AAEzB,WAAK,QAAQ,KAAK;AAElB,UAAI,KAAK,IAAI,MAAM,MAAM,CAAC,IAAI,OAAO,KAAK,IAAI,MAAM,MAAM,CAAC,IAAI,KAAK;AAClE,aAAK,KAAK;AAAA,MACZ;AAAA,IACF;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKA,KAAK,OAAgC;AACnC,UAAM,EAAE,MAAM,IAAI;AAElB,QAAI,MAAM,YAAY;AACpB,YAAM,WAAW,KAAK,iBAAiB,KAAK;AAC5C,YAAM,IAAI,SAAS;AACnB,YAAM,IAAI,SAAS;AAEnB,UAAI,KAAK,eAAe;AACtB,cAAM,mBAAmB,KAAK,iBAAiB,KAAK,aAAa;AACjE,cAAM,MAAM,IAAI,SAAS,IAAI,iBAAiB;AAC9C,cAAM,MAAM,IAAI,SAAS,IAAI,iBAAiB;AAAA,MAChD;AAEA,YAAM,MAAM,IAAI,SAAS;AACzB,YAAM,MAAM,IAAI,SAAS;AACzB,YAAM,SAAS,IAAI,MAAM,IAAI,MAAM,OAAO;AAC1C,YAAM,SAAS,IAAI,MAAM,IAAI,MAAM,OAAO;AAC1C,YAAM,OAAO,MAAM,MAAM;AAEzB,WAAK,QAAQ,KAAK;AAClB,WAAK,gBAAgB;AAAA,IACvB;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKA,YAAY,OAA8C;AACxD,YAAQ,MAAM,MAAM;AAAA,MAClB,KAAK;AACH,cAAM,eAAe;AACrB;AAAA,MACF,KAAK;AACH,YAAI,KAAK,oBAAoB;AAC3B,gBAAM,yBAAyB;AAC/B,gBAAM,gBAAgB;AACtB,gBAAM,eAAe;AAAA,QACvB;AACA;AAAA,MACF,KAAK;AAAA,MACL,KAAK;AACH,aAAK,KAAK;AACV;AAAA,MACF,KAAK;AAAA,MACL,KAAK;AACH,aAAK,KAAK,KAAK;AACf;AAAA,MACF;AACE,YAAI,MAAM,WAAW,GAAG;AACtB,eAAK,MAAM,MAAM,GAAG,MAAM,CAAC;AAAA,QAC7B;AAAA,IACJ;AAAA,EACF;AACF;AAcO,SAAS,QAAQ,QAAqB,SAAmD;AAC9F,SAAO,YAAY;AAAA,IACjB,CAAC,QAAQ,KAAK,UAAU,OAAO,CAAC;AAAA,IAChC;AAAA,IACA;AAAA,EACF;AACF;",
6
6
  "names": []
7
7
  }
@@ -5,8 +5,14 @@ export interface RafServiceProps {
5
5
  }
6
6
  export type RafServiceInterface = ServiceInterface<RafServiceProps>;
7
7
  export declare class RafService extends AbstractService<RafServiceProps> {
8
+ /**
9
+ * @private
10
+ */
8
11
  isTicking: boolean;
9
- scheduler: Record<"update" | "render", (fn: () => unknown) => void>;
12
+ /**
13
+ * @private
14
+ */
15
+ scheduler: Record<"read" | "write" | "afterWrite", (fn: () => unknown) => void>;
10
16
  props: RafServiceProps;
11
17
  trigger(props: RafServiceProps): void;
12
18
  loop(): void;
@@ -1,18 +1,24 @@
1
1
  import { AbstractService } from "./AbstractService.js";
2
- import { useScheduler } from "../utils/scheduler.js";
2
+ import { domScheduler } from "../utils/scheduler.js";
3
3
  import { isFunction } from "../utils/is.js";
4
4
  class RafService extends AbstractService {
5
+ /**
6
+ * @private
7
+ */
5
8
  isTicking = false;
6
- scheduler = useScheduler(["update", "render"]);
9
+ /**
10
+ * @private
11
+ */
12
+ scheduler = domScheduler;
7
13
  props = {
8
14
  time: performance.now()
9
15
  };
10
16
  trigger(props) {
11
17
  for (const callback of this.callbacks.values()) {
12
- this.scheduler.update(() => {
18
+ this.scheduler.read(() => {
13
19
  const render = callback(props);
14
20
  if (isFunction(render)) {
15
- this.scheduler.render(() => {
21
+ this.scheduler.write(() => {
16
22
  render(props);
17
23
  });
18
24
  }
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../packages/js-toolkit/services/RafService.ts"],
4
- "sourcesContent": ["import type { ServiceInterface } from './AbstractService.js';\nimport { AbstractService } from './AbstractService.js';\nimport { useScheduler } from '../utils/scheduler.js';\nimport { isFunction } from '../utils/is.js';\n\nexport interface RafServiceProps {\n time: DOMHighResTimeStamp;\n}\n\nexport type RafServiceInterface = ServiceInterface<RafServiceProps>;\n\nexport class RafService extends AbstractService<RafServiceProps> {\n isTicking = false;\n scheduler = useScheduler(['update', 'render']);\n\n props: RafServiceProps = {\n time: performance.now(),\n };\n\n trigger(props: RafServiceProps) {\n for (const callback of this.callbacks.values()) {\n this.scheduler.update(() => {\n const render = callback(props) as (() => unknown) | void;\n if (isFunction(render)) {\n this.scheduler.render(() => {\n // @ts-ignore\n render(props);\n });\n }\n });\n }\n }\n\n loop() {\n this.props.time = performance.now();\n this.trigger(this.props);\n\n if (!this.isTicking) {\n return;\n }\n\n requestAnimationFrame(() => this.loop());\n }\n\n init() {\n this.isTicking = true;\n requestAnimationFrame(() => this.loop());\n }\n kill() {\n this.isTicking = false;\n }\n}\n\n/**\n * Use the RequestAnimationFrame (raf) service.\n */\nexport function useRaf(): RafServiceInterface {\n return RafService.getInstance();\n}\n"],
5
- "mappings": "AACA,SAAS,uBAAuB;AAChC,SAAS,oBAAoB;AAC7B,SAAS,kBAAkB;AAQpB,MAAM,mBAAmB,gBAAiC;AAAA,EAC/D,YAAY;AAAA,EACZ,YAAY,aAAa,CAAC,UAAU,QAAQ,CAAC;AAAA,EAE7C,QAAyB;AAAA,IACvB,MAAM,YAAY,IAAI;AAAA,EACxB;AAAA,EAEA,QAAQ,OAAwB;AAC9B,eAAW,YAAY,KAAK,UAAU,OAAO,GAAG;AAC9C,WAAK,UAAU,OAAO,MAAM;AAC1B,cAAM,SAAS,SAAS,KAAK;AAC7B,YAAI,WAAW,MAAM,GAAG;AACtB,eAAK,UAAU,OAAO,MAAM;AAE1B,mBAAO,KAAK;AAAA,UACd,CAAC;AAAA,QACH;AAAA,MACF,CAAC;AAAA,IACH;AAAA,EACF;AAAA,EAEA,OAAO;AACL,SAAK,MAAM,OAAO,YAAY,IAAI;AAClC,SAAK,QAAQ,KAAK,KAAK;AAEvB,QAAI,CAAC,KAAK,WAAW;AACnB;AAAA,IACF;AAEA,0BAAsB,MAAM,KAAK,KAAK,CAAC;AAAA,EACzC;AAAA,EAEA,OAAO;AACL,SAAK,YAAY;AACjB,0BAAsB,MAAM,KAAK,KAAK,CAAC;AAAA,EACzC;AAAA,EACA,OAAO;AACL,SAAK,YAAY;AAAA,EACnB;AACF;AAKO,SAAS,SAA8B;AAC5C,SAAO,WAAW,YAAY;AAChC;",
4
+ "sourcesContent": ["import type { ServiceInterface } from './AbstractService.js';\nimport { AbstractService } from './AbstractService.js';\nimport { domScheduler } from '../utils/scheduler.js';\nimport { isFunction } from '../utils/is.js';\n\nexport interface RafServiceProps {\n time: DOMHighResTimeStamp;\n}\n\nexport type RafServiceInterface = ServiceInterface<RafServiceProps>;\n\nexport class RafService extends AbstractService<RafServiceProps> {\n /**\n * @private\n */\n isTicking = false;\n\n /**\n * @private\n */\n scheduler = domScheduler;\n\n props: RafServiceProps = {\n time: performance.now(),\n };\n\n trigger(props: RafServiceProps) {\n for (const callback of this.callbacks.values()) {\n this.scheduler.read(() => {\n const render = callback(props) as (() => unknown) | void;\n if (isFunction(render)) {\n this.scheduler.write(() => {\n // @ts-ignore\n render(props);\n });\n }\n });\n }\n }\n\n loop() {\n this.props.time = performance.now();\n this.trigger(this.props);\n\n if (!this.isTicking) {\n return;\n }\n\n requestAnimationFrame(() => this.loop());\n }\n\n init() {\n this.isTicking = true;\n requestAnimationFrame(() => this.loop());\n }\n\n kill() {\n this.isTicking = false;\n }\n}\n\n/**\n * Use the RequestAnimationFrame (raf) service.\n */\nexport function useRaf(): RafServiceInterface {\n return RafService.getInstance();\n}\n"],
5
+ "mappings": "AACA,SAAS,uBAAuB;AAChC,SAAS,oBAAoB;AAC7B,SAAS,kBAAkB;AAQpB,MAAM,mBAAmB,gBAAiC;AAAA;AAAA;AAAA;AAAA,EAI/D,YAAY;AAAA;AAAA;AAAA;AAAA,EAKZ,YAAY;AAAA,EAEZ,QAAyB;AAAA,IACvB,MAAM,YAAY,IAAI;AAAA,EACxB;AAAA,EAEA,QAAQ,OAAwB;AAC9B,eAAW,YAAY,KAAK,UAAU,OAAO,GAAG;AAC9C,WAAK,UAAU,KAAK,MAAM;AACxB,cAAM,SAAS,SAAS,KAAK;AAC7B,YAAI,WAAW,MAAM,GAAG;AACtB,eAAK,UAAU,MAAM,MAAM;AAEzB,mBAAO,KAAK;AAAA,UACd,CAAC;AAAA,QACH;AAAA,MACF,CAAC;AAAA,IACH;AAAA,EACF;AAAA,EAEA,OAAO;AACL,SAAK,MAAM,OAAO,YAAY,IAAI;AAClC,SAAK,QAAQ,KAAK,KAAK;AAEvB,QAAI,CAAC,KAAK,WAAW;AACnB;AAAA,IACF;AAEA,0BAAsB,MAAM,KAAK,KAAK,CAAC;AAAA,EACzC;AAAA,EAEA,OAAO;AACL,SAAK,YAAY;AACjB,0BAAsB,MAAM,KAAK,KAAK,CAAC;AAAA,EACzC;AAAA,EAEA,OAAO;AACL,SAAK,YAAY;AAAA,EACnB;AACF;AAKO,SAAS,SAA8B;AAC5C,SAAO,WAAW,YAAY;AAChC;",
6
6
  "names": []
7
7
  }
@@ -6,12 +6,12 @@ export interface ResizeServiceProps<U extends Features['breakpoints'] = Features
6
6
  height: number;
7
7
  ratio: number;
8
8
  orientation: 'square' | 'landscape' | 'portrait';
9
- breakpoint: string;
10
- breakpoints: string[];
9
+ breakpoint: keyof U;
10
+ breakpoints: Array<keyof U>;
11
11
  activeBreakpoints: Record<keyof U, boolean>;
12
12
  }
13
13
  export type ResizeServiceInterface<U extends Features['breakpoints'] = Features['breakpoints']> = ServiceInterface<ResizeServiceProps<U>>;
14
- export declare class ResizeService<T extends Features['breakpoints'] = Features['breakpoints']> extends AbstractService<ResizeServiceProps> {
14
+ export declare class ResizeService<T extends Features['breakpoints'] = Features['breakpoints']> extends AbstractService<ResizeServiceProps<T>> {
15
15
  static config: ServiceConfig;
16
16
  breakpoints: T;
17
17
  props: ResizeServiceProps<T>;
@@ -1,6 +1,7 @@
1
1
  import { AbstractService } from "./AbstractService.js";
2
2
  import { features } from "../Base/features.js";
3
3
  import { debounce } from "../utils/debounce.js";
4
+ import { cache } from "../utils/cache.js";
4
5
  class ResizeService extends AbstractService {
5
6
  static config = [[() => window, [["resize"]]]];
6
7
  breakpoints;
@@ -26,7 +27,10 @@ class ResizeService extends AbstractService {
26
27
  for (const [name, breakpoint] of Object.entries(
27
28
  this.breakpoints ?? features.get("breakpoints")
28
29
  )) {
29
- activeBreakpoints[name] = window.matchMedia(`(min-width: ${breakpoint})`).matches;
30
+ activeBreakpoints[name] = cache(
31
+ [this, window.innerWidth, window.innerHeight, breakpoint],
32
+ () => window.matchMedia(`(min-width: ${breakpoint})`).matches
33
+ );
30
34
  }
31
35
  return activeBreakpoints;
32
36
  }
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../packages/js-toolkit/services/ResizeService.ts"],
4
- "sourcesContent": ["import type { ServiceConfig, ServiceInterface } from './AbstractService.js';\nimport { AbstractService } from './AbstractService.js';\nimport type { Features } from '../Base/features.js';\nimport { features } from '../Base/features.js';\nimport { debounce } from '../utils/debounce.js';\n\nexport interface ResizeServiceProps<U extends Features['breakpoints'] = Features['breakpoints']> {\n width: number;\n height: number;\n ratio: number;\n orientation: 'square' | 'landscape' | 'portrait';\n breakpoint: string;\n breakpoints: string[];\n activeBreakpoints: Record<keyof U, boolean>;\n}\n\nexport type ResizeServiceInterface<U extends Features['breakpoints'] = Features['breakpoints']> =\n ServiceInterface<ResizeServiceProps<U>>;\n\nexport class ResizeService<\n T extends Features['breakpoints'] = Features['breakpoints'],\n> extends AbstractService<ResizeServiceProps> {\n static config: ServiceConfig = [[() => window, [['resize']]]];\n\n breakpoints: T;\n\n props: ResizeServiceProps<T> = {\n width: window.innerWidth,\n height: window.innerHeight,\n ratio: window.innerWidth / window.innerHeight,\n orientation: 'square',\n get breakpoint() {\n return Object.keys(this.activeBreakpoints)\n .toReversed()\n .find((name) => this.activeBreakpoints[name]);\n },\n get breakpoints() {\n return Object.keys(this.activeBreakpoints);\n },\n activeBreakpoints: {} as Record<keyof T, boolean>,\n };\n\n constructor(breakpoints: T) {\n super();\n this.breakpoints = breakpoints;\n\n Object.defineProperty(this.props, 'activeBreakpoints', {\n get: () => {\n const activeBreakpoints = {};\n\n for (const [name, breakpoint] of Object.entries(\n this.breakpoints ?? features.get('breakpoints'),\n )) {\n activeBreakpoints[name] = window.matchMedia(`(min-width: ${breakpoint})`).matches;\n }\n\n return activeBreakpoints as Record<keyof T, boolean>;\n },\n });\n }\n\n /**\n * Update props.\n */\n updateProps(): ResizeServiceProps<T> {\n const { props } = this;\n\n props.width = window.innerWidth;\n props.height = window.innerHeight;\n props.ratio = window.innerWidth / window.innerHeight;\n props.orientation = 'square';\n\n if (props.ratio > 1) {\n props.orientation = 'landscape';\n }\n\n if (props.ratio < 1) {\n props.orientation = 'portrait';\n }\n\n return props;\n }\n\n onResizeDebounce = debounce(() => {\n this.trigger(this.updateProps());\n }, 100);\n\n handleEvent() {\n this.onResizeDebounce();\n }\n}\n\n/**\n * Use the resize service.\n */\nexport function useResize<T extends Features['breakpoints'] = Features['breakpoints']>(\n breakpoints?: T,\n): ResizeServiceInterface<T> {\n return ResizeService.getInstance([breakpoints], breakpoints);\n}\n"],
5
- "mappings": "AACA,SAAS,uBAAuB;AAEhC,SAAS,gBAAgB;AACzB,SAAS,gBAAgB;AAelB,MAAM,sBAEH,gBAAoC;AAAA,EAC5C,OAAO,SAAwB,CAAC,CAAC,MAAM,QAAQ,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC;AAAA,EAE5D;AAAA,EAEA,QAA+B;AAAA,IAC7B,OAAO,OAAO;AAAA,IACd,QAAQ,OAAO;AAAA,IACf,OAAO,OAAO,aAAa,OAAO;AAAA,IAClC,aAAa;AAAA,IACb,IAAI,aAAa;AACf,aAAO,OAAO,KAAK,KAAK,iBAAiB,EACtC,WAAW,EACX,KAAK,CAAC,SAAS,KAAK,kBAAkB,IAAI,CAAC;AAAA,IAChD;AAAA,IACA,IAAI,cAAc;AAChB,aAAO,OAAO,KAAK,KAAK,iBAAiB;AAAA,IAC3C;AAAA,IACA,mBAAmB,CAAC;AAAA,EACtB;AAAA,EAEA,YAAY,aAAgB;AAC1B,UAAM;AACN,SAAK,cAAc;AAEnB,WAAO,eAAe,KAAK,OAAO,qBAAqB;AAAA,MACrD,KAAK,MAAM;AACT,cAAM,oBAAoB,CAAC;AAE3B,mBAAW,CAAC,MAAM,UAAU,KAAK,OAAO;AAAA,UACtC,KAAK,eAAe,SAAS,IAAI,aAAa;AAAA,QAChD,GAAG;AACD,4BAAkB,IAAI,IAAI,OAAO,WAAW,eAAe,UAAU,GAAG,EAAE;AAAA,QAC5E;AAEA,eAAO;AAAA,MACT;AAAA,IACF,CAAC;AAAA,EACH;AAAA;AAAA;AAAA;AAAA,EAKA,cAAqC;AACnC,UAAM,EAAE,MAAM,IAAI;AAElB,UAAM,QAAQ,OAAO;AACrB,UAAM,SAAS,OAAO;AACtB,UAAM,QAAQ,OAAO,aAAa,OAAO;AACzC,UAAM,cAAc;AAEpB,QAAI,MAAM,QAAQ,GAAG;AACnB,YAAM,cAAc;AAAA,IACtB;AAEA,QAAI,MAAM,QAAQ,GAAG;AACnB,YAAM,cAAc;AAAA,IACtB;AAEA,WAAO;AAAA,EACT;AAAA,EAEA,mBAAmB,SAAS,MAAM;AAChC,SAAK,QAAQ,KAAK,YAAY,CAAC;AAAA,EACjC,GAAG,GAAG;AAAA,EAEN,cAAc;AACZ,SAAK,iBAAiB;AAAA,EACxB;AACF;AAKO,SAAS,UACd,aAC2B;AAC3B,SAAO,cAAc,YAAY,CAAC,WAAW,GAAG,WAAW;AAC7D;",
4
+ "sourcesContent": ["import type { ServiceConfig, ServiceInterface } from './AbstractService.js';\nimport { AbstractService } from './AbstractService.js';\nimport type { Features } from '../Base/features.js';\nimport { features } from '../Base/features.js';\nimport { debounce } from '../utils/debounce.js';\nimport { cache } from '../utils/cache.js';\n\nexport interface ResizeServiceProps<U extends Features['breakpoints'] = Features['breakpoints']> {\n width: number;\n height: number;\n ratio: number;\n orientation: 'square' | 'landscape' | 'portrait';\n breakpoint: keyof U;\n breakpoints: Array<keyof U>;\n activeBreakpoints: Record<keyof U, boolean>;\n}\n\nexport type ResizeServiceInterface<U extends Features['breakpoints'] = Features['breakpoints']> =\n ServiceInterface<ResizeServiceProps<U>>;\n\nexport class ResizeService<\n T extends Features['breakpoints'] = Features['breakpoints'],\n> extends AbstractService<ResizeServiceProps<T>> {\n static config: ServiceConfig = [[() => window, [['resize']]]];\n\n breakpoints: T;\n\n props: ResizeServiceProps<T> = {\n width: window.innerWidth,\n height: window.innerHeight,\n ratio: window.innerWidth / window.innerHeight,\n orientation: 'square',\n get breakpoint() {\n return Object.keys(this.activeBreakpoints)\n .toReversed()\n .find((name) => this.activeBreakpoints[name]);\n },\n get breakpoints() {\n return Object.keys(this.activeBreakpoints);\n },\n activeBreakpoints: {} as Record<keyof T, boolean>,\n };\n\n constructor(breakpoints: T) {\n super();\n this.breakpoints = breakpoints;\n\n Object.defineProperty(this.props, 'activeBreakpoints', {\n get: () => {\n const activeBreakpoints = {};\n\n for (const [name, breakpoint] of Object.entries(\n this.breakpoints ?? features.get('breakpoints'),\n )) {\n activeBreakpoints[name] = cache(\n [this, window.innerWidth, window.innerHeight, breakpoint],\n () => window.matchMedia(`(min-width: ${breakpoint})`).matches,\n );\n }\n\n return activeBreakpoints as Record<keyof T, boolean>;\n },\n });\n }\n\n /**\n * Update props.\n */\n updateProps(): ResizeServiceProps<T> {\n const { props } = this;\n\n props.width = window.innerWidth;\n props.height = window.innerHeight;\n props.ratio = window.innerWidth / window.innerHeight;\n props.orientation = 'square';\n\n if (props.ratio > 1) {\n props.orientation = 'landscape';\n }\n\n if (props.ratio < 1) {\n props.orientation = 'portrait';\n }\n\n return props;\n }\n\n onResizeDebounce = debounce(() => {\n this.trigger(this.updateProps());\n }, 100);\n\n handleEvent() {\n this.onResizeDebounce();\n }\n}\n\n/**\n * Use the resize service.\n */\nexport function useResize<T extends Features['breakpoints'] = Features['breakpoints']>(\n breakpoints?: T,\n): ResizeServiceInterface<T> {\n return ResizeService.getInstance([breakpoints], breakpoints);\n}\n"],
5
+ "mappings": "AACA,SAAS,uBAAuB;AAEhC,SAAS,gBAAgB;AACzB,SAAS,gBAAgB;AACzB,SAAS,aAAa;AAef,MAAM,sBAEH,gBAAuC;AAAA,EAC/C,OAAO,SAAwB,CAAC,CAAC,MAAM,QAAQ,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC;AAAA,EAE5D;AAAA,EAEA,QAA+B;AAAA,IAC7B,OAAO,OAAO;AAAA,IACd,QAAQ,OAAO;AAAA,IACf,OAAO,OAAO,aAAa,OAAO;AAAA,IAClC,aAAa;AAAA,IACb,IAAI,aAAa;AACf,aAAO,OAAO,KAAK,KAAK,iBAAiB,EACtC,WAAW,EACX,KAAK,CAAC,SAAS,KAAK,kBAAkB,IAAI,CAAC;AAAA,IAChD;AAAA,IACA,IAAI,cAAc;AAChB,aAAO,OAAO,KAAK,KAAK,iBAAiB;AAAA,IAC3C;AAAA,IACA,mBAAmB,CAAC;AAAA,EACtB;AAAA,EAEA,YAAY,aAAgB;AAC1B,UAAM;AACN,SAAK,cAAc;AAEnB,WAAO,eAAe,KAAK,OAAO,qBAAqB;AAAA,MACrD,KAAK,MAAM;AACT,cAAM,oBAAoB,CAAC;AAE3B,mBAAW,CAAC,MAAM,UAAU,KAAK,OAAO;AAAA,UACtC,KAAK,eAAe,SAAS,IAAI,aAAa;AAAA,QAChD,GAAG;AACD,4BAAkB,IAAI,IAAI;AAAA,YACxB,CAAC,MAAM,OAAO,YAAY,OAAO,aAAa,UAAU;AAAA,YACxD,MAAM,OAAO,WAAW,eAAe,UAAU,GAAG,EAAE;AAAA,UACxD;AAAA,QACF;AAEA,eAAO;AAAA,MACT;AAAA,IACF,CAAC;AAAA,EACH;AAAA;AAAA;AAAA;AAAA,EAKA,cAAqC;AACnC,UAAM,EAAE,MAAM,IAAI;AAElB,UAAM,QAAQ,OAAO;AACrB,UAAM,SAAS,OAAO;AACtB,UAAM,QAAQ,OAAO,aAAa,OAAO;AACzC,UAAM,cAAc;AAEpB,QAAI,MAAM,QAAQ,GAAG;AACnB,YAAM,cAAc;AAAA,IACtB;AAEA,QAAI,MAAM,QAAQ,GAAG;AACnB,YAAM,cAAc;AAAA,IACtB;AAEA,WAAO;AAAA,EACT;AAAA,EAEA,mBAAmB,SAAS,MAAM;AAChC,SAAK,QAAQ,KAAK,YAAY,CAAC;AAAA,EACjC,GAAG,GAAG;AAAA,EAEN,cAAc;AACZ,SAAK,iBAAiB;AAAA,EACxB;AACF;AAKO,SAAS,UACd,aAC2B;AAC3B,SAAO,cAAc,YAAY,CAAC,WAAW,GAAG,WAAW;AAC7D;",
6
6
  "names": []
7
7
  }