@esportsplus/ui 0.18.0 → 0.19.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.
Files changed (93) hide show
  1. package/.github/dependabot.yml +2 -0
  2. package/.github/workflows/bump.yml +2 -0
  3. package/.github/workflows/dependabot.yml +12 -0
  4. package/.github/workflows/publish.yml +2 -0
  5. package/build/components/accordion/index.d.ts +5 -15
  6. package/build/components/accordion/index.js +28 -20
  7. package/build/components/counter/index.d.ts +2 -2
  8. package/build/components/counter/index.js +10 -10
  9. package/build/components/ellipsis/index.d.ts +1 -1
  10. package/build/components/field/checkbox.d.ts +9 -339
  11. package/build/components/field/checkbox.js +24 -21
  12. package/build/components/field/description.d.ts +3 -3
  13. package/build/components/field/error.d.ts +3 -2
  14. package/build/components/field/index.d.ts +1912 -0
  15. package/build/components/field/index.js +9 -0
  16. package/build/components/field/input.d.ts +18 -348
  17. package/build/components/field/input.js +33 -23
  18. package/build/components/field/select.d.ts +119 -226
  19. package/build/components/field/select.js +81 -69
  20. package/build/components/field/title.d.ts +114 -107
  21. package/build/components/field/title.js +15 -8
  22. package/build/components/form/action.d.ts +21 -6
  23. package/build/components/form/action.js +20 -9
  24. package/build/components/form/index.d.ts +130 -2
  25. package/build/components/frame/index.d.ts +3 -112
  26. package/build/components/frame/index.js +4 -6
  27. package/build/components/highlight/index.d.ts +5 -7
  28. package/build/components/highlight/index.js +23 -15
  29. package/build/components/icon/index.d.ts +3 -3
  30. package/build/components/index.d.ts +2 -2
  31. package/build/components/index.js +2 -2
  32. package/build/components/loader/index.d.ts +3 -3
  33. package/build/components/loader/index.js +13 -16
  34. package/build/components/loading/index.d.ts +1 -1
  35. package/build/components/overlay/index.d.ts +3 -112
  36. package/build/components/overlay/index.js +4 -6
  37. package/build/components/page/index.d.ts +0 -12
  38. package/build/components/page/index.js +0 -10
  39. package/build/components/root/onclick.js +4 -6
  40. package/build/components/scrollbar/index.d.ts +8 -112
  41. package/build/components/scrollbar/index.js +25 -20
  42. package/build/components/sidebar/index.d.ts +3 -112
  43. package/build/components/sidebar/index.js +4 -6
  44. package/build/components/site/index.d.ts +3 -112
  45. package/build/components/site/index.js +5 -8
  46. package/build/components/template/index.d.ts +8 -4
  47. package/build/components/template/index.js +5 -4
  48. package/build/components/tooltip/index.d.ts +235 -332
  49. package/build/components/tooltip/index.js +3 -106
  50. package/build/components/tooltip/menu.d.ts +20 -0
  51. package/build/components/tooltip/menu.js +28 -0
  52. package/build/components/tooltip/onclick.d.ts +124 -0
  53. package/build/components/tooltip/onclick.js +67 -0
  54. package/build/components/tooltip/onhover.d.ts +7 -0
  55. package/build/components/tooltip/onhover.js +25 -0
  56. package/build/components/typewriter/index.d.ts +4 -6
  57. package/build/components/typewriter/index.js +34 -33
  58. package/package.json +10 -9
  59. package/src/components/accordion/index.ts +41 -26
  60. package/src/components/counter/index.ts +15 -13
  61. package/src/components/field/checkbox.ts +31 -30
  62. package/src/components/field/description.ts +2 -2
  63. package/src/components/field/error.ts +2 -2
  64. package/src/components/field/index.ts +8 -3
  65. package/src/components/field/input.ts +41 -33
  66. package/src/components/field/select.ts +123 -108
  67. package/src/components/field/title.ts +18 -8
  68. package/src/components/form/action.ts +75 -47
  69. package/src/components/frame/index.ts +4 -9
  70. package/src/components/highlight/index.ts +53 -39
  71. package/src/components/icon/index.ts +3 -3
  72. package/src/components/index.ts +2 -2
  73. package/src/components/loader/index.ts +15 -17
  74. package/src/components/overlay/index.ts +4 -9
  75. package/src/components/page/index.ts +1 -17
  76. package/src/components/root/onclick.ts +6 -7
  77. package/src/components/scrollbar/index.ts +34 -23
  78. package/src/components/sidebar/index.ts +4 -9
  79. package/src/components/site/index.ts +5 -13
  80. package/src/components/template/index.ts +18 -10
  81. package/src/components/tooltip/index.ts +4 -156
  82. package/src/components/tooltip/menu.ts +52 -0
  83. package/src/components/tooltip/onclick.ts +97 -0
  84. package/src/components/tooltip/onhover.ts +35 -0
  85. package/src/components/typewriter/index.ts +44 -41
  86. package/build/components/footer/index.d.ts +0 -22
  87. package/build/components/footer/index.js +0 -61
  88. package/build/components/footer/scss/index.scss +0 -2
  89. package/build/components/form/types.d.ts +0 -10
  90. package/build/components/form/types.js +0 -1
  91. package/src/components/footer/index.ts +0 -74
  92. package/src/components/footer/scss/index.scss +0 -26
  93. package/src/components/form/types.ts +0 -14
