@kws3/ui 4.1.3 → 4.2.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.
package/CHANGELOG.mdx CHANGED
@@ -1,92 +1,105 @@
1
- # 4.1.3
1
+ ## 4.2.0
2
+ - Fix grid row transitions to be global
3
+ - Add `createScaler` and `scale` functions to utils
4
+ - `GridView` is no longer wrapped in `div.data-table`
5
+ - New component `StickyColumnsTableWrapper` added
6
+ - Use dynamic scroll shadows for `InfiniteList` component
7
+
8
+ ## 4.1.4
9
+ - add `black` and `white` as valid color types
10
+
11
+ ## 4.1.3
2
12
  - Ensure transitions are global for `Modal`, `CardModal` and `ActionSheet` components
3
13
  - `InfiniteList` - do not fake a scroll event on a resize event, fixes loading bugs
4
14
 
5
- # 4.1.2
15
+ ## 4.1.2
6
16
  - `MultiSelect` - fixed issue where raw object were initially outputted (if provided)
7
17
  - `SearchableSelect and `MultiSelect` (Async): resolved issue with the 'active' class not attaching to the most matched item in dropdown options
8
18
  - `SearchableSelect` - improved behavior, ensuring the `tab` key correctly loses focus
9
19
  - `AutoComplete` - dropdown now closes on loss of focus triggered by the `tab` key
10
20
 
11
- # 4.1.1
21
+ ## 4.1.1
12
22
  - `DatePicker` - add support for configurable border radius
13
23
  - `DatePicker` - add support for configurable box shadow
14
24
  - `DatePicker` - bug fix on date range selection colors
15
25
 
16
- # 4.1.0
26
+ ## 4.1.0
17
27
  - New component `InfiniteList` Added
18
28
 
19
- # 4.0.3
29
+ ## 4.0.3
20
30
  - `ScrollableList` - bugfixes, and add support for removing items without affecting scroll position
21
31
 
22
- # 4.0.2
32
+ ## 4.0.2
23
33
  - `ToggleButtons` - add support for count tags, and fix css not applying
24
34
 
25
- # 4.0.1
35
+ ## 4.0.1
26
36
  - `ScrollableList` - add support for custom iteration key for keyed each block
27
37
 
28
- # 4.0.0
38
+ ## 4.0.0
29
39
  - add svelte 4 compatibility
30
40
 
31
41
  --------------
32
- # 2.3.3
42
+ ## 2.3.4
43
+ - add `black` and `white` as valid color types
44
+
45
+ ## 2.3.3
33
46
  - `InfiniteList` - do not fake a scroll event on a resize event, fixes loading bugs
34
47
 
35
- # 2.3.2
48
+ ## 2.3.2
36
49
  - `MultiSelect` - fixed issue where raw object were initially outputted (if provided)
37
50
  - `SearchableSelect and `MultiSelect` (Async): resolved issue with the 'active' class not attaching to the most matched item in dropdown options
38
51
  - `SearchableSelect` - improved behavior, ensuring the `tab` key correctly loses focus
39
52
  - `AutoComplete` - dropdown now closes on loss of focus triggered by the `tab` key
40
53
 
41
54
 
42
- # 2.3.1
55
+ ## 2.3.1
43
56
  - `DatePicker` - add support for configurable border radius
44
57
  - `DatePicker` - add support for configurable box shadow
45
58
  - `DatePicker` - bug fix on date range selection colors
46
59
 
47
- # 2.3.0
60
+ ## 2.3.0
48
61
  - New component `InfiniteList` Added
49
62
 
50
- # 2.2.5
63
+ ## 2.2.5
51
64
  - `ScrollableList` - bugfixes, and add support for removing items without affecting scroll position
52
65
 
53
- # 2.2.4
66
+ ## 2.2.4
54
67
  - `ToggleButtons` - add support for count tags, and fix css not applying
55
68
 
56
- # 2.2.3
69
+ ## 2.2.3
57
70
  - `ScrollableList` - add support for custom iteration key for keyed each block
58
71
 
59
- # 2.2.2
72
+ ## 2.2.2
60
73
  - `ScrollableList` - fix jumping issue when edge of scrollable regions are reached
61
74
 
62
- # 2.2.1
75
+ ## 2.2.1
63
76
  - `Icon` fix icon sizes when line-awesome icons are loaded
64
77
  - `Icon` fix material icons when icon family is set globally
65
78
  - `Icon` add support for adding custom icon families
66
79
  - `Chart` fix, ensure ApexCharts is loaded before trying to initialise it
67
80
 
68
- # 2.2.0
81
+ ## 2.2.0
69
82
  - Fix bug where `ScrollableList` component puts extra padding on top of list when fast scroll to top
70
83
  - `ScrollableList` - Fix bug to reset internal positioning props when data changes
71
84
  - fix Popperjs position strategy to work better on mobile by default, affects `SearchableSelect`, `MultiSelect` and `AutoComplete` components
72
85
  - `ActionSheet` - Update styling to make it wokr better visually on desktop
73
86
 
74
- # 2.1.4
87
+ ## 2.1.4
75
88
  - Fix bug where disabled `Checkbox` components wrongly appeared as if they were checked
76
89
  - Allow adding CSS classes to the input field in `NumberInput` component
77
90
 
78
- # 2.1.3
91
+ ## 2.1.3
79
92
  - `PasswordInput` - add support for field `autocomplete` and `required`
80
93
  - `PasswordValidator` - fix `valid` field binding
81
94
 
82
- # 2.1.2
95
+ ## 2.1.2
83
96
  - `PasswordInput` - add support for field `name`
84
97
  - `PasswordInput` - fix spacing when icons are turned off
85
98
 
86
- # 2.1.1
99
+ ## 2.1.1
87
100
  - Package `text-mask-core` moved to @kws3 namespace.
88
101
 
89
- # 2.1.0
102
+ ## 2.1.0
90
103
  - Bugfix with Colorpicker component, where colors could not be typed in manually.
91
104
  - Extended typing and typescript support
92
105
  - New Components added `Canvas`, `ESignature`, `PenInput` and `PenControls`
@@ -16,7 +16,7 @@ This is to prevent unnecessary event subscriptions., Default: `[]`
16
16
  <div class="kws-chart {klass}" bind:this={canvas} />
17
17
 
18
18
  <script>
