@tempots/dom 5.1.0 → 7.0.0

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.
@@ -1,18 +1,20 @@
1
- import { Clear } from "../clean";
2
- import { DOMContext } from "../dom-context";
3
- import { Renderable } from "../renderable";
4
- import { Animatable } from "./animatable";
1
+ import { type Clear } from '../clean';
2
+ import { type DOMContext } from '../dom-context';
3
+ import { type Renderable } from '../renderable';
4
+ import { type Animatable } from './animatable';
5
5
  export declare class FadeInImpl implements Renderable {
6
6
  private readonly end;
7
7
  private readonly start;
8
+ private readonly easing;
8
9
  private readonly duration;
9
10
  private readonly delay;
10
- constructor(end: Animatable, start: Animatable | undefined, duration: number, delay: number);
11
+ constructor(end: Animatable, start: Animatable | undefined, easing: (t: number) => number, duration: number, delay: number);
11
12
  appendTo(ctx: DOMContext): Clear;
12
13
  }
13
14
  export interface FadeInProps extends Animatable {
14
15
  start?: Animatable;
15
16
  duration?: number;
17
+ easing?: (t: number) => number;
16
18
  delay?: number;
17
19
  }
18
20
  export declare function FadeIn(props: FadeInProps): Renderable;
@@ -1,12 +1,14 @@
1
- import { getComputedAnimatable, applyInterpolatedAnimatable, applyAnimatable } from "./animatable";
1
+ import { getComputedAnimatable, applyInterpolatedAnimatable, applyAnimatable } from './animatable';
2
2
  export class FadeInImpl {
3
3
  end;
4
4
  start;
5
+ easing;
5
6
  duration;
6
7
  delay;
7
- constructor(end, start, duration, delay) {
8
+ constructor(end, start, easing, duration, delay) {
8
9
  this.end = end;
9
10
  this.start = start;
11
+ this.easing = easing;
10
12
  this.duration = duration;
11
13
  this.delay = delay;
12
14
  }
@@ -22,7 +24,7 @@ export class FadeInImpl {
22
24
  }
23
25
  })();
24
26
  const startTime = Date.now() + this.delay;
25
- const { duration, end } = this;
27
+ const { duration, end, easing } = this;
26
28
  let nextFrameId = null;
27
29
  function frame() {
28
30
  const now = Date.now();
@@ -31,7 +33,7 @@ export class FadeInImpl {
31
33
  return;
32
34
  }
33
35
  const progress = Math.min((now - startTime) / duration, 1);
34
- applyInterpolatedAnimatable(el, start, end, progress);
36
+ applyInterpolatedAnimatable(el, start, end, easing(progress));
35
37
  if (progress < 1) {
36
38
  nextFrameId = requestAnimationFrame(frame);
37
39
  }
@@ -47,6 +49,6 @@ export class FadeInImpl {
47
49
  }
48
50
  }
49
51
  export function FadeIn(props) {
50
- const { start, duration, delay, ...end } = props;
51
- return new FadeInImpl(end, start, duration ?? 200, delay ?? 0);
52
+ const { start, duration, easing, delay, ...end } = props;
53
+ return new FadeInImpl(end, start, easing ?? (v => v), duration ?? 200, delay ?? 0);
52
54
  }