@@ -4,14 +4,24 @@ import tooltip from '~/components/tooltip';
4
4
  import template from '~/components/template';
5
5
 
6
6
 
7
+ const OMIT = ['required'];
8
+
9
+
7
10
  export default template.factory<Attributes & { required?: boolean }>(
8
- (attributes, content) => html`
9
- <div class='field-title --flex-horizontal-space-between --flex-vertical' ${omit(attributes, ['required'])}>
10
- ${content}
11
+ (attributes, content) =>{
12
+ let required = attributes?.required;
13
+
14
+ return html`
15
+ <div
16
+ class='field-title --flex-horizontal-space-between --flex-vertical'
17
+ ${omit(attributes, OMIT)}
18
+ >
19
+ ${content}
11
20
 
12
- ${attributes.required && tooltip.onhover({ class: 'bubble --background-primary --margin-left' }, html`
13
- <span class='tooltip-message tooltip-message--w'>Required</span>
14
- `)}
15
- </div>
16
- `
21
+ ${required && tooltip.onhover({ class: 'bubble --background-primary --margin-left' }, html`
22
+ <span class='tooltip-message tooltip-message--w'>Required</span>
23
+ `)}
24
+ </div>
25
+ `;
26
+ }
17
27
  );
@@ -1,8 +1,25 @@
1
- import response from '@esportsplus/action';
2
- import { Action } from './types';
1
+ import response, { Response } from '@esportsplus/action';
2
+ import { html, Attributes, Element } from '@esportsplus/template';
3
+ import { omit } from '@esportsplus/utilities';
4
+ import template from '~/components/template';
3
5
  import input from './input';
4
6
 
5
7
 