19
- import { createEventDispatcher, onMount } from "svelte";
19
+ import { createEventDispatcher, onMount, onDestroy } from "svelte";
20
20
  import { merge } from "./utils";
21
21
 
22
22
  const fire = createEventDispatcher();
@@ -88,12 +88,11 @@ This is to prevent unnecessary event subscriptions., Default: `[]`
88
88
  const module = await import("apexcharts/dist/apexcharts.esm");
89
89
  ApexCharts = module.default;
90
90
  window.ApexCharts = ApexCharts;
91
-
92
91
  init();
92
+ });
93
93
 
94
- return () => {
95
- chart && chart.destroy();
96
- };
94
+ onDestroy(() => {
95
+ if (chart) chart.destroy();
97
96
  });
98
97
 
99
98
  let Events;
@@ -11,7 +11,7 @@
11
11
  -->
12
12
 
13
13
  <td class={classNames(column, row)} style={styles(column, row)}>
14
- <!-- eslint-disable-next-line @ota-meshi/svelte/no-at-html-tags -->
14
+ <!-- eslint-disable-next-line svelte/no-at-html-tags -->
15
15
  {@html transforms(column, row)}
16
16
  </td>
17
17
 
@@ -28,7 +28,7 @@
28
28
  -->
29
29
  {#if transition}
30
30
  <tr
31
- in:fly={{ x: 20, delay: 25 * row_index }}
31
+ in:fly|global={{ x: 20, delay: 25 * row_index }}
32
32
  on:click|stopPropagation={rowClick}
33
33
  class:is-selected={activated && visualActivationOnClick}
34
34
  class:is-checked={checked}>
@@ -32,59 +32,60 @@
32
32
  - `_forwardEvent`
33
33
 
34
34
  -->
35
- <div class="data-table" data-cy="data-table">
36
- <table
37
- class="table is-fullwidth {is_striped ? 'is-striped' : ''} {is_narrow
38
- ? 'is-narrow'
39
- : ''} {clickableRows ? 'is-hoverable' : ''} is-bordered {klass}">
40
- <thead>
41
- <tr>
42
- {#if bulk_actions}
43
- <th width="30">
44
- <Checkbox
45
- size="medium"
46
- color={selectAllCheckboxColor}
47
- style="margin:0;"
48
- inverted={true}
49
- bind:checked={selectAll}
50
- on:change={changed} />
51
- </th>
35
+
36
+ <table
37
+ class="table kws-grid-view is-fullwidth {is_striped
38
+ ? 'is-striped'
39
+ : ''} {is_narrow ? 'is-narrow' : ''} {clickableRows
40
+ ? 'is-hoverable'
41
+ : ''} is-bordered {klass}">
42
+ <thead>
43
+ <tr>
44
+ {#if bulk_actions}
45
+ <th style="width:30px">
46
+ <Checkbox
47
+ size="medium"
48
+ color={selectAllCheckboxColor}
49
+ style="margin:0;"
50
+ inverted={true}
51
+ bind:checked={selectAll}
52
+ on:change={changed} />
53
+ </th>
54
+ {/if}
55
+ {#each column_keys as column}
56
+ {#if isVisible(column)}
57
+ <th>{columns[column]}</th>
52
58
  {/if}
53
- {#each column_keys as column}
54
- {#if isVisible(column)}
55
- <th>{columns[column]}</th>
56
- {/if}
57
- {/each}
58
- </tr>
59
- </thead>
60
- <tbody data-cy="grid-view-tbody">
61
- {#each data as row, row_index (row[iteration_key])}
62
- <svelte:component
63
- this={mainRowComponent}
64
- on:rowClick
65
- on:rowSelectChecked
66
- on:_forwardEvent
67
- {row_index}
68
- {transition}
69
- {column_keys}
70
- {clickableRows}
71
- {visualActivationOnClick}
72
- {isVisible}
73
- {transforms}
74
- {classNames}
75
- {styles}
76
- {cellComponent}
77
- {row}
78
- {bulk_actions}
79
- {activatedId}
80
- {selectedIds}
81
- {selectCheckboxColor}
82
- {selectCheckboxSize}
83
- checked={selectAll} />
84
59
  {/each}
85
- </tbody>
86
- </table>
87
- </div>
60
+ </tr>
61
+ </thead>
62
+ <tbody data-cy="grid-view-tbody">
63
+ {#each data as row, row_index (row[iteration_key])}
64
+ <svelte:component
65
+ this={mainRowComponent}
66
+ on:rowClick
67
+ on:rowSelectChecked
68
+ on:_forwardEvent
69
+ {row_index}
70
+ {transition}
71
+ {column_keys}
72
+ {clickableRows}
73
+ {visualActivationOnClick}
74
+ {isVisible}
75
+ {transforms}
76
+ {classNames}
77
+ {styles}
78
+ {cellComponent}
79
+ {row}
80
+ {bulk_actions}
81
+ {activatedId}
82
+ {selectedIds}
83
+ {selectCheckboxColor}
84
+ {selectCheckboxSize}
85
+ checked={selectAll} />
86
+ {/each}
87
+ </tbody>
88
+ </table>
88
89
 
89
90
  <script>
90
91
  import GridRow from "./GridRow.svelte";
@@ -0,0 +1,107 @@
1
+ <!--
2
+ @component
3
+
4
+
5
+ @param {string} [outer_class=""] - CSS classes for the outer container, Default: `""`
6
+ @param {string} [inner_class=""] - CSS classes for the inner container, Default: `""`
7
+ @param {string} [width="100%"] - CSS value string to determine the width of the inner table, Default: `"100%"`
8
+ @method `relayout()` - Method to recalculate layouts.
9
+ Call this method when the content of the <slot> changes
10
+
11
+ ### Slots
12
+ - `<slot name="default" />`
13
+
14
+ -->
15
+ <svelte:window on:resize={debouncedResize} />
16
+ <div class="kws-sticky-columns-table-wrapper data-table-wrapper {outer_class}">
17
+ <div class="data-list-inner-wrapper {inner_class}">
18
+ <div
19
+ class="data-table has-custom-scrollbar"
20
+ bind:this={scrollContainer}
21
+ on:scroll={scrollHandler}>
22
+ <slot />
23
+ </div>
24
+ </div>
25
+ <div
26
+ class="fake-shadow-contraint left"
27
+ style="left: {leftShadowLeft}px;opacity:{needsScroll
28
+ ? scale(scrollLeft)
29
+ : 0};">
30
+ <div class="fake-inner-shadow" />
31
+ </div>
32
+ <div
33
+ class="fake-shadow-contraint right"
34
+ style="opacity:{needsScroll ? 1 - scale(scrollLeft) : 0};">
35
+ <div class="fake-inner-shadow" />
36
+ </div>
37
+ </div>
38
+
39
+ <script>
40
+ import { debounce, createScaler } from "@kws3/ui/utils";
41
+ import { onMount, tick } from "svelte";
42
+
43
+ /** CSS classes for the outer container*/
44
+ export let outer_class = "";
45
+ /** CSS classes for the inner container*/
46
+ export let inner_class = "";
47
+ /** CSS value string to determine the width of the inner table*/
48
+ export let width = "100%";
49
+
50
+ $: width, applyWidth(width);
51
+
52
+ let scrollContainer;
53
+
54
+ let leftShadowLeft = 0;
55
+ let scrollWidth = 0;
56
+ let offsetWidth = 0;
57
+ let needsScroll = false;
58
+ let scrollLeft = 0;
59
+
60
+ let scale = createScaler([0, 1], [0, 1]);
61
+
62
+ onMount(mounted);
63
+
64
+ function mounted() {
65
+ relayout();
66
+ }
67
+
68
+ /**
69
+ * Method to recalculate layouts.
70
+ * Call this method when the content of the <slot> changes
71
+ */
72
+ export function relayout() {
73
+ tick().then(() => {
74
+ leftShadowLeft = -1;
75
+ needsScroll = false;
76
+ const stickies = scrollContainer.querySelectorAll(
77
+ "thead>tr>th.is-sticky-column",
78
+ );
79
+
80
+ stickies.forEach((th) => {
81
+ leftShadowLeft += th.offsetWidth;
82
+ });
83
+
84
+ scrollWidth = scrollContainer.scrollWidth;
85
+ offsetWidth = scrollContainer.offsetWidth;
86
+
87
+ let diff = scrollWidth - offsetWidth;
88
+ if (scrollWidth > offsetWidth) {
89
+ needsScroll = true;
90
+ }
91
+ scale = createScaler([0, diff], [0, 1]);
92
+ });
93
+ }
94
+
95
+ const debouncedResize = debounce(relayout, 350);
96
+
97
+ function scrollHandler(e) {
98
+ scrollLeft = e.target.scrollLeft;
99
+ }
100
+
101
+ function applyWidth(width) {
102
+ tick().then(() => {
103
+ scrollContainer.querySelector("table.table").style.width = width;
104
+ debouncedResize();
105
+ });
106
+ }
107
+ </script>
@@ -23,7 +23,7 @@
23
23
  {#each column_keys as column}
24
24
  {#if isVisible(column)}
25
25
  <div class={classNames(column, row)} style={styles(column, row)}>
26
- <!-- eslint-disable-next-line @ota-meshi/svelte/no-at-html-tags -->
26
+ <!-- eslint-disable-next-line svelte/no-at-html-tags -->
27
27
  {@html transforms(column, row)}
28
28
  </div>
29
29
  {/if}
package/form/index.d.ts CHANGED
@@ -1,3 +1,4 @@
1
+ /// <reference types="svelte" />
1
2
  export function makeForms(items: any): any[] | {
2
3
  formData: import("svelte/store").Writable<any>;
3
4
  errors: import("svelte/store").Readable<any>;
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["index.js"],"names":[],"mappings":"AAGA;;;;;;;;;;EAWC;AA2ID,0CAA4C;AAE5C,0CAAqC;AAErC,8EAA8E"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["index.js"],"names":[],"mappings":";AAGA;;;;;;;;;;EAWC;AA2ID,0CAA4C;AAE5C,0CAAqC;AAErC,8EAA8E"}
@@ -38,7 +38,7 @@ For internal use only - not part of config object, Default: `""`
38
38
  {/if}
39
39
  <div class="column">
40
40
  <div>
41
- <!-- eslint-disable-next-line @ota-meshi/svelte/no-at-html-tags -->
41
+ <!-- eslint-disable-next-line svelte/no-at-html-tags -->
42
42
  <span class="is-block">{@html _text}</span>
43
43
  {#if _type === "prompt"}
44
44
  <div class="field" style="margin-top:0.5rem;">
@@ -49,7 +49,7 @@
49
49
  <h4 class="title is-5 is-marginless">{title}</h4>
50
50
  {/if}
51
51
 
52
- <!-- eslint-disable-next-line @ota-meshi/svelte/no-at-html-tags -->
52
+ <!-- eslint-disable-next-line svelte/no-at-html-tags -->
53
53
  <p>{@html message}</p>
54
54
 
55
55
  {#if !persistent}
@@ -13,7 +13,7 @@
13
13
  class="kws-floatie-item has-{variant} is-{item.position}"
14
14
  id={item.id}
15
15
  animate:flip={{ easing: sineOut }}
16
- transition:fly={{
16
+ transition:fly|global={{
17
17
  y: transitionDistance(item.position),
18
18
  duration: transitionDuration,
19
19
  easing: sineOut,
@@ -1,3 +1,4 @@
1
+ /// <reference types="svelte" />
1
2
  export namespace FloatiesStore {
2
3
  export { create };
3
4
  export { remove };
@@ -35,7 +36,7 @@ declare const subscribe: (this: void, run: import("svelte/store").Subscriber<{
35
36
  snackbar: any[];
36
37
  toast: any[];
37
38
  };
38
- }>, invalidate?: (value?: {
39
+ }>, invalidate?: import("svelte/store").Invalidator<{
39
40
  top: {
40
41
  notification: any[];
41
42
  snackbar: any[];
@@ -46,6 +47,6 @@ declare const subscribe: (this: void, run: import("svelte/store").Subscriber<{
46
47
  snackbar: any[];
47
48
  toast: any[];
48
49
  };
49
- }) => void) => import("svelte/store").Unsubscriber;
50
+ }>) => import("svelte/store").Unsubscriber;
50
51
  export {};
51
52
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["index.js"],"names":[],"mappings":";;;;;AA4GA;;GAEG;AAEH;;GAEG;AACH,4BAFU,WAAW,CAgBnB;AAEF;;GAEG;AACH,qBAFU,WAAW,CAiBnB;AAEF;;GAEG;AACH,wBAFU,WAAW,CAiBnB;0BA5DW,OAAO,gBAAgB,EAAE,WAAW;AAxD/C;;;EA6BC;AAED,0CAYC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["index.js"],"names":[],"mappings":";;;;;;AA4GA;;GAEG;AAEH;;GAEG;AACH,4BAFU,WAAW,CAgBnB;AAEF;;GAEG;AACH,qBAFU,WAAW,CAiBnB;AAEF;;GAEG;AACH,wBAFU,WAAW,CAiBnB;0BA5DW,OAAO,gBAAgB,EAAE,WAAW;AAxD/C;;;EA6BC;AAED,0CAYC"}
@@ -21,12 +21,14 @@ while more items are loading
21
21
  {#if hasResizeObserver}
22
22
  <div
23
23
  bind:this={viewport}
24
- class="kws-scrollable-list kws-infinite-list with-resize-observer {klass}"
25
- on:scroll={handle_scroll}
24
+ class="kws-infinite-list with-resize-observer {klass}"
26
25
  style="height:{height};{style}"
27
26
  use:resizeObserver
28
27
  on:resize={resize}>
29
- <div bind:this={contents}>
28
+ <div
29
+ class="kws-infinite-list-inner"
30
+ on:scroll={handle_scroll}
31
+ bind:this={contents}>
30
32
  {#each visible as item (item.index)}
31
33
  <div class="row">
32
34
  <!--Default slot for list view items-->
@@ -34,18 +36,30 @@ while more items are loading
34
36
  </div>
35
37
  {/each}
36
38
  <!--Optional slot to display a loading graphic at the bottom of the list
37
- while more items are loading-->
39
+ while more items are loading-->
38
40
  <slot name="loader" />
39
41
  </div>
42
+ <div
43
+ class="fake-shadow-contraint top"
44
+ style="opacity:{needsScroll ? scale(scrollTop) : 0};">
45
+ <div class="fake-inner-shadow" />
46
+ </div>
47
+ <div
48
+ class="fake-shadow-contraint bottom"
49
+ style="opacity:{needsScroll ? 1 - scale(scrollTop) : 0};">
50
+ <div class="fake-inner-shadow" />
51
+ </div>
40
52
  </div>
41
53
  {:else}
42
54
  <div
43
55
  bind:this={viewport}
44
- class="kws-scrollable-list kws-infinite-list {klass}"
45
- on:scroll={handle_scroll}
56
+ class="kws-infinite-list {klass}"
46
57
  style="height:{height};{style}"
47
- bind:offsetHeight={viewport_height}>
48
- <div bind:this={contents}>
58
+ bind:offsetHeight={viewportHeight}>
59
+ <div
60
+ class="kws-infinite-list-inner"
61
+ on:scroll={handle_scroll}
62
+ bind:this={contents}>
49
63
  {#each visible as item (item.index)}
50
64
  <div class="row">
51
65
  <!--Default slot for list view items-->
@@ -53,24 +67,72 @@ while more items are loading
53
67
  </div>
54
68
  {/each}
55
69
  <!--Optional slot to display a loading graphic at the bottom of the list
56
- while more items are loading-->
70
+ while more items are loading-->
57
71
  <slot name="loader" />
58
72
  </div>
73
+ <div
74
+ class="fake-shadow-contraint top"
75
+ style="opacity:{needsScroll ? scale(scrollTop) : 0};">
76
+ <div class="fake-inner-shadow" />
77
+ </div>
78
+ <div
79
+ class="fake-shadow-contraint bottom"
80
+ style="opacity:{needsScroll ? 1 - scale(scrollTop) : 0};">
81
+ <div class="fake-inner-shadow" />
82
+ </div>
59
83
  </div>
60
84
  {/if}
61
85
 
62
86
  <style>
63
87
  .kws-infinite-list {
64
- overflow: auto;
65
- -webkit-overflow-scrolling: touch;
66
88
  position: relative;
67
89
  height: 100%;
68
90
  }
91
+
92
+ .kws-infinite-list-inner {
93
+ width: 100%;
94
+ height: 100%;
95
+ overflow: auto;
96
+ -webkit-overflow-scrolling: touch;
97
+ }
98
+
99
+ .fake-shadow-contraint {
100
+ position: absolute;
101
+ pointer-events: none;
102
+ top: 0;
103
+ left: 0;
104
+ width: 100%;
105
+ height: 20px;
106
+ overflow: hidden;
107
+ }
108
+ .fake-shadow-contraint .fake-inner-shadow {
109
+ position: absolute;
110
+ left: 0;
111
+ top: -1px;
112
+ width: 100%;
113
+ height: 1px;
114
+ box-shadow:
115
+ 0 0 6px 0 #000,
116
+ 0 0 12px 0 #000,
117
+ 0 0 18px 0 #000,
118
+ 0 0 20px 0 #000;
119
+ }
120
+
121
+ .fake-shadow-contraint.bottom {
122
+ top: auto;
123
+ bottom: 0;
124
+ }
125
+
126
+ .fake-shadow-contraint.bottom .fake-inner-shadow {
127
+ top: auto;
128
+ bottom: -1px;
129
+ }
69
130
  </style>
70
131
 
71
132
  <script>
72
133
  import { hasResizeObserver, resizeObserver } from "@kws3/ui/resizeObserver";
73
- import { createEventDispatcher } from "svelte";
134
+ import { createEventDispatcher, tick } from "svelte";
135
+ import { createScaler } from "@kws3/ui/utils";
74
136
 
75
137
  const fire = createEventDispatcher();
76
138
  /**
@@ -103,18 +165,28 @@ while more items are loading
103
165
  // local state
104
166
  let viewport,
105
167
  contents,
106
- viewport_height = 0,
168
+ viewportHeight = 0,
169
+ scrollHeight = 0,
170
+ offsetHeight = 0,
107
171
  visible,
108
172
  fired = false;
109
173
 
174
+ let scale = createScaler([0, 1], [0, 1]);
175
+
176
+ let needsScroll = false,
177
+ scrollTop = 0;
178
+
110
179
  $: visible = items.slice().map((data, i) => ({
111
180
  index: iteration_key ? data[iteration_key] : i,
112
181
  data,
113
182
  }));
114
183
 
184
+ $: viewportHeight, visible, relayout();
185
+
115
186
  function handle_scroll() {
116
- const { scrollTop, scrollHeight } = viewport;
117
- let offset = scrollHeight - viewport_height - scrollTop;
187
+ scrollHeight = contents.scrollHeight;
188
+ scrollTop = contents.scrollTop;
189
+ let offset = scrollHeight - viewportHeight - scrollTop;
118
190
  // fire on:end event if we scrolled past the end_offset
119
191
  if (offset <= end_offset) {
120
192
  if (!fired) {
@@ -130,6 +202,21 @@ while more items are loading
130
202
  }
131
203
 
132
204
  const resize = () => {
133
- viewport_height = viewport.offsetHeight;
205
+ viewportHeight = viewport.offsetHeight;
134
206
  };
207
+
208
+ export function relayout() {
209
+ tick().then(() => {
210
+ needsScroll = false;
211
+
212
+ scrollHeight = contents.scrollHeight;
213
+ offsetHeight = contents.offsetHeight;
214
+
215
+ let diff = scrollHeight - offsetHeight;
216
+ if (scrollHeight > offsetHeight) {
217
+ needsScroll = true;
218
+ }
219
+ scale = createScaler([0, diff], [0, 1]);
220
+ });
221
+ }
135
222
  </script>
@@ -48,7 +48,7 @@ This will not work if there is no title area
48
48
  <div class="level is-mobile">
49
49
  <div class="level-left">
50
50
  <div class="level-item">
51
- <!-- eslint-disable-next-line @ota-meshi/svelte/no-at-html-tags -->
51
+ <!-- eslint-disable-next-line svelte/no-at-html-tags -->
52
52
  <h2 class="subtitle">{@html title}</h2>
53
53
  </div>
54
54
  </div>
@@ -74,7 +74,7 @@ This will not work if there is no title area
74
74
  </div>
75
75
  {/if}
76
76
  <div class="level-item">
77
- <!-- eslint-disable-next-line @ota-meshi/svelte/no-at-html-tags -->
77
+ <!-- eslint-disable-next-line svelte/no-at-html-tags -->
78
78
  <h2 class="subtitle is-6">{@html subtitle}</h2>
79
79
  </div>
80
80
  <span class="collapsor level-item">
package/index.d.ts CHANGED
@@ -53,6 +53,7 @@ export { default as TileView } from "./datagrid/TileView/TileView.svelte";
53
53
  export { default as DataSearch } from "./datagrid/DataSearch/DataSearch.svelte";
54
54
  export { default as Pagination } from "./datagrid/Pagination/Pagination.svelte";
55
55
  export { default as DataSort } from "./datagrid/DataSort/DataSort.svelte";
56
+ export { default as StickyColumnsTableWrapper } from "./datagrid/StickyColumnsTableWrapper.svelte";
56
57
  export { default as Chart } from "./charts/Chart.svelte";
57
58
  export { default as DonutChart } from "./charts/DonutChart.svelte";
58
59
  export { default as PieChart } from "./charts/PieChart.svelte";
package/index.js CHANGED
@@ -79,6 +79,7 @@ export { default as TileView } from "./datagrid/TileView/TileView.svelte";
79
79
  export { default as DataSearch } from "./datagrid/DataSearch/DataSearch.svelte";
80
80
  export { default as Pagination } from "./datagrid/Pagination/Pagination.svelte";
81
81
  export { default as DataSort } from "./datagrid/DataSort/DataSort.svelte";
82
+ export { default as StickyColumnsTableWrapper } from "./datagrid/StickyColumnsTableWrapper.svelte";
82
83
 
83
84
  export { default as Chart } from "./charts/Chart.svelte";
84
85
  export { default as DonutChart } from "./charts/DonutChart.svelte";
@@ -69,14 +69,14 @@ export class DrawingPad {
69
69
  setColor(color: any): void;
70
70
  }
71
71
  export namespace Pen {
72
- const name: string;
73
- const icon: string;
72
+ let name: string;
73
+ let icon: string;
74
74
  function draw(instance: any): void;
75
75
  }
76
76
  export namespace Eraser {
77
- const name_1: string;
77
+ let name_1: string;
78
78
  export { name_1 as name };
79
- const icon_1: string;
79
+ let icon_1: string;
80
80
  export { icon_1 as icon };
81
81
  export function draw_1(instance: any): void;
82
82
  export { draw_1 as draw };
@@ -153,7 +153,7 @@ export class DrawingPad {
153
153
  0,
154
154
  0,
155
155
  this.canvas.width,
156
- this.canvas.height
156
+ this.canvas.height,
157
157
  );
158
158
  }
159
159
 
@@ -179,7 +179,7 @@ export class DrawingPad {
179
179
  0,
180
180
  0,
181
181
  this.canvas.width,
182
- this.canvas.height
182
+ this.canvas.height,
183
183
  );
184
184
  if (callBackFn) {
185
185
  callBackFn();
@@ -4,11 +4,11 @@ declare class UndoManager {
4
4
  index: number;
5
5
  isExecuting: boolean;
6
6
  callback: any;
7
- execute(command: any, action: any): UndoManager;
8
- add(command: any): UndoManager;
7
+ execute(command: any, action: any): this;
8
+ add(command: any): this;
9
9
  setCallback(callbackFunc: any): void;
10
- undo(): UndoManager;
11
- redo(): UndoManager;
10
+ undo(): this;
11
+ redo(): this;
12
12
  clear(): void;
13
13
  hasUndo(): boolean;
14
14
  hasRedo(): boolean;
@@ -1 +1 @@
1
- {"version":3,"file":"UndoManager.d.ts","sourceRoot":"","sources":["UndoManager.js"],"names":[],"mappings":";AAAA;IAEI,gBAAkB;IAClB,cAAe;IACf,qBAAwB;IACxB,cAAoB;IAGtB,gDAQC;IAED,+BAcC;IAED,qCAEC;IAED,oBAWC;IAED,oBAWC;IAED,cAOC;IACD,mBAEC;IACD,mBAEC;IACD,qBAEC;CACF"}
1
+ {"version":3,"file":"UndoManager.d.ts","sourceRoot":"","sources":["UndoManager.js"],"names":[],"mappings":";AAAA;IAEI,gBAAkB;IAClB,cAAe;IACf,qBAAwB;IACxB,cAAoB;IAGtB,yCAQC;IAED,wBAcC;IAED,qCAEC;IAED,aAWC;IAED,aAWC;IAED,cAOC;IACD,mBAEC;IACD,mBAEC;IACD,qBAEC;CACF"}
@@ -1,19 +1,20 @@
1
+ /// <reference types="svelte" />
1
2
  /**
2
3
  * @param {string | number} definition - can be a string like 'Enter', 'Tab' or number as keyCode, also allow combination key like 'ctrl+d', 'ctrl+alt+x'
3
4
  * @param {boolean} CommandKey - if true, in mac 'ctrl' key binding will be shift on 'command' key.
4
5
  * @returns {import("svelte/action").Action} .
5
6
  */
6
7
  export function makeKeyDefinition(definition: string | number, CommandKey?: boolean): import("svelte/action").Action;
7
- export let enter: import("svelte/action").Action<HTMLElement, any, any>;
8
- export let tab: import("svelte/action").Action<HTMLElement, any, any>;
9
- export let escape: import("svelte/action").Action<HTMLElement, any, any>;
10
- export let space: import("svelte/action").Action<HTMLElement, any, any>;
11
- export let leftarrow: import("svelte/action").Action<HTMLElement, any, any>;
12
- export let rightarrow: import("svelte/action").Action<HTMLElement, any, any>;
13
- export let downarrow: import("svelte/action").Action<HTMLElement, any, any>;
14
- export let uparrow: import("svelte/action").Action<HTMLElement, any, any>;
15
- export let backspace: import("svelte/action").Action<HTMLElement, any, any>;
16
- export let del: import("svelte/action").Action<HTMLElement, any, any>;
8
+ export let enter: import("svelte/action").Action<HTMLElement, undefined, any>;
9
+ export let tab: import("svelte/action").Action<HTMLElement, undefined, any>;
10
+ export let escape: import("svelte/action").Action<HTMLElement, undefined, any>;
11
+ export let space: import("svelte/action").Action<HTMLElement, undefined, any>;
12
+ export let leftarrow: import("svelte/action").Action<HTMLElement, undefined, any>;
13
+ export let rightarrow: import("svelte/action").Action<HTMLElement, undefined, any>;
14
+ export let downarrow: import("svelte/action").Action<HTMLElement, undefined, any>;
15
+ export let uparrow: import("svelte/action").Action<HTMLElement, undefined, any>;
16
+ export let backspace: import("svelte/action").Action<HTMLElement, undefined, any>;
17
+ export let del: import("svelte/action").Action<HTMLElement, undefined, any>;
17
18
  declare namespace _default {
18
19
  export { enter };
19
20
  export { tab };
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["index.js"],"names":[],"mappings":"AAQA;;;;GAIG;AAEH,8CALW,MAAM,GAAG,MAAM,eACf,OAAO,GACL,OAAO,eAAe,EAAE,MAAM,CA0D1C;AAED,wEAA8C;AAC9C,sEAA0C;AAC1C,yEAAgD;AAChD,wEAA0C;AAC1C,4EAAsD;AACtD,6EAAwD;AACxD,4EAAsD;AACtD,0EAAkD;AAClD,4EAAsD;AACtD,sEAA6C"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["index.js"],"names":[],"mappings":";AAQA;;;;GAIG;AAEH,8CALW,MAAM,GAAG,MAAM,eACf,OAAO,GACL,OAAO,eAAe,EAAE,MAAM,CA2D1C;AAED,8EAA8C;AAC9C,4EAA0C;AAC1C,+EAAgD;AAChD,8EAA0C;AAC1C,kFAAsD;AACtD,mFAAwD;AACxD,kFAAsD;AACtD,gFAAkD;AAClD,kFAAsD;AACtD,4EAA6C"}
package/keyboard/index.js CHANGED
@@ -54,7 +54,8 @@ export function makeKeyDefinition(definition, CommandKey = false) {
54
54
  valid = true;
55
55
  }
56
56
 
57
- if (valid) {
57
+ if (valid && fire) {
58
+ // @ts-ignore
58
59
  fire(event);
59
60
  }
60
61
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kws3/ui",
3
- "version": "4.1.3",
3
+ "version": "4.2.0",
4
4
  "description": "UI components for use with Svelte v3 applications.",
5
5
  "main": "index.js",
6
6
  "svelte": "index.js",
@@ -26,14 +26,14 @@
26
26
  },
27
27
  "types": "./index.d.ts",
28
28
  "dependencies": {
29
- "@kws3/text-mask-core": "^5.1.2",
29
+ "@kws3/text-mask-core": "^5.1.3",
30
30
  "apexcharts": "3.33.2",
31
31
  "flatpickr": "^4.5.2",
32
- "svelte-portal": "^2.1.2",
32
+ "svelte-portal": "^2.2.1",
33
33
  "tippy.js": "^6.3.1"
34
34
  },
35
35
  "devDependencies": {
36
- "typescript": "^5.0.4"
36
+ "typescript": "^5.2.2"
37
37
  },
38
- "gitHead": "4fd2b4a801628800fc5498495277ad45b40a01c6"
38
+ "gitHead": "cc7a86edeb6099b9ac018c0bf73a4134ebf287a3"
39
39
  }
package/search/index.d.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * @typedef {import('@kws3/ui/types').SearchOptions} SearchOptions - contains search options and fuzzy lib options
3
- * @typedef {import('@kws3/ui/types').SearchHelper} SearchHelper - returned search helper function which take unction take params `needle` and `haystack`.
3
+ * @typedef {import('@kws3/ui/types').SearchHelper} SearchHelper - returned search helper function which take function take params `needle` and `haystack`.
4
4
  */
5
5
  /**
6
6
  * @param {SearchOptions} opts
@@ -11,7 +11,7 @@ export function makeSearchEngine(opts: SearchOptions): (needle: string, haystack
11
11
  */
12
12
  export type SearchOptions = import('@kws3/ui/types').SearchOptions;
13
13
  /**
14
- * - returned search helper function which take unction take params `needle` and `haystack`.
14
+ * - returned search helper function which take function take params `needle` and `haystack`.
15
15
  */
16
16
  export type SearchHelper = import('@kws3/ui/types').SearchHelper;
17
17
  //# sourceMappingURL=index.d.ts.map
package/search/index.js CHANGED
@@ -2,7 +2,7 @@ import { fuzzy } from "../internal";
2
2
 
3
3
  /**
4
4
  * @typedef {import('@kws3/ui/types').SearchOptions} SearchOptions - contains search options and fuzzy lib options
5
- * @typedef {import('@kws3/ui/types').SearchHelper} SearchHelper - returned search helper function which take unction take params `needle` and `haystack`.
5
+ * @typedef {import('@kws3/ui/types').SearchHelper} SearchHelper - returned search helper function which take function take params `needle` and `haystack`.
6
6
  */
7
7
 
8
8
  /**
@@ -45,7 +45,7 @@ export function makeSearchEngine(opts) {
45
45
  let calculatedLimit = maxScore - scoreThreshold;
46
46
 
47
47
  OPTS = OPTS.filter(
48
- (r) => r.score > (calculatedLimit > 0 ? calculatedLimit : 0)
48
+ (r) => r.score > (calculatedLimit > 0 ? calculatedLimit : 0),
49
49
  );
50
50
  return OPTS.map((i) => i.original);
51
51
  };
package/settings.d.ts CHANGED
@@ -1,3 +1,4 @@
1
+ /// <reference types="svelte" />
1
2
  /**
2
3
  * - `defaultIconFamily`: `"fa"` - default icon family
3
4
  * - `defaultToastPlacement`: `"bottom"` - default placement of toast notifications
package/settings.d.ts.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"settings.d.ts","sourceRoot":"","sources":["settings.js"],"names":[],"mappings":"AAsCA;;;;;;;;;GASG;AAEH,mCAJW,cAAc,QAQxB;6BAlDY,OAAO,gBAAgB,EAAE,cAAc;AADpD;;GAEG;AAEH,wEAAyC;AACzC,4EAAiD;AACjD,+EAA0D;AAC1D,mFAA2D;AAC3D,sEAAsC;AACtC,2EAOG"}
1
+ {"version":3,"file":"settings.d.ts","sourceRoot":"","sources":["settings.js"],"names":[],"mappings":";AAsCA;;;;;;;;;GASG;AAEH,mCAJW,cAAc,QAQxB;6BAlDY,OAAO,gBAAgB,EAAE,cAAc;AADpD;;GAEG;AAEH,wEAAyC;AACzC,4EAAiD;AACjD,+EAA0D;AAC1D,mFAA2D;AAC3D,sEAAsC;AACtC,2EAOG"}
package/styles/Grid.scss CHANGED
@@ -1,12 +1,20 @@
1
- $kws-gridview-box-shadow: 0 0.125rem 0.1875rem rgba(0, 0, 0, 0.1),
1
+ $kws-gridview-box-shadow:
2
+ 0 0.125rem 0.1875rem rgba(0, 0, 0, 0.1),
2
3
  0 0 0 0.0625rem rgba(0, 0, 0, 0.1) !default;
3
4
  $kws-gridview-th-background: $table-background-color !default;
4
5
  $kws-gridview-th-color: $text !default;
5
6
  $kws-gridview-tag-margin: 0 0.125rem 0.125rem 0 !default;
6
7
  $kws-gridview-icon-size: 1.5em !default;
7
- $kws-gridview-checked-row-background: $primary-light !default;
8
+ $kws-gridview-checked-row-background: $info-light !default;
8
9
 
9
- .data-table .table {
10
+ $kws-gridview-active-row-background: $table-row-active-background-color !default;
11
+ $kws-gridview-active-row-color: $table-row-active-color !default;
12
+
13
+ $kws-gridview-background: $table-background-color !default;
14
+ $kws-gridview-striped-row-background: $table-striped-row-even-background-color !default;
15
+ $kws-gridview-sticky-column-border-color: $border !default;
16
+
17
+ .table.kws-grid-view {
10
18
  box-shadow: $kws-gridview-box-shadow;
11
19
  th {
12
20
  background: $kws-gridview-th-background;
@@ -69,3 +77,85 @@ $kws-gridview-checked-row-background: $primary-light !default;
69
77
  }
70
78
  }
71
79
  }
80
+
81
+ .data-table {
82
+ overflow: auto;
83
+ }
84
+ .kws-sticky-columns-table-wrapper {
85
+ position: relative;
86
+
87
+ .data-table {
88
+ margin-bottom: 1.5rem;
89
+ .table {
90
+ margin-bottom: 0;
91
+ }
92
+ }
93
+
94
+ .data-table .table {
95
+ &.is-striped tbody {
96
+ tr:not(.is-selected):nth-child(odd) td.is-sticky-column {
97
+ background: $kws-gridview-background;
98
+ }
99
+ tr:not(.is-selected):nth-child(even) td.is-sticky-column {
100
+ background: $kws-gridview-striped-row-background;
101
+ }
102
+ tr.is-selected {
103
+ background-color: transparent;
104
+ td {
105
+ background: $kws-gridview-active-row-background;
106
+ color: $kws-gridview-active-row-color;
107
+ }
108
+ }
109
+ }
110
+ td,
111
+ th {
112
+ &.is-sticky-column {
113
+ position: sticky;
114
+ &:first-child {
115
+ left: 0;
116
+ }
117
+ }
118
+ }
119
+ &.is-bordered {
120
+ td.is-sticky-column,
121
+ th.is-sticky-column {
122
+ box-shadow: inset -1px 0 0 $kws-gridview-sticky-column-border-color;
123
+ border-left-width: 0;
124
+ border-right-width: 0;
125
+ }
126
+ }
127
+ &.has-sticky-columns {
128
+ margin-bottom: 0px;
129
+ }
130
+ }
131
+
132
+ .fake-shadow-contraint {
133
+ position: absolute;
134
+ pointer-events: none;
135
+ top: 0;
136
+ right: 0;
137
+ bottom: 0;
138
+ width: 15px;
139
+ overflow: hidden;
140
+ .fake-inner-shadow {
141
+ position: absolute;
142
+ top: 0;
143
+ right: -1px;
144
+ height: 100%;
145
+ width: 1px;
146
+ box-shadow:
147
+ 0 0 6px 0 #000,
148
+ 0 0 9px 0 #000,
149
+ 0 0 15px 0 #000;
150
+ }
151
+
152
+ &.left {
153
+ right: auto;
154
+ left: 0;
155
+ .fake-inner-shadow {
156
+ right: auto;
157
+ left: -1px;
158
+ }
159
+ }
160
+ }
161
+ }
package/tsconfig.json CHANGED
@@ -5,6 +5,8 @@
5
5
  "lib": ["es2020", "DOM"],
6
6
  "skipLibCheck": true,
7
7
  "checkJs": true,
8
+ "jsx": "react",
9
+ "esModuleInterop": true,
8
10
  // Tells TypeScript to read JS files, as
9
11
  // normally they are ignored as source files
10
12
  "allowJs": true,
package/types/index.d.ts CHANGED
@@ -56,7 +56,7 @@ export type SearchOptions = {
56
56
 
57
57
  export type SearchHelper = (
58
58
  needle: string,
59
- haystack: Array<string>
59
+ haystack: Array<string>,
60
60
  ) => Array<any>;
61
61
 
62
62
  export type ValidatePasswordOptions = {
@@ -7,6 +7,8 @@ export const Colors = [
7
7
  "danger",
8
8
  "dark",
9
9
  "light",
10
+ "white",
11
+ "black",
10
12
  "link",
11
13
  ] as const;
12
14
 
package/utils/index.d.ts CHANGED
@@ -69,6 +69,22 @@ export function randomPercent(min?: number, max?: number): string;
69
69
  * @param {string} [fileName=''] - File Name.
70
70
  */
71
71
  export function fileDownloader(data?: any, fileName?: string): void;
72
+ /**
73
+ * Scales `value` between `inputRange` and `outputRange`
74
+ * @param {number} value
75
+ * @param {[number, number]} inputRange
76
+ * @param {[number, number]} outputRange
77
+ * @returns {number}
78
+ */
79
+ export function scale(value: number, inputRange: [number, number], outputRange: [number, number]): number;
80
+ /**
81
+ * Returns a function that can scale a `value` between
82
+ * `inputRange` and `outputRange`
83
+ * @param {[number, number]} inputRange
84
+ * @param {[number, number]} outputRange
85
+ * @returns {function}
86
+ */
87
+ export function createScaler(inputRange: [number, number], outputRange: [number, number]): Function;
72
88
  /**
73
89
  * Request an animation before the next repaint.
74
90
  * @param {function} [callback=function(){}] - callback function
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["index.js"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,+BAHW,MAAM,QACN,MAAM,UAIhB;AAED;;;GAGG;AACH,8BAFW,MAAM,UAQhB;AAED;;;GAGG;AACH,kCAFW,MAAM,OAIhB;AAsBD;;;;;;;;GAQG;AACH,oDAJW,MAAM,WACN,OAAO;;;EA2BjB;AAED;;;GAGG;AACH,+CAFW,MAAM,UAIhB;AAED;;;;GAIG;AACH,qCAFW,MAAM,QAKhB;AAED;;;GAGG;AACH,6BAFW,MAAM,UAKhB;AAED;;;GAGG;AACH,kCAFW,MAAM,UAUhB;AAED;;;;;;;GAOG;AACH,gDAHW,MAAM,QACN,MAAM,UAMhB;AAED;;;;GAIG;AACH,oCAHW,MAAM,QACN,MAAM,UAIhB;AAED;;;;GAIG;AACH,sDAFW,MAAM,QAYhB;AAtID;;;GAGG;AACH,sBAce"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["index.js"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,+BAHW,MAAM,QACN,MAAM,UAIhB;AAED;;;GAGG;AACH,8BAFW,MAAM,UAQhB;AAED;;;GAGG;AACH,kCAFW,MAAM,OAIhB;AAsBD;;;;;;;;GAQG;AACH,oDAJW,MAAM,WACN,OAAO;;;EA2BjB;AAED;;;GAGG;AACH,+CAFW,MAAM,UAIhB;AAED;;;;GAIG;AACH,qCAFW,MAAM,QAKhB;AAED;;;GAGG;AACH,6BAFW,MAAM,UAKhB;AAED;;;GAGG;AACH,kCAFW,MAAM,UAUhB;AAED;;;;;;;GAOG;AACH,gDAHW,MAAM,QACN,MAAM,UAMhB;AAED;;;;GAIG;AACH,oCAHW,MAAM,QACN,MAAM,UAIhB;AAED;;;;GAIG;AACH,sDAFW,MAAM,QAYhB;AAED;;;;;;GAMG;AACH,6BALW,MAAM,cACN,CAAC,MAAM,EAAE,MAAM,CAAC,eAChB,CAAC,MAAM,EAAE,MAAM,CAAC,GACd,MAAM,CAQlB;AAED;;;;;;GAMG;AACH,yCAJW,CAAC,MAAM,EAAE,MAAM,CAAC,eAChB,CAAC,MAAM,EAAE,MAAM,CAAC,YAU1B;AArKD;;;GAGG;AACH,sBAce"}
package/utils/index.js CHANGED
@@ -143,7 +143,7 @@ export function randomIntegerFromInterval(min = 0, max = Infinity) {
143
143
  * @param {number} [max=100] - Maximum Number, Default: `100`
144
144
  */
145
145
  export function randomPercent(min = 1, max = 100) {
146
- return randomIntegerFromInterval(min, max) + "%";
146
+ return `${randomIntegerFromInterval(min, max)}%`;
147
147
  }
148
148
 
149
149
  /**
@@ -162,3 +162,34 @@ export function fileDownloader(data, fileName = "file_name") {
162
162
  a.click();
163
163
  window.URL.revokeObjectURL(url);
164
164
  }
165
+
166
+ /**
167
+ * Scales `value` between `inputRange` and `outputRange`
168
+ * @param {number} value
169
+ * @param {[number, number]} inputRange
170
+ * @param {[number, number]} outputRange
171
+ * @returns {number}
172
+ */
173
+ export function scale(value, inputRange, outputRange) {
174
+ return (
175
+ ((value - inputRange[0]) * (outputRange[1] - outputRange[0])) /
176
+ (inputRange[1] - inputRange[0]) +
177
+ outputRange[0]
178
+ );
179
+ }
180
+
181
+ /**
182
+ * Returns a function that can scale a `value` between
183
+ * `inputRange` and `outputRange`
184
+ * @param {[number, number]} inputRange
185
+ * @param {[number, number]} outputRange
186
+ * @returns {function}
187
+ */
188
+ export function createScaler(inputRange, outputRange) {
189
+ /**
190
+ * @param {number} value
191
+ */
192
+ return function (value) {
193
+ return scale(value, inputRange, outputRange);
194
+ };
195
+ }