@@ -1,7 +1,7 @@
1
- import { Clear } from "../clean";
2
- import { DOMContext } from "../dom-context";
3
- import { Renderable } from "../renderable";
4
- import { Animatable } from "./animatable";
1
+ import { type Clear } from '../clean';
2
+ import { type DOMContext } from '../dom-context';
3
+ import { type Renderable } from '../renderable';
4
+ import { type Animatable } from './animatable';
5
5
  export declare class FadeOutImpl implements Renderable {
6
6
  private readonly end;
7
7
  private readonly duration;
@@ -1,4 +1,4 @@
1
- import { getComputedAnimatable, applyInterpolatedAnimatable } from "./animatable";
1
+ import { getComputedAnimatable, applyInterpolatedAnimatable } from './animatable';
2
2
  export class FadeOutImpl {
3
3
  end;
4
4
  duration;
@@ -1,9 +1,18 @@
1
1
  import { type Signal } from '../prop';
2
- import { SeparatorProps } from './Repeat';
2
+ import { type PositionProps } from './Repeat';
3
3
  import { type JSX } from '../jsx-runtime';
4
4
  export interface ForProps<T> {
5
5
  of: Signal<T[]>;
6
- separator?: (value: Signal<SeparatorProps>) => JSX.DOMNode;
7
- children?: (value: Signal<T>, index: number) => JSX.DOMNode;
6
+ separator?: (value: Signal<PositionProps>) => JSX.DOMNode;
7
+ children?: (value: Signal<T>, pos: Signal<PositionProps>) => JSX.DOMNode;
8
8
  }
9
9
  export declare function For<T>({ of, children: render, separator }: ForProps<T>): JSX.DOMNode;
10
+ export interface ForWithPositionProps<T> {
11
+ of: Signal<T[]>;
12
+ separator?: (value: Signal<PositionProps>) => JSX.DOMNode;
13
+ children?: (data: Signal<{
14
+ value: T;
15
+ pos: PositionProps;
16
+ }>) => JSX.DOMNode;
17
+ }
18
+ export declare function ForWithPosition<T>({ of, children: render, separator }: ForWithPositionProps<T>): JSX.DOMNode;
package/components/For.js CHANGED
@@ -1,15 +1,27 @@
1
1
  import { RepeatImpl } from './Repeat';
2
2
  import { makeRenderable } from '../jsx-runtime';
3
3
  import { FragmentImpl } from './Fragment';
4
- import { OnRemoveImpl } from './OnRemove';
5
- // <For of={values}>{(value) => <span>{value}</span>}</For>
4
+ import { OnRemove } from './OnRemove';
5
+ // <For of={values} separator={() => ", "}>{(value) => <span>{value}</span>}</For>
6
6
  export function For({ of, children: render, separator }) {
7
7
  const times = of.map(v => v.length);
8
- return new RepeatImpl(times, (index) => {
9
- const value = of.at(index);
8
+ return new RepeatImpl(times, (pos) => {
9
+ // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
10
+ const value = of.combine(pos, (arr, { index }) => arr.at(index));
10
11
  return new FragmentImpl([
11
- makeRenderable(render?.(value, index)),
12
- new OnRemoveImpl(value.clean)
12
+ makeRenderable(render?.(value, pos)),
13
+ OnRemove({ clear: value.clean })
14
+ ]);
15
+ }, separator);
16
+ }
17
+ export function ForWithPosition({ of, children: render, separator }) {
18
+ const times = of.map(v => v.length);
19
+ return new RepeatImpl(times, (pos) => {
20
+ // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
21
+ const value = of.combine(pos, (arr, { index }) => arr.at(index));
22
+ return new FragmentImpl([
23
+ makeRenderable(render?.(value.combine(pos, (v, p) => ({ value: v, pos: p })))),
24
+ OnRemove({ clear: value.clean })
13
25
  ]);
14
26
  }, separator);
15
27
  }
@@ -1,9 +1,10 @@
1
1
  export class HiddenWhenEmptyImpl {
2
2
  appendTo(ctx) {
3
+ const initial = ctx.getStyle(':empty');
3
4
  ctx.setStyle(':empty', 'display: none');
4
5
  return (removeTree) => {
5
6
  if (removeTree)
6
- ctx.setStyle(':empty', null);
7
+ ctx.setStyle(':empty', initial);
7
8
  };
8
9
  }
9
10
  }
package/components/If.js CHANGED
@@ -1,18 +1,18 @@
1
1
  import { OneOfImpl } from './OneOf';
2
2
  export function If({ is, then, otherwise }) {
3
- return new OneOfImpl(is.map(v => v ? [1, true] : [2, false]), {
3
+ return new OneOfImpl(is.map(v => v ? { 1: true } : { 2: false }), {
4
4
  1: () => then,
5
5
  2: () => otherwise
6
6
  });
7
7
  }
8
8
  export function When({ is, children }) {
9
- return new OneOfImpl(is.map(v => v ? [1, true] : [2, false]), {
9
+ return new OneOfImpl(is.map(v => v ? { 1: true } : { 2: false }), {
10
10
  1: () => children,
11
11
  2: () => null
12
12
  });
13
13
  }
14
14
  export function Unless({ is, children }) {
15
- return new OneOfImpl(is.map(v => v ? [1, true] : [2, false]), {
15
+ return new OneOfImpl(is.map(v => v ? { 1: true } : { 2: false }), {
16
16
  1: () => null,
17
17
  2: () => children
18
18
  });
@@ -4,11 +4,11 @@ import { type Renderable } from '../renderable';
4
4
  export declare class LifecycleImpl implements Renderable {
5
5
  private readonly onMount;
6
6
  private readonly onUnmount;
7
- constructor(onMount: (el: HTMLElement) => void, onUnmount: (el: HTMLElement) => void);
7
+ constructor(onMount: (el: HTMLElement) => void, onUnmount: (el: HTMLElement, removeTree: boolean) => void);
8
8
  readonly appendTo: (ctx: DOMContext) => Clear;
9
9
  }
10
10
  export interface LifecycleProps {
11
11
  onMount?: (el: HTMLElement) => void;
12
- onUnmount?: (el: HTMLElement) => void;
12
+ onUnmount?: (el: HTMLElement, removeTree: boolean) => void;
13
13
  }
14
14
  export declare function Lifecycle({ onMount, onUnmount }: LifecycleProps): Renderable;
@@ -7,8 +7,8 @@ export class LifecycleImpl {
7
7
  }
8
8
  appendTo = (ctx) => {
9
9
  this.onMount(ctx.getElement());
10
- return () => {
11
- this.onUnmount(ctx.getElement());
10
+ return (removeTree) => {
11
+ this.onUnmount(ctx.getElement(), removeTree);
12
12
  };
13
13
  };
14
14
  }
@@ -1,10 +1,9 @@
1
1
  /** @jsxImportSource .. */
2
2
  import { type Signal } from '../prop';
3
- import { type AnyKey } from './OneOf';
4
3
  import { type JSX } from '../jsx-runtime';
5
4
  export interface NotEmptyProps<T> {
6
5
  on: Signal<T>;
7
6
  whenEmpty?: JSX.DOMNode;
8
7
  display: JSX.DOMNode;
9
8
  }
10
- export declare function NotEmpty<T extends unknown[] | Record<AnyKey, unknown>>({ on, display, whenEmpty }: NotEmptyProps<T>): JSX.DOMNode;
9
+ export declare function NotEmpty<T extends unknown[] | Record<any, unknown>>({ on, display, whenEmpty }: NotEmptyProps<T>): JSX.DOMNode;
@@ -1,11 +1,6 @@
1
1
  import { type Clear } from '../clean';
2
- import { type Renderable } from '../renderable';
3
- export declare class OnRemoveImpl implements Renderable {
4
- private readonly clear;
5
- constructor(clear: Clear);
6
- readonly appendTo: () => Clear;
7
- }
2
+ import { LifecycleImpl } from './Lifecycle';
8
3
  export interface OnRemoveProps {
9
4
  clear: Clear;
10
5
  }
11
- export declare function OnRemove(props: OnRemoveProps): OnRemoveImpl;
6
+ export declare function OnRemove(props: OnRemoveProps): LifecycleImpl;
@@ -1,14 +1,4 @@
1
- export class OnRemoveImpl {
2
- clear;
3
- constructor(clear) {
4
- this.clear = clear;
5
- }
6
- appendTo = () => {
7
- return (removeTree) => {
8
- this.clear(removeTree);
9
- };
10
- };
11
- }
1
+ import { LifecycleImpl } from './Lifecycle';
12
2
  export function OnRemove(props) {
13
- return new OnRemoveImpl(props.clear);
3
+ return new LifecycleImpl(() => { }, (_, removeTree) => { props.clear(removeTree); });
14
4
  }
@@ -3,18 +3,60 @@ import { type Clear } from '../clean';
3
3
  import { type DOMContext } from '../dom-context';
4
4
  import { type Renderable } from '../renderable';
5
5
  import { type JSX } from '../jsx';
6
- export type AnyKey = string | number | symbol;
7
- export declare class OneOfImpl<T extends [AnyKey, unknown]> implements Renderable {
6
+ export declare class OneOfImpl<T extends Record<string, unknown>> implements Renderable {
8
7
  private readonly match;
9
8
  private readonly cases;
10
9
  constructor(match: Signal<T>, cases: {
11
- [KK in T[0]]: (value: Signal<T[1]>) => JSX.DOMNode;
10
+ [KK in keyof T]: (value: Signal<T[KK]>) => JSX.DOMNode;
12
11
  });
13
12
  readonly appendTo: (ctx: DOMContext) => Clear;
14
13
  }
15
- export type OneOfProps<T extends [AnyKey, unknown]> = {
14
+ export type OneOfProps<T extends Record<string, unknown>> = {
16
15
  match: Signal<T>;
17
16
  } & {
18
- [KK in T[0]]: (value: Signal<T[1]>) => JSX.DOMNode;
17
+ [KK in keyof T]: (value: Signal<T[KK]>) => JSX.DOMNode;
19
18
  };
20
- export declare function OneOf<T extends [AnyKey, unknown]>(props: OneOfProps<T>): JSX.DOMNode;
19
+ export declare function OneOf<T extends Record<string, unknown>>(props: OneOfProps<T>): JSX.DOMNode;
20
+ export type OneOfLiteralProps<K extends string> = {
21
+ match: Signal<K>;
22
+ } & {
23
+ [KK in K]: JSX.DOMNode;
24
+ };
25
+ export declare function OneOfLiteral<K extends string>(props: OneOfLiteralProps<K>): JSX.DOMNode;
26
+ export type OneOfUnionProps<T extends {
27
+ [_ in K]: string;
28
+ }, K extends string> = {
29
+ match: Signal<T>;
30
+ using: K;
31
+ } & {
32
+ [KK in T[K]]: (value: Signal<T extends {
33
+ [_ in K]: KK;
34
+ } ? T : never>) => JSX.DOMNode;
35
+ };
36
+ export declare function OneOfUnion<T extends {
37
+ [_ in K]: string;
38
+ }, K extends string>(props: OneOfUnionProps<T, K>): JSX.DOMNode;
39
+ export type OneOfUnionTypeProps<T extends {
40
+ [_ in 'type']: string;
41
+ }> = {
42
+ match: Signal<T>;
43
+ } & {
44
+ [KK in T['type']]: (value: Signal<T extends {
45
+ [_ in 'type']: KK;
46
+ } ? T : never>) => JSX.DOMNode;
47
+ };
48
+ export declare function OneOfUnionType<T extends {
49
+ type: string;
50
+ }>(props: OneOfUnionTypeProps<T>): JSX.DOMNode;
51
+ export type OneOfUnionKindProps<T extends {
52
+ [_ in 'kind']: string;
53
+ }> = {
54
+ match: Signal<T>;
55
+ } & {
56
+ [KK in T['kind']]: (value: Signal<T extends {
57
+ [_ in 'kind']: KK;
58
+ } ? T : never>) => JSX.DOMNode;
59
+ };
60
+ export declare function OneOfUnionKind<T extends {
61
+ kind: string;
62
+ }>(props: OneOfUnionKindProps<T>): JSX.DOMNode;
@@ -9,16 +9,17 @@ export class OneOfImpl {
9
9
  }
10
10
  appendTo = (ctx) => {
11
11
  const pair = this.match.get();
12
- let key = pair[0];
13
- const value = pair[1];
14
- const stableCtx = ctx.makeReference();
12
+ let key = Object.keys(pair)[0];
13
+ const value = pair[key];
15
14
  let prop = new Prop(value);
16
- let newCtx = stableCtx.makeReference();
15
+ let newCtx = ctx.makeReference();
17
16
  let clear = makeRenderable(this.cases[key](prop)).appendTo(newCtx);
18
- const cancel = this.match.subscribe(([newKey, newValue]) => {
17
+ const cancel = this.match.subscribe((newPair) => {
18
+ const newKey = Object.keys(newPair)[0];
19
+ const newValue = newPair[newKey];
19
20
  if (newKey !== key) {
20
21
  newCtx.requestClear(true, () => {
21
- newCtx = stableCtx.makeReference();
22
+ newCtx = newCtx.makeReference();
22
23
  key = newKey;
23
24
  prop.clean();
24
25
  prop = new Prop(newValue);
@@ -32,14 +33,36 @@ export class OneOfImpl {
32
33
  });
33
34
  return (removeTree) => {
34
35
  newCtx.requestClear(removeTree, () => {
36
+ clear(removeTree);
35
37
  cancel();
36
38
  prop.clean();
37
- stableCtx.requestClear(removeTree, () => { });
38
39
  });
39
40
  };
40
41
  };
41
42
  }
42
- // <OneOf match={counter.map(v => v % 2 == 0 ? [1, "odd"] : [2, "even"])} 1={t => <b>{t}</b>} 2={t => <i>{t}</i>} /
43
+ // <OneOf match={counter.map(v => v % 2 == 0 ? {1: "odd"} : {2: "even"})} 1={t => <b>{t}</b>} 2={t => <i>{t}</i>} /
43
44
  export function OneOf(props) {
44
45
  return new OneOfImpl(props.match, props);
45
46
  }
47
+ export function OneOfLiteral(props) {
48
+ const { match, ...cases } = props;
49
+ const keys = Object.keys(cases);
50
+ const obj = keys.reduce((acc, k) => {
51
+ acc[k] = () => cases[k];
52
+ return acc;
53
+ // eslint-disable-next-line @typescript-eslint/prefer-reduce-type-parameter, @typescript-eslint/consistent-type-assertions
54
+ }, {});
55
+ return new OneOfImpl(
56
+ // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
57
+ match.map(k => ({ [k]: null })), obj);
58
+ }
59
+ export function OneOfUnion(props) {
60
+ const { match, using, ...cases } = props;
61
+ return new OneOfImpl(match.map(t => ({ [t[using]]: t })), cases);
62
+ }
63
+ export function OneOfUnionType(props) {
64
+ return OneOfUnion({ ...props, using: 'type' });
65
+ }
66
+ export function OneOfUnionKind(props) {
67
+ return OneOfUnion({ ...props, using: 'kind' });
68
+ }
@@ -1,25 +1,28 @@
1
- import { Signal } from '../prop';
1
+ /** @jsxImportSource .. */
2
+ import { type Signal } from '../prop';
2
3
  import { type Clear } from '../clean';
3
4
  import { type DOMContext } from '../dom-context';
4
5
  import { type Renderable } from '../renderable';
5
- import { JSX } from '../jsx';
6
- export interface SeparatorProps {
7
- first: boolean;
8
- last: boolean;
9
- index: number;
6
+ import { type JSX } from '../jsx';
7
+ export interface PositionProps {
8
+ readonly first: boolean;
9
+ readonly last: boolean;
10
+ readonly index: number;
10
11
  }
12
+ export declare function makePosition(index: number, length: number): PositionProps;
11
13
  export declare class RepeatImpl implements Renderable {
12
14
  private readonly times;
13
15
  private readonly children;
14
16
  private readonly separator?;
15
- constructor(times: Signal<number>, children: (index: number) => JSX.DOMNode, separator?: ((sep: Signal<SeparatorProps>) => JSX.DOMNode) | undefined);
17
+ constructor(times: Signal<number>, children: (pos: Signal<PositionProps>) => JSX.DOMNode, separator?: ((sep: Signal<PositionProps>) => JSX.DOMNode) | undefined);
16
18
  readonly appendTo: (ctx: DOMContext) => Clear;
17
19
  readonly appendToWithoutSeparator: (ctx: DOMContext) => Clear;
18
- readonly appendToWithSeparator: (ctx: DOMContext, separator: (sep: Signal<SeparatorProps>) => JSX.DOMNode) => Clear;
20
+ readonly appendToWithSeparator: (ctx: DOMContext, separator: (sep: Signal<PositionProps>) => JSX.DOMNode) => Clear;
19
21
  }
20
22
  export interface RepeatProps {
21
23
  times: Signal<number>;
22
- children?: (index: number) => JSX.DOMNode;
23
- separator?: (sep: Signal<SeparatorProps>) => JSX.DOMNode;
24
+ children?: (pos: Signal<PositionProps>) => JSX.DOMNode;
25
+ separator?: (sep: Signal<PositionProps>) => JSX.DOMNode;
24
26
  }
25
27
  export declare function Repeat(props: RepeatProps): Renderable;
28
+ export declare function conjuctions(other: JSX.DOMNode, lastConjunction?: JSX.DOMNode, firstConjunction?: JSX.DOMNode): (sep: Signal<PositionProps>) => JSX.DOMNode;
@@ -1,6 +1,16 @@
1
+ import { jsx as _jsx } from "../jsx-runtime";
2
+ /** @jsxImportSource .. */
1
3
  import { Prop } from '../prop';
2
4
  import { Fragment } from './Fragment';
3
5
  import { makeRenderable } from '../jsx-runtime';
6
+ import { OneOf } from './OneOf';
7
+ export function makePosition(index, length) {
8
+ return {
9
+ first: index === 0,
10
+ last: index === length - 1,
11
+ index
12
+ };
13
+ }
4
14
  export class RepeatImpl {
5
15
  times;
6
16
  children;
@@ -11,7 +21,7 @@ export class RepeatImpl {
11
21
  this.separator = separator;
12
22
  }
13
23
  appendTo = (ctx) => {
14
- if (!this.separator) {
24
+ if (this.separator == null) {
15
25
  return this.appendToWithoutSeparator(ctx);
16
26
  }
17
27
  else {
@@ -22,16 +32,24 @@ export class RepeatImpl {
22
32
  const newCtx = ctx.makeReference();
23
33
  const count = this.times.get();
24
34
  const clears = new Array(count);
35
+ const positions = new Array(count);
25
36
  for (let i = 0; i < count; i++) {
26
- clears[i] = makeRenderable(this.children(i)).appendTo(newCtx);
37
+ positions[i] = new Prop(makePosition(i, count));
38
+ clears[i] = makeRenderable(this.children(positions[i])).appendTo(newCtx);
27
39
  }
28
40
  const cancel = this.times.subscribe((newCount) => {
29
41
  while (newCount < clears.length) {
30
42
  // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
31
43
  clears.pop()(true);
44
+ // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
45
+ positions.pop().clean();
46
+ }
47
+ for (let i = 0; i < positions.length; i++) {
48
+ positions[i].set(makePosition(i, newCount));
32
49
  }
33
50
  for (let i = clears.length; i < newCount; i++) {
34
- clears[i] = makeRenderable(this.children(i)).appendTo(newCtx);
51
+ positions[i] = new Prop(makePosition(i, count));
52
+ clears[i] = makeRenderable(this.children(positions[i])).appendTo(newCtx);
35
53
  }
36
54
  });
37
55
  return (removeTree) => {
@@ -47,8 +65,10 @@ export class RepeatImpl {
47
65
  const separatorProps = new Array(Math.max(0, count - 1));
48
66
  const separatorClears = new Array(Math.max(0, count - 1));
49
67
  const clears = new Array(count);
68
+ const positions = new Array(count);
50
69
  for (let i = 0; i < count; i++) {
51
- clears[i] = makeRenderable(this.children(i)).appendTo(newCtx);
70
+ positions[i] = new Prop(makePosition(i, count));
71
+ clears[i] = makeRenderable(this.children(positions[i])).appendTo(newCtx);
52
72
  if (i < count - 1) {
53
73
  separatorProps[i] = Prop.of({
54
74
  first: i === 0,
@@ -62,6 +82,8 @@ export class RepeatImpl {
62
82
  while (newCount < clears.length) {
63
83
  // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
64
84
  clears.pop()(true);
85
+ // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
86
+ positions.pop().clean();
65
87
  if (separatorClears.length > 0) {
66
88
  // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
67
89
  separatorClears.pop()(true);
@@ -69,6 +91,9 @@ export class RepeatImpl {
69
91
  separatorProps.pop().clean();
70
92
  }
71
93
  }
94
+ for (let i = 0; i < positions.length; i++) {
95
+ positions[i].set(makePosition(i, newCount));
96
+ }
72
97
  for (let i = 0; i < separatorProps.length; i++) {
73
98
  separatorProps[i].set({
74
99
  first: i === 0,
@@ -77,7 +102,8 @@ export class RepeatImpl {
77
102
  });
78
103
  }
79
104
  for (let i = clears.length; i < newCount; i++) {
80
- clears[i] = makeRenderable(this.children(i)).appendTo(newCtx);
105
+ positions[i] = new Prop(makePosition(i, count));
106
+ clears[i] = makeRenderable(this.children(positions[i])).appendTo(newCtx);
81
107
  if (i < newCount - 1) {
82
108
  separatorProps[i] = Prop.of({
83
109
  first: i === 0,
@@ -101,3 +127,18 @@ export class RepeatImpl {
101
127
  export function Repeat(props) {
102
128
  return new RepeatImpl(props.times, props.children ?? (() => Fragment({ children: [] })), props.separator);
103
129
  }
130
+ export function conjuctions(other, lastConjunction, firstConjunction) {
131
+ return (sep) => {
132
+ return _jsx(OneOf, { match: sep.map(({ first, last }) => {
133
+ if (last) {
134
+ return { last: true };
135
+ }
136
+ else if (first) {
137
+ return { first: true };
138
+ }
139
+ else {
140
+ return { other: true };
141
+ }
142
+ }), first: () => firstConjunction ?? other, last: () => lastConjunction ?? other, other: () => other });
143
+ };
144
+ }
@@ -23,8 +23,15 @@ export interface Animatable {
23
23
  marginRight?: number;
24
24
  fontSize?: number;
25
25
  letterSpacing?: number;
26
+ color?: string;
27
+ backgroundColor?: string;
28
+ borderColor?: string;
26
29
  borderWidth?: number;
27
30
  borderRadius?: number;
31
+ boxShadow?: string;
32
+ textShadow?: string;
33
+ outlineWidth?: number;
34
+ outlineColor?: string;
28
35
  translateX?: number;
29
36
  translateY?: number;
30
37
  translateZ?: number;
@@ -45,6 +52,19 @@ export interface Animatable {
45
52
  contrast?: number;
46
53
  blur?: number;
47
54
  }
55
+ export type ColorChannels = [number, number, number, number, 'rgba' | 'hex' | 'hsla'];
56
+ export declare function parseColorChannels(color: string): ColorChannels;
57
+ export interface BoxShadow {
58
+ inset: boolean;
59
+ x: number;
60
+ y: number;
61
+ blur: number;
62
+ spread: number;
63
+ color: string;
64
+ }
65
+ export declare function colorChannelsToString(channels: ColorChannels): string;
66
+ export declare function interpolateColor(startColor: string, endColor: string): (t: number) => string;
67
+ export declare function interpolateShadow(startShadow: string, endShadow: string): (t: number) => string;
48
68
  export declare function getComputedAnimatableProp(styles: CSSStyleDeclaration, key: keyof Animatable): Animatable[typeof key];
49
69
  export declare function getComputedAnimatable(el: HTMLElement, styles: Animatable): Animatable;
50
70
  export declare function applyAnimatableProp(el: HTMLElement, key: keyof Animatable, value: Animatable[typeof key]): void;
@@ -1,3 +1,104 @@
1
+ export function parseColorChannels(color) {
2
+ let match = color.match(/rgba?\((\d+), (\d+), (\d+)(, (\d+))?\)/);
3
+ if (match != null) {
4
+ return [
5
+ Number(match[1]),
6
+ Number(match[2]),
7
+ Number(match[3]),
8
+ match[4] != null ? Number(match[5]) : 1,
9
+ 'rgba'
10
+ ];
11
+ }
12
+ else {
13
+ match = color.match(/#([0-9a-f]{2})([0-9a-f]{2})([0-9a-f]{2})/);
14
+ if (match != null) {
15
+ return [
16
+ parseInt(match[1], 16),
17
+ parseInt(match[2], 16),
18
+ parseInt(match[3], 16),
19
+ 1,
20
+ 'hex'
21
+ ];
22
+ }
23
+ else {
24
+ match = color.match(/hsla?\((\d+), (\d+)%?, (\d+)%?(, (\d+))?\)/);
25
+ if (match != null) {
26
+ return [
27
+ Number(match[1]),
28
+ Number(match[2]),
29
+ Number(match[3]),
30
+ match[4] != null ? Number(match[5]) : 1,
31
+ 'hsla'
32
+ ];
33
+ }
34
+ }
35
+ }
36
+ return [0, 0, 0, 1, 'rgba'];
37
+ }
38
+ function parseBoxShadow(cssString) {
39
+ const boxShadowRegex = /^(inset\s)?(-?\d+)([a-zA-Z]*)(\s+)(-?\d+)([a-zA-Z]*)(?:\s+(-?\d+)([a-zA-Z]*))?(?:\s+(-?\d+)([a-zA-Z]*))?(?:\s+(-?\d+)([a-zA-Z]*))?(?:\s+)([a-zA-Z0-9(),.]+)$/i;
40
+ const match = cssString.match(boxShadowRegex);
41
+ if (match == null) {
42
+ return {
43
+ inset: false,
44
+ x: 0,
45
+ y: 0,
46
+ blur: 0,
47
+ spread: 0,
48
+ color: 'rgba(0, 0, 0, 0)'
49
+ };
50
+ }
51
+ const [, inset, x, , , y, , blur, , spread, , color] = match;
52
+ const parsedBlur = blur != null ? parseInt(blur, 10) : 0;
53
+ const parsedSpread = spread != null ? parseInt(spread, 10) : 0;
54
+ return {
55
+ inset: !!inset,
56
+ x: parseInt(x, 10),
57
+ y: parseInt(y, 10),
58
+ blur: parsedBlur,
59
+ spread: parsedSpread,
60
+ color
61
+ };
62
+ }
63
+ function boxShadowToString(shadow) {
64
+ const { inset, x, y, blur, spread, color } = shadow;
65
+ return `${inset ? 'inset ' : ''}${x}px ${y}px ${blur}px ${spread}px ${color}`;
66
+ }
67
+ export function colorChannelsToString(channels) {
68
+ if (channels[4] === 'rgba') {
69
+ return `rgba(${channels[0]}, ${channels[1]}, ${channels[2]}, ${channels[3]})`;
70
+ }
71
+ else if (channels[4] === 'hex') {
72
+ return `#${channels[0].toString(16).padStart(2, '0')}${channels[1].toString(16).padStart(2, '0')}${channels[2].toString(16).padStart(2, '0')}`;
73
+ }
74
+ else if (channels[4] === 'hsla') {
75
+ return `hsla(${channels[0]}, ${channels[1]}%, ${channels[2]}%, ${channels[3]})`;
76
+ }
77
+ return '';
78
+ }
79
+ export function interpolateColor(startColor, endColor) {
80
+ const [startR, startG, startB, startA, startType] = parseColorChannels(startColor);
81
+ const [endR, endG, endB, endA] = parseColorChannels(endColor);
82
+ return (t) => {
83
+ const r = startR + (endR - startR) * t;
84
+ const g = startG + (endG - startG) * t;
85
+ const b = startB + (endB - startB) * t;
86
+ const a = startA + (endA - startA) * t;
87
+ return colorChannelsToString([r, g, b, a, startType]);
88
+ };
89
+ }
90
+ export function interpolateShadow(startShadow, endShadow) {
91
+ const start = parseBoxShadow(startShadow);
92
+ const end = parseBoxShadow(endShadow);
93
+ return (t) => {
94
+ const x = start.x + (end.x - start.x) * t;
95
+ const y = start.y + (end.y - start.y) * t;
96
+ const blur = start.blur + (end.blur - start.blur) * t;
97
+ const spread = start.spread + (end.spread - start.spread) * t;
98
+ const color = getColorInterpolation(start.color, end.color)(t);
99
+ return boxShadowToString({ inset: start.inset, x, y, blur, spread, color });
100
+ };
101
+ }
1
102
  export function getComputedAnimatableProp(styles, key) {
2
103
  if (key === 'translateX') {
3
104
  return new WebKitCSSMatrix(styles.transform).m41;
@@ -131,10 +232,36 @@ export function applyAnimatableProp(el, key, value) {
131
232
  }
132
233
  el.style.setProperty(key, String(value));
133
234
  }
235
+ const interpolationCache = new Map();
236
+ function getInterpolate(from, to, type) {
237
+ if (interpolationCache.has(type + ':' + from + to)) {
238
+ // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
239
+ return interpolationCache.get(from + to);
240
+ }
241
+ const f = interpolateColor(from, to);
242
+ interpolationCache.set(type + ':' + from + to, f);
243
+ return f;
244
+ }
245
+ function getColorInterpolation(from, to) {
246
+ return getInterpolate(from, to, 'c');
247
+ }
248
+ function getShadowInterpolation(from, to) {
249
+ return getInterpolate(from, to, 's');
250
+ }
134
251
  export function applyInterpolatedAnimatableProp(el, key, from, to, progress) {
135
252
  if (from != null && to != null) {
136
- const value = from + (to - from) * progress;
137
- applyAnimatableProp(el, key, value);
253
+ if (typeof from === 'number' && typeof to === 'number') {
254
+ const value = from + (to - from) * progress;
255
+ applyAnimatableProp(el, key, value);
256
+ }
257
+ else if (key === 'boxShadow' || key === 'textShadow') {
258
+ const value = getShadowInterpolation(from, to)(progress);
259
+ applyAnimatableProp(el, key, value);
260
+ }
261
+ else if (key === 'color' || key === 'backgroundColor' || key === 'borderColor' || key === 'outlineColor') {
262
+ const value = getColorInterpolation(from, to)(progress);
263
+ applyAnimatableProp(el, key, value);
264
+ }
138
265
  }
139
266
  }
140
267
  export function applyInterpolatedAnimatable(el, from, to, progress) {
package/dom-context.d.ts CHANGED
@@ -33,6 +33,7 @@ export declare class DOMContext {
33
33
  private clear;
34
34
  withProvider<T>(mark: ProviderMark<T>, provider: T): DOMContext;
35
35
  getProvider<T>(mark: ProviderMark<T>): T;
36
+ getStyle(name: string): string | undefined | null;
36
37
  setStyle(name: string, value: string | undefined | null): void;
37
38
  createStyle(name: string, value: string | undefined | null): [(newValue: string) => void, Clear];
38
39
  }
package/dom-context.js CHANGED
@@ -235,6 +235,9 @@ export class DOMContext {
235
235
  getProvider(mark) {
236
236
  return this.providers[mark];
237
237
  }
238
+ getStyle(name) {
239
+ return this.element.style.getPropertyValue(name);
240
+ }
238
241
  setStyle(name, value) {
239
242
  if (value == null) {
240
243
  this.element.style.removeProperty(name);
package/index.d.ts CHANGED
@@ -1,36 +1,33 @@
1
- import { type Clear, type Clean } from './clean';
2
- import { makeProviderMark, DOMContext, type ProviderMark } from './dom-context';
3
- import { Prop, Signal } from './prop';
4
- import { render } from './render';
5
- import { type Renderable } from './renderable';
6
- import { isEmptyElement } from './helpers/is-empty-element';
7
- import { handleTextInput } from './helpers/handle-text-input';
8
- import { handleAnchorClick } from './helpers/handle-anchor-click';
9
- import { Animatable, applyInterpolatedAnimatableProp, applyInterpolatedAnimatable, applyAnimatableProp, applyAnimatable, getComputedAnimatableProp, getComputedAnimatable } from './components/animatable';
10
- import { AttributeImpl, Attribute, type AttributeProps } from './components/Attribute';
11
- import { BooleanAttributeImpl, BooleanAttribute, type BooleanAttributeProps } from './components/BooleanAttribute';
12
- import { ClassNameImpl, ClassName, type ClassNameProps } from './components/ClassName';
13
- import { ConsumerImpl, Consumer, type ConsumerProps, ProviderImpl, Provider, type ProviderProps } from './components/Provider';
14
- import { ElImpl, El, type ElProps } from './components/El';
15
- import { FadeIn, FadeInImpl, FadeInProps } from './components/FadeIn';
16
- import { FadeOut, FadeOutImpl, FadeOutProps } from './components/FadeOut';
17
- import { For, type ForProps } from './components/For';
18
- import { FragmentImpl, Fragment } from './components/Fragment';
19
- import { HiddenWhenEmptyImpl, HiddenWhenEmpty } from './components/HiddenWhenEmpty';
20
- import { If, type IfProps, Unless, When, type WhenProps } from './components/If';
21
- import { InnerHTMLImpl, InnerHTML, type InnerHTMLProps } from './components/InnerHTML';
22
- import { Lifecycle, LifecycleImpl, type LifecycleProps } from './components/Lifecycle';
23
- import { MatchImpl, Match, type MatchProps } from './components/Match';
24
- import { NotEmpty, type NotEmptyProps } from './components/NotEmpty';
25
- import { OnImpl, On, type OnProps } from './components/On';
26
- import { OnRemoveImpl, OnRemove, type OnRemoveProps } from './components/OnRemove';
27
- import { OneOfImpl, OneOf, type OneOfProps } from './components/OneOf';
28
- import { PortalImpl, Portal, type PortalProps } from './components/Portal';
29
- import { PropertyImpl, Property, type PropertyProps } from './components/Property';
30
- import { RepeatImpl, Repeat, type RepeatProps } from './components/Repeat';
31
- import { ShowImpl, Show, type ShowProps } from './components/Show';
32
- import { TextImpl, Text, type TextProps } from './components/Text';
33
- import { TextContentImpl, TextContent, type TextContentProps } from './components/TextContent';
34
- import type { JSX } from './jsx-runtime';
35
- export { applyInterpolatedAnimatableProp, applyInterpolatedAnimatable, applyAnimatableProp, applyAnimatable, getComputedAnimatableProp, getComputedAnimatable, AttributeImpl, Attribute, BooleanAttributeImpl, BooleanAttribute, ClassNameImpl, ClassName, ConsumerImpl, Consumer, DOMContext, ElImpl, El, FadeIn, FadeInImpl, FadeOut, FadeOutImpl, For, FragmentImpl, Fragment, handleTextInput, handleAnchorClick, HiddenWhenEmptyImpl, HiddenWhenEmpty, If, InnerHTMLImpl, InnerHTML, isEmptyElement, Lifecycle, LifecycleImpl, makeProviderMark, MatchImpl, Match, NotEmpty, OnImpl, On, OnRemoveImpl, OnRemove, OneOfImpl, OneOf, PortalImpl, Portal, Prop, PropertyImpl, Property, ProviderImpl, Provider, render, RepeatImpl, Repeat, ShowImpl, Show, Signal, TextImpl, Text, TextContentImpl, TextContent, Unless, When };
36
- export type { AttributeProps, BooleanAttributeProps, ClassNameProps, Clean, Clear, ConsumerProps, ElProps, FadeInProps, FadeOutProps, ForProps, InnerHTMLProps, IfProps, JSX, LifecycleProps, MatchProps, NotEmptyProps, OnProps, OnRemoveProps, OneOfProps, PortalProps, PropertyProps, ProviderMark, ProviderProps, Renderable, RepeatProps, ShowProps, TextProps, TextContentProps, Animatable, WhenProps };
1
+ export { type Clear, type Clean } from './clean';
2
+ export { makeProviderMark, DOMContext, type ProviderMark } from './dom-context';
3
+ export { Prop, Signal } from './prop';
4
+ export { render } from './render';
5
+ export { type Renderable } from './renderable';
6
+ export { isEmptyElement } from './helpers/is-empty-element';
7
+ export { handleTextInput } from './helpers/handle-text-input';
8
+ export { handleAnchorClick } from './helpers/handle-anchor-click';
9
+ export { type Animatable, applyInterpolatedAnimatableProp, applyInterpolatedAnimatable, applyAnimatableProp, applyAnimatable, getComputedAnimatableProp, getComputedAnimatable } from './components/animatable';
10
+ export { AttributeImpl, Attribute, type AttributeProps } from './components/Attribute';
11
+ export { BooleanAttributeImpl, BooleanAttribute, type BooleanAttributeProps } from './components/BooleanAttribute';
12
+ export { ClassNameImpl, ClassName, type ClassNameProps } from './components/ClassName';
13
+ export { ConsumerImpl, Consumer, type ConsumerProps, ProviderImpl, Provider, type ProviderProps } from './components/Provider';
14
+ export { ElImpl, El, type ElProps } from './components/El';
15
+ export { FadeIn, FadeInImpl, type FadeInProps } from './components/FadeIn';
16
+ export { FadeOut, FadeOutImpl, type FadeOutProps } from './components/FadeOut';
17
+ export { For, type ForProps } from './components/For';
18
+ export { FragmentImpl, Fragment } from './components/Fragment';
19
+ export { HiddenWhenEmptyImpl, HiddenWhenEmpty } from './components/HiddenWhenEmpty';
20
+ export { If, type IfProps, Unless, When, type WhenProps } from './components/If';
21
+ export { InnerHTMLImpl, InnerHTML, type InnerHTMLProps } from './components/InnerHTML';
22
+ export { Lifecycle, LifecycleImpl, type LifecycleProps } from './components/Lifecycle';
23
+ export { NotEmpty, type NotEmptyProps } from './components/NotEmpty';
24
+ export { OnImpl, On, type OnProps } from './components/On';
25
+ export { OnRemove, type OnRemoveProps } from './components/OnRemove';
26
+ export { OneOfImpl, OneOf, type OneOfProps, OneOfLiteral, type OneOfLiteralProps, OneOfUnion, type OneOfUnionProps, OneOfUnionKind, type OneOfUnionKindProps, OneOfUnionType, type OneOfUnionTypeProps } from './components/OneOf';
27
+ export { PortalImpl, Portal, type PortalProps } from './components/Portal';
28
+ export { PropertyImpl, Property, type PropertyProps } from './components/Property';
29
+ export { RepeatImpl, Repeat, type RepeatProps, conjuctions, type PositionProps } from './components/Repeat';
30
+ export { ShowImpl, Show, type ShowProps } from './components/Show';
31
+ export { TextImpl, Text, type TextProps } from './components/Text';
32
+ export { TextContentImpl, TextContent, type TextContentProps } from './components/TextContent';
33
+ export type { JSX } from './jsx-runtime';
package/index.js CHANGED
@@ -1,32 +1,30 @@
1
- import { makeProviderMark, DOMContext } from './dom-context';
2
- import { Prop, Signal } from './prop';
3
- import { render } from './render';
4
- import { isEmptyElement } from './helpers/is-empty-element';
5
- import { handleTextInput } from './helpers/handle-text-input';
6
- import { handleAnchorClick } from './helpers/handle-anchor-click';
7
- import { applyInterpolatedAnimatableProp, applyInterpolatedAnimatable, applyAnimatableProp, applyAnimatable, getComputedAnimatableProp, getComputedAnimatable } from './components/animatable';
8
- import { AttributeImpl, Attribute } from './components/Attribute';
9
- import { BooleanAttributeImpl, BooleanAttribute } from './components/BooleanAttribute';
10
- import { ClassNameImpl, ClassName } from './components/ClassName';
11
- import { ConsumerImpl, Consumer, ProviderImpl, Provider } from './components/Provider';
12
- import { ElImpl, El } from './components/El';
13
- import { FadeIn, FadeInImpl } from './components/FadeIn';
14
- import { FadeOut, FadeOutImpl } from './components/FadeOut';
15
- import { For } from './components/For';
16
- import { FragmentImpl, Fragment } from './components/Fragment';
17
- import { HiddenWhenEmptyImpl, HiddenWhenEmpty } from './components/HiddenWhenEmpty';
18
- import { If, Unless, When } from './components/If';
19
- import { InnerHTMLImpl, InnerHTML } from './components/InnerHTML';
20
- import { Lifecycle, LifecycleImpl } from './components/Lifecycle';
21
- import { MatchImpl, Match } from './components/Match';
22
- import { NotEmpty } from './components/NotEmpty';
23
- import { OnImpl, On } from './components/On';
24
- import { OnRemoveImpl, OnRemove } from './components/OnRemove';
25
- import { OneOfImpl, OneOf } from './components/OneOf';
26
- import { PortalImpl, Portal } from './components/Portal';
27
- import { PropertyImpl, Property } from './components/Property';
28
- import { RepeatImpl, Repeat } from './components/Repeat';
29
- import { ShowImpl, Show } from './components/Show';
30
- import { TextImpl, Text } from './components/Text';
31
- import { TextContentImpl, TextContent } from './components/TextContent';
32
- export { applyInterpolatedAnimatableProp, applyInterpolatedAnimatable, applyAnimatableProp, applyAnimatable, getComputedAnimatableProp, getComputedAnimatable, AttributeImpl, Attribute, BooleanAttributeImpl, BooleanAttribute, ClassNameImpl, ClassName, ConsumerImpl, Consumer, DOMContext, ElImpl, El, FadeIn, FadeInImpl, FadeOut, FadeOutImpl, For, FragmentImpl, Fragment, handleTextInput, handleAnchorClick, HiddenWhenEmptyImpl, HiddenWhenEmpty, If, InnerHTMLImpl, InnerHTML, isEmptyElement, Lifecycle, LifecycleImpl, makeProviderMark, MatchImpl, Match, NotEmpty, OnImpl, On, OnRemoveImpl, OnRemove, OneOfImpl, OneOf, PortalImpl, Portal, Prop, PropertyImpl, Property, ProviderImpl, Provider, render, RepeatImpl, Repeat, ShowImpl, Show, Signal, TextImpl, Text, TextContentImpl, TextContent, Unless, When };
1
+ export { makeProviderMark, DOMContext } from './dom-context';
2
+ export { Prop, Signal } from './prop';
3
+ export { render } from './render';
4
+ export { isEmptyElement } from './helpers/is-empty-element';
5
+ export { handleTextInput } from './helpers/handle-text-input';
6
+ export { handleAnchorClick } from './helpers/handle-anchor-click';
7
+ export { applyInterpolatedAnimatableProp, applyInterpolatedAnimatable, applyAnimatableProp, applyAnimatable, getComputedAnimatableProp, getComputedAnimatable } from './components/animatable';
8
+ export { AttributeImpl, Attribute } from './components/Attribute';
9
+ export { BooleanAttributeImpl, BooleanAttribute } from './components/BooleanAttribute';
10
+ export { ClassNameImpl, ClassName } from './components/ClassName';
11
+ export { ConsumerImpl, Consumer, ProviderImpl, Provider } from './components/Provider';
12
+ export { ElImpl, El } from './components/El';
13
+ export { FadeIn, FadeInImpl } from './components/FadeIn';
14
+ export { FadeOut, FadeOutImpl } from './components/FadeOut';
15
+ export { For } from './components/For';
16
+ export { FragmentImpl, Fragment } from './components/Fragment';
17
+ export { HiddenWhenEmptyImpl, HiddenWhenEmpty } from './components/HiddenWhenEmpty';
18
+ export { If, Unless, When } from './components/If';
19
+ export { InnerHTMLImpl, InnerHTML } from './components/InnerHTML';
20
+ export { Lifecycle, LifecycleImpl } from './components/Lifecycle';
21
+ export { NotEmpty } from './components/NotEmpty';
22
+ export { OnImpl, On } from './components/On';
23
+ export { OnRemove } from './components/OnRemove';
24
+ export { OneOfImpl, OneOf, OneOfLiteral, OneOfUnion, OneOfUnionKind, OneOfUnionType } from './components/OneOf';
25
+ export { PortalImpl, Portal } from './components/Portal';
26
+ export { PropertyImpl, Property } from './components/Property';
27
+ export { RepeatImpl, Repeat, conjuctions } from './components/Repeat';
28
+ export { ShowImpl, Show } from './components/Show';
29
+ export { TextImpl, Text } from './components/Text';
30
+ export { TextContentImpl, TextContent } from './components/TextContent';
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "name": "@tempots/dom",
3
3
  "main": "index.js",
4
4
  "types": "index.d.ts",
5
- "version": "5.1.0",
5
+ "version": "7.0.0",
6
6
  "scripts": {
7
7
  "watch": "tsc --watch",
8
8
  "build": "tsc",