8
+ type A = Attributes & { action: Action, state?: { processing: boolean } };
9
+
10
+ type Action = (data: Payload) => Promise<Errors> | Errors;
11
+
12
+ type Errors = { errors: Response<unknown>['errors'] };
13
+
14
+ type Payload = {
15
+ input: Record<string, any>;
16
+ response: typeof response;
17
+ };
18
+
19
+
20
+ const OMIT = ['action', 'state'];
21
+
22
+
6
23
  function parse(input: ReturnType<FormData['entries']>) {
7
24
  let data: Record<string, any> = {};
8
25
 
@@ -33,48 +50,59 @@ function parse(input: ReturnType<FormData['entries']>) {
33
50
  };
34
51
 
35
52
 
36
- export default function(action: Action, s?: { processing: boolean }) {
37
- return {
38
- onclick: function(this: HTMLFormElement, event: Event) {
39
- let trigger = event.target as HTMLButtonElement;
40
-
41
- if (trigger?.type !== 'submit') {
42
- return;
43
- }
44
-
45
- // On initial page load both events will be dispatched without preventDefault
46
- event.preventDefault();
47
-
48
- this.dispatchEvent(
49
- new SubmitEvent('submit', { cancelable: true, bubbles:true, submitter: trigger })
50
- );
51
- },
52
- onsubmit: async function(this: HTMLFormElement, event: SubmitEvent) {
53
- event.preventDefault();
54
-
55
- if (s) {
56
- s.processing = true;
57
- }
58
-
59
- let { errors } = await action({
60
- input: parse( new FormData( this ).entries() ),
61
- response
62
- });
63
-
64
- for (let i = 0, n = errors.length; i < n; i++) {
65
- let { message, path } = errors[i],
66
- reactive = input.get( this[path!] );
67
-
68
- if (!reactive) {
69
- continue;
70
- }
71
-
72
- reactive.error = `${message[0].toUpperCase()}${message.substring(1)}`;
73
- }
74
-
75
- if (s) {
76
- s.processing = false;
77
- }
78
- }
79
- };
80
- };
53
+ export default template.factory<A>(
54
+ (attributes, content) => {
55
+ let { action, state } = attributes;
56
+
57
+ return html`
58
+ <form
59
+ ${omit(attributes, OMIT)}
60
+ ${{
61
+ onclick: function(event) {
62
+ let trigger = event.target as HTMLButtonElement;
63
+
64
+ if (trigger?.type !== 'submit') {
65
+ return;
66
+ }
67
+
68
+ // On initial page load both events will be dispatched without preventDefault
69
+ event.preventDefault();
70
+
71
+ this.dispatchEvent(
72
+ new SubmitEvent('submit', { cancelable: true, bubbles:true, submitter: trigger })
73
+ );
74
+ },
75
+ onsubmit: async function(event) {
76
+ event.preventDefault();
77
+
78
+ if (state) {
79
+ state.processing = true;
80
+ }
81
+
82
+ let { errors } = await action({
83
+ input: parse( new FormData( this as any as HTMLFormElement ).entries() ),
84
+ response
85
+ });
86
+
87
+ for (let i = 0, n = errors.length; i < n; i++) {
88
+ let { message, path } = errors[i],
89
+ reactive = input.get( (this as any as HTMLFormElement)[path!] as Element | undefined );
90
+
91
+ if (!reactive) {
92
+ continue;
93
+ }
94
+
95
+ reactive.error = `${message[0].toUpperCase()}${message.substring(1)}`;
96
+ }
97
+
98
+ if (state) {
99
+ state.processing = false;
100
+ }
101
+ }
102
+ }}
103
+ >
104
+ ${content}
105
+ </form>
106
+ `;
107
+ }
108
+ );
@@ -1,14 +1,9 @@
1
- import { toArray } from '@esportsplus/utilities';
2
1
  import scrollbar from '~/components/scrollbar';
3
- import template from '~/components/template';
4
2
  import './scss/index.scss';
5
3
 
6
4
 
7
- export default template.factory<Parameters<typeof scrollbar>[0]>(
8
- (attributes, content) => {
9
- attributes.class = toArray(attributes.class);
10
- attributes.class.push('frame');
11
-
12
- return scrollbar(attributes, content);
5
+ export default scrollbar.bind({
6
+ attributes: {
7
+ class: 'frame'
13
8
  }
14
- );
9
+ });
@@ -1,53 +1,67 @@
1
1
  import { reactive } from '@esportsplus/reactivity'
2
- import { Element } from '@esportsplus/template';
2
+ import { html, Attributes } from '@esportsplus/template';
3
+ import { omit } from '@esportsplus/utilities';
4
+ import template from '~/components/template';
3
5
  import './scss/index.scss';
4
6
 
5
7
 
8
+ type A = Attributes & { background?: string };
9
+
10
+
11
+ const OMIT = ['background'];
12
+
13
+
6
14
  let key = Symbol(),
7
15
  observer: IntersectionObserver | null = null;
8
16
 
9
17
 
10
- export default (background: string) => {
11
- if (observer === null) {
12
- observer = new IntersectionObserver((entries) => {
13
- let disconnected = 0,
14
- n = entries.length;
18
+ export default template.factory(
19
+ (attributes: A, content) => {
20
+ if (observer === null) {
21
+ observer = new IntersectionObserver((entries) => {
22
+ let disconnected = 0,
23
+ n = entries.length;
15
24
 
16
- for (let i = 0; i < n; i++) {
17
- let { isIntersecting, target } = entries[i];
25
+ for (let i = 0; i < n; i++) {
26
+ let { isIntersecting, target } = entries[i];
18
27
 
19
- if (target.isConnected) {
20
- // @ts-ignore
21
- target[key].highlight = +isIntersecting;
28
+ if (target.isConnected) {
29
+ // @ts-ignore
30
+ target[key].highlight = +isIntersecting;
31
+ }
32
+ else {
33
+ disconnected++;
34
+ observer!.unobserve(target);
35
+ }
22
36
  }
23
- else {
24
- disconnected++;
25
- observer!.unobserve(target);
37
+
38
+ if (n - disconnected === 0) {
39
+ observer!.disconnect();
40
+ observer = null;
26
41
  }
27
- }
42
+ }, { threshold: 1 });
43
+ }
28
44
 
29
- if (n - disconnected === 0) {
30
- observer!.disconnect();
31
- observer = null;
32
- }
33
- }, { threshold: 1 });
34
- }
45
+ let state = reactive({
46
+ highlight: 0
47
+ });
35
48
 
36
- let state = reactive({
37
- highlight: 0
38
- });
39
-
40
- return {
41
- attributes: {
42
- class: 'highlight',
43
- onrender: function(element: Element) {
44
- element[key] = state;
45
- observer!.observe(element);
46
- },
47
- style: [
48
- () => `--highlight: ${state.highlight}`,
49
- `--background: ${background}`,
50
- ]
51
- }
52
- };
53
- }
49
+ return html`
50
+ <div class='highlight'
51
+ ${omit(attributes, OMIT)}
52
+ ${{
53
+ onrender: (element) => {
54
+ element[key] = state;
55
+ observer!.observe(element);
56
+ },
57
+ style: [
58
+ () => `--highlight: ${state.highlight}`,
59
+ `--background: ${attributes.background}`,
60
+ ]
61
+ }}
62
+ >
63
+ ${content}
64
+ </div>
65
+ `;
66
+ }
67
+ );
@@ -1,10 +1,10 @@
1
- import { html, Attributes, svg } from '@esportsplus/template';
1
+ import { html, svg } from '@esportsplus/template';
2
2
  import template from '~/components/template';
3
3
  import './scss/index.scss';
4
4
 
5
5
 
6
- export default template.factory<Attributes, Parameters<typeof svg.sprite>[0]>(
7
- (attributes, icon) => {
6
+ export default template.factory(
7
+ (attributes, icon: Parameters<typeof svg.sprite>[0]) => {
8
8
  return html`
9
9
  <div class='icon' ${attributes}>
10
10
  ${svg.sprite(icon)}
@@ -10,7 +10,6 @@ export * as container from './container';
10
10
  export { default as counter } from './counter';
11
11
  export { default as ellipsis } from './ellipsis';
12
12
  export { default as field } from './field';
13
- export { default as footer } from './footer';
14
13
  export { default as form } from './form';
15
14
  export * as frame from './frame';
16
15
  export * as grid from './grid';
@@ -24,12 +23,13 @@ export { default as loading } from './loading';
24
23
  export * as modal from './modal';
25
24
  export { default as number } from './number';
26
25
  export { default as overlay } from './overlay';
27
- export { default as page } from './page';
26
+ export * from './page';
28
27
  export { default as root } from './root';
29
28
  export * as row from './row';
30
29
  export { default as scrollbar } from './scrollbar';
31
30
  export { default as sidebar } from './sidebar';
32
31
  export { default as site } from './site';
32
+ export { default as template } from './template';
33
33
  export * as text from './text';
34
34
  export * as thumbnail from './thumbnail';
35
35
  export { default as tooltip } from './tooltip';
@@ -1,12 +1,13 @@
1
- import { html, Attributes } from '@esportsplus/template';
2
1
  import { reactive } from '@esportsplus/reactivity';
2
+ import { html } from '@esportsplus/template';
3
3
  import template from '~/components/template';
4
4
  import './scss/index.scss';
5
5
 
6
6
 
7
- export default template.factory<Attributes>(
7
+ export default template.factory(
8
8
  (attributes, content) => {
9
- let state = reactive({
9
+ let c = () => state.load && 'loader--load',
10
+ state = reactive({
10
11
  load: false,
11
12
  scale: false
12
13
  });
@@ -16,24 +17,21 @@ export default template.factory<Attributes>(
16
17
  }, 300);
17
18
 
18
19
  return html`
19
- <div class='loader ${() => state.load && 'loader--load'}'>
20
- <div class='loader ${() => state.load && 'loader--load'}'>
20
+ <div class='loader' ${{ class: c }}>
21
+ <div class='loader' ${{ class: c }}>
21
22
  <div class='loader-content'>
22
23
  <div
23
- class='
24
- ${() => state.scale && 'loader-logo--scale'}
25
- loader-logo
26
- text
27
- --flex-center
28
- --text-uppercase --text-600
29
- '
24
+ class='loader-logo text --flex-center --text-uppercase --text-600'
30
25
  style='color: var(--color-grey-500);'
31
- onanimationend='${({ animationName: name }: AnimationEvent) => {
32
- if (name === 'scale') {
33
- state.load = true;
34
- }
35
- }}'
36
26
  ${attributes}
27
+ ${{
28
+ class: () => state.scale && 'loader-logo--scale',
29
+ onanimationend: ({ animationName: name }) => {
30
+ if (name === 'scale') {
31
+ state.load = true;
32
+ }
33
+ }
34
+ }}
37
35
  >
38
36
  ${content}
39
37
  </div>
@@ -1,14 +1,9 @@
1
- import { toArray } from '@esportsplus/utilities';
2
1
  import scrollbar from '~/components/scrollbar';
3
- import template from '~/components/template';
4
2
  import './scss/index.scss';
5
3
 
6
4
 
7
- export default template.factory<Parameters<typeof scrollbar>[0]>(
8
- (attributes, content) => {
9
- attributes.class = toArray(attributes.class);
10
- attributes.class.push('overlay');
11
-
12
- return scrollbar(attributes, content);
5
+ export default scrollbar.bind({
6
+ attributes: {
7
+ class: 'overlay'
13
8
  }
14
- );
9
+ });
@@ -1,17 +1 @@
1
- import './scss/index.scss';
2
-
3
-
4
- const subtitle = {
5
- class: 'page-subtitle --margin-200 --text-crop-bottom'
6
- };
7
-
8
- const suptitle = {
9
- class: 'page-suptitle --text-bold-600 --text-crop --text-uppercase --text-300'
10
- };
11
-
12
- const title = {
13
- class: 'page-title --line-height-200 --margin-400 --text-crop'
14
- };
15
-
16
-
17
- export default { subtitle, suptitle, title };
1
+ import './scss/index.scss';
@@ -1,20 +1,19 @@
1
- let queue: (VoidFunction | (() => Promise<void>))[] = [];
1
+ import factory from '@esportsplus/queue';
2
2
 
3
3
 
4
- const onclick = async () => {
5
- if (queue.length === 0) {
6
- return;
7
- }
4
+ let queue = factory<VoidFunction | (() => Promise<void>)>(64);
8
5
 
6
+
7
+ const onclick = async () => {
9
8
  let item;
10
9
 
11
- while (item = queue.pop()) {
10
+ while (item = queue.next()) {
12
11
  await item();
13
12
  }
14
13
  };
15
14
 
16
15
  onclick.push = (fn: VoidFunction) => {
17
- queue.push(fn);
16
+ queue.add(fn);
18
17
  };
19
18
 
20
19
 
@@ -5,12 +5,18 @@ import template from '~/components/template';
5
5
  import './scss/index.scss';
6
6
 
7
7
 
8
+ type A = Attributes & { scrollbar?: Attributes, 'scrollbar-container-content'?: Attributes };
9
+
10
+
11
+ const OMIT = ['scrollbar', 'scrollbar-container-content'];
12
+
13
+
8
14
  let root = document.body,
9
15
  width: number | undefined;
10
16
 
11
17
 
12
- export default template.factory<Attributes & { scrollbar?: Attributes, 'scrollbar-container-content'?: Attributes }>(
13
- (attributes, content) => {
18
+ export default template.factory<A>(
19
+ function(attributes, content) {
14
20
  let state = reactive({
15
21
  height: 100,
16
22
  translate: 0
@@ -19,40 +25,45 @@ export default template.factory<Attributes & { scrollbar?: Attributes, 'scrollba
19
25
  return html`
20
26
  <div
21
27
  class='scrollbar-container'
22
- ${omit(attributes, ['scrollbar-container-content', 'scrollbar'])}
28
+ ${omit(attributes, OMIT)}
29
+ ${this.attributes && omit(this.attributes, OMIT)}
23
30
  >
24
31
  <div
25
32
  class='scrollbar-container-content'
26
- onscroll='${function(this: HTMLElement) {
27
- if (width === undefined) {
28
- width = this.offsetWidth - this.clientWidth;
33
+ ${attributes['scrollbar-container-content']}
34
+ ${this.attributes?.['scrollbar-container-content']}
35
+ ${{
36
+ onscroll: function() {
37
+ if (width === undefined) {
38
+ width = this.offsetWidth - this.clientWidth;
29
39
 
30
- if (width && width !== 17) {
31
- root.style.setProperty('--scrollbar-width', `${width}px`);
40
+ if (width && width !== 17) {
41
+ root.style.setProperty('--scrollbar-width', `${width}px`);
42
+ }
32
43
  }
33
- }
34
44
 
35
- state.height = (this.clientHeight / this.scrollHeight) * 100;
36
- state.translate = (this.scrollTop / this.clientHeight) * 100;
37
- }}'
38
- ${attributes['scrollbar-container-content']}
45
+ state.height = (this.clientHeight / this.scrollHeight) * 100;
46
+ state.translate = (this.scrollTop / this.clientHeight) * 100;
47
+ }
48
+ }}
39
49
  >
40
50
  ${content}
41
51
  </div>
42
52
 
43
53
  <div
44
- class='
45
- ${() => state.height >= 100 && 'scrollbar--hidden'}
46
- scrollbar
47
- '
48
- style='${() => `
49
- --translate: translate3d(0, ${state.translate}%, 0);
50
- --height: ${state.height}%;
51
- `}'
52
- ${attributes.scrollbar}
54
+ class='scrollbar'
55
+ ${this.attributes?.scrollbar}
56
+ ${{
57
+ class: () => state.height >= 100 && 'scrollbar--hidden',
58
+ style: () => `
59
+ --translate: translate3d(0, ${state.translate}%, 0);
60
+ --height: ${state.height}%;
61
+ `
62
+ }}
53
63
  >
54
64
  </div>
55
65
  </div>
56
66
  `;
57
67
  }
58
- );
68
+ );
69
+ export type { A as Attributes }
@@ -1,14 +1,9 @@
1
- import { toArray } from '@esportsplus/utilities';
2
1
  import scrollbar from '~/components/scrollbar';
3
- import template from '~/components/template';
4
2
  import './scss/index.scss';
5
3
 
6
4
 
7
- export default template.factory<Parameters<typeof scrollbar>[0]>(
8
- (attributes, content) => {
9
- attributes.class = toArray(attributes.class);
10
- attributes.class.push('sidebar');
11
-
12
- return scrollbar(attributes, content);
5
+ export default scrollbar.bind({
6
+ attributes: {
7
+ class: 'sidebar'
13
8
  }
14
- );
9
+ });
@@ -1,19 +1,11 @@
1
- import { toArray } from '@esportsplus/utilities';
2
1
  import { onclick } from '~/components/root';
3
2
  import scrollbar from '~/components/scrollbar';
4
- import template from '~/components/template';
5
3
  import './scss/index.scss';
6
4
 
7
5
 
8
- export default template.factory<Parameters<typeof scrollbar>[0]>(
9
- (attributes, content) => {
10
- attributes.class = toArray(attributes.class);
11
- attributes.class.push('site');
12
-
13
- attributes.onclick = onclick;
14
-
15
- attributes.style ??= '--background-default: var(--color-black-400);';
16
-
17
- return scrollbar(attributes, content);
6
+ export default scrollbar.bind({
7
+ attributes: {
8
+ class: 'site',
9
+ onclick
18
10
  }
19
- );
11
+ });
@@ -1,25 +1,33 @@
1
- import { Renderable } from '@esportsplus/template';
1
+ import { Attributes, Renderable } from '@esportsplus/template';
2
+ import { EMPTY_OBJECT } from '@esportsplus/utilities';
2
3
 
3
4
 
4
- const factory = <A extends Record<string, unknown>, C = unknown>(template: (attributes: A, content: C) => Renderable) => {
5
- function factory(content: C): Renderable;
6
- function factory(attributes: A, content: C): Renderable;
7
- function factory(one: A | C, two?: C): Renderable {
8
- let content: C,
9
- attributes: A = {} as A;
5
+ const factory = <
6
+ A extends Attributes,
7
+ C = Renderable<unknown>,
8
+ Context = { attributes?: A, content?: C }
9
+ >(
10
+ template: (this: Context, attributes: Readonly<A>, content: C) => Renderable<unknown>
11
+ ) => {
12
+ function factory(): Renderable<unknown>;
13
+ function factory(content: C): Renderable<unknown>;
14
+ function factory(attributes: A, content: C): Renderable<unknown>;
15
+ function factory(this: Context, one?: A | C, two?: C): Renderable<unknown> {
16
+ let attributes: A = {} as A,
17
+ content: C;
10
18
 
11
19
  if (two === undefined) {
12
20
  content = one as C;
13
21
  }
14
22
  else {
15
- content = two;
16
23
  attributes = one as A;
24
+ content = two;
17
25
  }
18
26
 
19
- return template(attributes, content);
27
+ return template.call(this, attributes, content);
20
28
  }
21
29
 
22
- return factory;
30
+ return factory.bind(EMPTY_OBJECT);
23
31
  };
24
32
 
25
33