@agnos-ui/core 0.0.1-alpha.1 → 0.0.1-alpha.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/accordion.d.ts +14 -14
- package/accordion.js +65 -67
- package/alert.js +9 -7
- package/config.d.ts +5 -0
- package/extendWidget.d.ts +3 -0
- package/extendWidget.js +28 -0
- package/index.d.ts +2 -0
- package/index.js +2 -0
- package/modal/modal.js +17 -13
- package/package.json +2 -2
- package/progressbar.d.ts +87 -0
- package/progressbar.js +78 -0
- package/rating.d.ts +2 -2
- package/services/checks.d.ts +5 -0
- package/services/checks.js +5 -0
- package/services/index.d.ts +1 -0
- package/services/index.js +1 -0
- package/services/navManager.d.ts +5 -0
- package/services/navManager.js +52 -0
- package/services/sortUtils.d.ts +2 -0
- package/services/sortUtils.js +14 -0
- package/services/stores.d.ts +60 -23
- package/services/stores.js +92 -53
- package/services/writables.d.ts +2 -1
- package/services/writables.js +15 -1
package/accordion.d.ts
CHANGED
|
@@ -39,11 +39,11 @@ export interface AccordionProps extends WidgetsCommonPropsAndState {
|
|
|
39
39
|
*/
|
|
40
40
|
itemDisabled: boolean;
|
|
41
41
|
/**
|
|
42
|
-
* If `true`, the accordion-item will be
|
|
42
|
+
* If `true`, the accordion-item will be visible (expanded). Otherwise, it will be hidden (collapsed).
|
|
43
43
|
*
|
|
44
44
|
* It is a prop of the accordion-item.
|
|
45
45
|
*/
|
|
46
|
-
|
|
46
|
+
itemVisible: boolean;
|
|
47
47
|
/**
|
|
48
48
|
* If `true`, accordion-item will be animated.
|
|
49
49
|
*
|
|
@@ -69,13 +69,13 @@ export interface AccordionProps extends WidgetsCommonPropsAndState {
|
|
|
69
69
|
*/
|
|
70
70
|
onItemHidden: () => void;
|
|
71
71
|
/**
|
|
72
|
-
* An event fired when the `
|
|
72
|
+
* An event fired when the `visible` value changes.
|
|
73
73
|
*
|
|
74
|
-
* Event payload is the new value of
|
|
74
|
+
* Event payload is the new value of visible.
|
|
75
75
|
*
|
|
76
76
|
* It is a prop of the accordion-item.
|
|
77
77
|
*/
|
|
78
|
-
|
|
78
|
+
onItemVisibleChange: (visible: boolean) => void;
|
|
79
79
|
/**
|
|
80
80
|
* Structure of the accordion-item. The default item structure is: accordion-item
|
|
81
81
|
* contains accordion header and accordion collapse; the accordion header contains the accordion button
|
|
@@ -135,7 +135,7 @@ export interface AccordionState extends WidgetsCommonPropsAndState {
|
|
|
135
135
|
}
|
|
136
136
|
export interface AccordionApi {
|
|
137
137
|
/**
|
|
138
|
-
* Given the itemId, it will return if such item is expanded or not.
|
|
138
|
+
* Given the itemId, it will return if such item is visible (expanded) or not.
|
|
139
139
|
*
|
|
140
140
|
* If the itemId is not a valid id it will return `false`.
|
|
141
141
|
*/
|
|
@@ -169,7 +169,7 @@ export interface AccordionApi {
|
|
|
169
169
|
*/
|
|
170
170
|
collapseAll(): void;
|
|
171
171
|
/**
|
|
172
|
-
* Creates a new
|
|
172
|
+
* Creates a new accordionItem.
|
|
173
173
|
*/
|
|
174
174
|
registerItem(itemConfig?: PropsConfig<AccordionItemProps>): AccordionItemWidget;
|
|
175
175
|
}
|
|
@@ -217,9 +217,9 @@ export interface AccordionItemDirectives {
|
|
|
217
217
|
}
|
|
218
218
|
export interface AccordionItemCommonPropsAndState {
|
|
219
219
|
/**
|
|
220
|
-
* If `true`, the accordion-item will be
|
|
220
|
+
* If `true`, the accordion-item will be visible (expanded). Otherwise, it will be hidden (collapsed).
|
|
221
221
|
*/
|
|
222
|
-
|
|
222
|
+
itemVisible: boolean;
|
|
223
223
|
/**
|
|
224
224
|
* If `true`, the accordion-item will be disabled.
|
|
225
225
|
* It will not react to user's clicks, but still will be possible to toggle programmatically.
|
|
@@ -286,16 +286,16 @@ export interface AccordionItemProps extends AccordionItemCommonPropsAndState {
|
|
|
286
286
|
*/
|
|
287
287
|
onItemHidden: () => void;
|
|
288
288
|
/**
|
|
289
|
-
* An event fired when the `
|
|
289
|
+
* An event fired when the `visible` value changes.
|
|
290
290
|
*
|
|
291
|
-
* Event payload is the new value of
|
|
291
|
+
* Event payload is the new value of visible.
|
|
292
292
|
*/
|
|
293
|
-
|
|
293
|
+
onItemVisibleChange: (visible: boolean) => void;
|
|
294
294
|
}
|
|
295
295
|
export interface AccordionItemState extends AccordionItemCommonPropsAndState {
|
|
296
296
|
/**
|
|
297
|
-
* If `true` the
|
|
298
|
-
* value of the `
|
|
297
|
+
* If `true` the content of the accordion-item collapse should be in DOM. Its value depends on the
|
|
298
|
+
* value of the `itemVisible` and `itemDestroyOnHide`.
|
|
299
299
|
*/
|
|
300
300
|
shouldBeInDOM: boolean;
|
|
301
301
|
}
|
package/accordion.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { bindDirectiveNoArg, bindableDerived, directiveSubscribe, registrationArray, stateStores, typeBoolean, typeFunction, typeString, writablesForProps, } from './services';
|
|
1
|
+
import { bindDirectiveNoArg, bindableDerived, directiveSubscribe, registrationArray, stateStores, typeBoolean, typeFunction, typeString, writablesForProps, normalizeConfigStores, mergeConfigStores, } from './services';
|
|
2
2
|
import { createTransition } from './transitions';
|
|
3
3
|
import { collapseVerticalTransition } from './transitions/bootstrap';
|
|
4
4
|
import { computed, readable, writable } from '@amadeus-it-group/tansu';
|
|
@@ -8,21 +8,19 @@ function getItemId() {
|
|
|
8
8
|
return `accordion-item-${itemId++}`;
|
|
9
9
|
}
|
|
10
10
|
function adjustItemsCloseOthers(items, openItems, oldOpen) {
|
|
11
|
+
let keepOpen;
|
|
11
12
|
if (openItems.length == 2) {
|
|
12
13
|
oldOpen = oldOpen ?? openItems[0];
|
|
13
|
-
|
|
14
|
-
items.forEach((item) => {
|
|
15
|
-
if (item.state$().itemId !== keepOpen && !item.state$().itemCollapsed) {
|
|
16
|
-
item.patch({ itemCollapsed: true });
|
|
17
|
-
}
|
|
18
|
-
});
|
|
14
|
+
keepOpen = openItems.find((id) => id !== oldOpen);
|
|
19
15
|
}
|
|
20
16
|
else if (openItems.length > 2) {
|
|
21
17
|
//this case can happen when you have multiple items open and you toggle the close others
|
|
22
|
-
|
|
18
|
+
keepOpen = openItems[0];
|
|
19
|
+
}
|
|
20
|
+
if (keepOpen) {
|
|
23
21
|
items.forEach((item) => {
|
|
24
|
-
if (item.state$().itemId !== keepOpen &&
|
|
25
|
-
item.patch({
|
|
22
|
+
if (item.state$().itemId !== keepOpen && item.state$().itemVisible) {
|
|
23
|
+
item.patch({ itemVisible: false });
|
|
26
24
|
}
|
|
27
25
|
});
|
|
28
26
|
}
|
|
@@ -39,12 +37,12 @@ const defaultAccordionConfig = {
|
|
|
39
37
|
itemId: '',
|
|
40
38
|
itemDestroyOnHide: false,
|
|
41
39
|
itemDisabled: false,
|
|
42
|
-
|
|
40
|
+
itemVisible: false,
|
|
43
41
|
itemAnimation: true,
|
|
44
42
|
itemTransition: collapseVerticalTransition,
|
|
45
43
|
onItemShown: noop,
|
|
46
44
|
onItemHidden: noop,
|
|
47
|
-
|
|
45
|
+
onItemVisibleChange: noop,
|
|
48
46
|
slotItemStructure: undefined,
|
|
49
47
|
slotItemBody: undefined,
|
|
50
48
|
slotItemHeader: undefined,
|
|
@@ -58,12 +56,12 @@ const defaultItemConfig = {
|
|
|
58
56
|
itemId: defaultAccordionConfig.itemId,
|
|
59
57
|
itemDestroyOnHide: defaultAccordionConfig.itemDestroyOnHide,
|
|
60
58
|
itemDisabled: defaultAccordionConfig.itemDisabled,
|
|
61
|
-
|
|
59
|
+
itemVisible: defaultAccordionConfig.itemVisible,
|
|
62
60
|
itemAnimation: defaultAccordionConfig.itemAnimation,
|
|
63
61
|
itemTransition: defaultAccordionConfig.itemTransition,
|
|
64
62
|
onItemShown: defaultAccordionConfig.onItemShown,
|
|
65
63
|
onItemHidden: defaultAccordionConfig.onItemHidden,
|
|
66
|
-
|
|
64
|
+
onItemVisibleChange: defaultAccordionConfig.onItemVisibleChange,
|
|
67
65
|
slotItemStructure: defaultAccordionConfig.slotItemStructure,
|
|
68
66
|
slotItemBody: defaultAccordionConfig.slotItemBody,
|
|
69
67
|
slotItemHeader: defaultAccordionConfig.slotItemHeader,
|
|
@@ -73,6 +71,7 @@ const defaultItemConfig = {
|
|
|
73
71
|
itemCollapseClass: defaultAccordionConfig.itemCollapseClass,
|
|
74
72
|
itemBodyClass: defaultAccordionConfig.itemBodyClass,
|
|
75
73
|
};
|
|
74
|
+
const accordionItemProps = Object.keys(defaultItemConfig);
|
|
76
75
|
/**
|
|
77
76
|
* Retrieve a shallow copy of the default accordion config
|
|
78
77
|
* @returns the default accordion config
|
|
@@ -87,12 +86,12 @@ const configAccordionValidator = {
|
|
|
87
86
|
itemId: typeString,
|
|
88
87
|
itemDestroyOnHide: typeBoolean,
|
|
89
88
|
itemDisabled: typeBoolean,
|
|
90
|
-
|
|
89
|
+
itemVisible: typeBoolean,
|
|
91
90
|
itemAnimation: typeBoolean,
|
|
92
91
|
itemTransition: typeFunction,
|
|
93
92
|
onItemShown: typeFunction,
|
|
94
93
|
onItemHidden: typeFunction,
|
|
95
|
-
|
|
94
|
+
onItemVisibleChange: typeFunction,
|
|
96
95
|
itemClass: typeString,
|
|
97
96
|
itemHeaderClass: typeString,
|
|
98
97
|
itemButtonClass: typeString,
|
|
@@ -103,12 +102,12 @@ const configItemValidator = {
|
|
|
103
102
|
itemId: typeString,
|
|
104
103
|
itemDestroyOnHide: typeBoolean,
|
|
105
104
|
itemDisabled: typeBoolean,
|
|
106
|
-
|
|
105
|
+
itemVisible: typeBoolean,
|
|
107
106
|
itemAnimation: typeBoolean,
|
|
108
107
|
itemTransition: typeFunction,
|
|
109
108
|
onItemShown: typeFunction,
|
|
110
109
|
onItemHidden: typeFunction,
|
|
111
|
-
|
|
110
|
+
onItemVisibleChange: typeFunction,
|
|
112
111
|
itemClass: typeString,
|
|
113
112
|
itemHeaderClass: typeString,
|
|
114
113
|
itemButtonClass: typeString,
|
|
@@ -116,34 +115,33 @@ const configItemValidator = {
|
|
|
116
115
|
itemBodyClass: typeString,
|
|
117
116
|
};
|
|
118
117
|
function createAccordionItem(accordionOnShown, accordionOnHidden, config) {
|
|
119
|
-
const [{ itemAnimation$, itemTransition$, itemDestroyOnHide$, onItemShown$, onItemHidden$,
|
|
118
|
+
const [{ itemAnimation$, itemTransition$, itemDestroyOnHide$, onItemShown$, onItemHidden$, onItemVisibleChange$, itemVisible$, itemId$: _dirtyItemId$, itemDisabled$, ...stateProps }, patch,] = writablesForProps(defaultItemConfig, config, configItemValidator);
|
|
120
119
|
const initDone$ = writable(false);
|
|
121
120
|
const itemId$ = bindableDerived(readable(noop), [_dirtyItemId$], ([dirtyItemId]) => (dirtyItemId ? dirtyItemId : getItemId()));
|
|
122
121
|
const shouldBeInDOM$ = computed(() => {
|
|
123
122
|
return itemDestroyOnHide$() === false || !itemTransition.state$().hidden;
|
|
124
123
|
});
|
|
125
124
|
const itemTransition = createTransition({
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
onItemShown$()();
|
|
125
|
+
props: {
|
|
126
|
+
transition: itemTransition$,
|
|
127
|
+
visible: itemVisible$,
|
|
128
|
+
onVisibleChange: onItemVisibleChange$,
|
|
129
|
+
animation: itemAnimation$,
|
|
130
|
+
animationOnInit: false,
|
|
131
|
+
initDone: initDone$,
|
|
132
|
+
onHidden: () => {
|
|
133
|
+
accordionOnHidden()(itemId$());
|
|
134
|
+
onItemHidden$()();
|
|
135
|
+
},
|
|
136
|
+
onShown: () => {
|
|
137
|
+
accordionOnShown()(itemId$());
|
|
138
|
+
onItemShown$()();
|
|
139
|
+
},
|
|
142
140
|
},
|
|
143
141
|
});
|
|
144
142
|
return {
|
|
145
143
|
...stateStores({
|
|
146
|
-
|
|
144
|
+
itemVisible$,
|
|
147
145
|
itemId$,
|
|
148
146
|
shouldBeInDOM$,
|
|
149
147
|
itemDisabled$,
|
|
@@ -153,7 +151,7 @@ function createAccordionItem(accordionOnShown, accordionOnHidden, config) {
|
|
|
153
151
|
actions: {
|
|
154
152
|
click: () => {
|
|
155
153
|
if (!itemDisabled$()) {
|
|
156
|
-
|
|
154
|
+
itemVisible$.update((c) => !c);
|
|
157
155
|
}
|
|
158
156
|
},
|
|
159
157
|
},
|
|
@@ -162,13 +160,13 @@ function createAccordionItem(accordionOnShown, accordionOnHidden, config) {
|
|
|
162
160
|
initDone$.set(true);
|
|
163
161
|
},
|
|
164
162
|
collapse: () => {
|
|
165
|
-
|
|
163
|
+
itemVisible$.set(false);
|
|
166
164
|
},
|
|
167
165
|
expand: () => {
|
|
168
|
-
|
|
166
|
+
itemVisible$.set(true);
|
|
169
167
|
},
|
|
170
168
|
toggle: () => {
|
|
171
|
-
|
|
169
|
+
itemVisible$.update((c) => !c);
|
|
172
170
|
},
|
|
173
171
|
},
|
|
174
172
|
directives: { collapseDirective: bindDirectiveNoArg(itemTransition.directives.directive), accordionItemDirective: noop },
|
|
@@ -180,12 +178,31 @@ function createAccordionItem(accordionOnShown, accordionOnHidden, config) {
|
|
|
180
178
|
* @returns a new accordion widget instance
|
|
181
179
|
*/
|
|
182
180
|
export function createAccordion(config) {
|
|
183
|
-
const [{ closeOthers$, onShown$, onHidden$, itemId$, itemAnimation$, itemClass$, itemDisabled$,
|
|
181
|
+
const [{ closeOthers$, onShown$, onHidden$, itemId$, itemAnimation$, itemClass$, itemDisabled$, itemVisible$, itemTransition$, itemDestroyOnHide$, itemBodyClass$, itemButtonClass$, itemCollapseClass$, itemHeaderClass$, onItemVisibleChange$, onItemHidden$, onItemShown$, slotItemStructure$, slotItemBody$, slotItemHeader$, ...stateProps }, patch,] = writablesForProps(defaultAccordionConfig, config, configAccordionValidator);
|
|
182
|
+
const accordionItemConfig = {
|
|
183
|
+
itemId: itemId$,
|
|
184
|
+
itemClass: itemClass$,
|
|
185
|
+
itemAnimation: itemAnimation$,
|
|
186
|
+
itemDisabled: itemDisabled$,
|
|
187
|
+
itemVisible: itemVisible$,
|
|
188
|
+
itemTransition: itemTransition$,
|
|
189
|
+
itemDestroyOnHide: itemDestroyOnHide$,
|
|
190
|
+
itemBodyClass: itemBodyClass$,
|
|
191
|
+
itemButtonClass: itemButtonClass$,
|
|
192
|
+
itemCollapseClass: itemCollapseClass$,
|
|
193
|
+
itemHeaderClass: itemHeaderClass$,
|
|
194
|
+
onItemVisibleChange: onItemVisibleChange$,
|
|
195
|
+
onItemHidden: onItemHidden$,
|
|
196
|
+
onItemShown: onItemShown$,
|
|
197
|
+
slotItemStructure: slotItemStructure$,
|
|
198
|
+
slotItemBody: slotItemBody$,
|
|
199
|
+
slotItemHeader: slotItemHeader$,
|
|
200
|
+
};
|
|
184
201
|
const itemsWidget$ = registrationArray();
|
|
185
202
|
const openItems$ = computed(() => {
|
|
186
203
|
const openItems = [];
|
|
187
204
|
itemsWidget$().forEach((item) => {
|
|
188
|
-
if (
|
|
205
|
+
if (item.state$().itemVisible) {
|
|
189
206
|
openItems.push(item.state$().itemId);
|
|
190
207
|
}
|
|
191
208
|
});
|
|
@@ -197,9 +214,6 @@ export function createAccordion(config) {
|
|
|
197
214
|
adjustItemsCloseOthers(itemsWidget$(), openItems$(), oldOpenItem$());
|
|
198
215
|
oldOpenItem$.set(openItems$()[0]);
|
|
199
216
|
}
|
|
200
|
-
else {
|
|
201
|
-
itemsWidget$();
|
|
202
|
-
}
|
|
203
217
|
});
|
|
204
218
|
const action$ = computed(() => {
|
|
205
219
|
checkCloseOthersAction$();
|
|
@@ -212,7 +226,7 @@ export function createAccordion(config) {
|
|
|
212
226
|
isExpanded: (id) => {
|
|
213
227
|
const item = getItem(itemsWidget$(), id);
|
|
214
228
|
if (item) {
|
|
215
|
-
return
|
|
229
|
+
return item.state$().itemVisible;
|
|
216
230
|
}
|
|
217
231
|
else {
|
|
218
232
|
return false;
|
|
@@ -233,27 +247,11 @@ export function createAccordion(config) {
|
|
|
233
247
|
collapseAll: () => {
|
|
234
248
|
itemsWidget$().forEach((i) => i.api.collapse());
|
|
235
249
|
},
|
|
236
|
-
registerItem: (
|
|
237
|
-
const item = createAccordionItem(onShown$, onHidden$,
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
itemDisabled: itemDisabled$(),
|
|
242
|
-
itemCollapsed: itemCollapsed$(),
|
|
243
|
-
itemTransition: itemTransition$(),
|
|
244
|
-
itemDestroyOnHide: itemDestroyOnHide$(),
|
|
245
|
-
itemBodyClass: itemBodyClass$(),
|
|
246
|
-
itemButtonClass: itemButtonClass$(),
|
|
247
|
-
itemCollapseClass: itemCollapseClass$(),
|
|
248
|
-
itemHeaderClass: itemHeaderClass$(),
|
|
249
|
-
onItemCollapsedChange: onItemCollapsedChange$(),
|
|
250
|
-
onItemHidden: onItemHidden$(),
|
|
251
|
-
onItemShown: onItemShown$(),
|
|
252
|
-
slotItemStructure: slotItemStructure$(),
|
|
253
|
-
slotItemBody: slotItemBody$(),
|
|
254
|
-
slotItemHeader: slotItemHeader$(),
|
|
255
|
-
...itemConfig?.(),
|
|
256
|
-
})));
|
|
250
|
+
registerItem: (propsConfig) => {
|
|
251
|
+
const item = createAccordionItem(onShown$, onHidden$, {
|
|
252
|
+
config: mergeConfigStores(accordionItemProps, normalizeConfigStores(accordionItemProps, propsConfig?.config), accordionItemConfig),
|
|
253
|
+
props: propsConfig?.props,
|
|
254
|
+
});
|
|
257
255
|
item.directives.accordionItemDirective = () => ({ destroy: itemsWidget$.register(item) });
|
|
258
256
|
return item;
|
|
259
257
|
},
|
package/alert.js
CHANGED
|
@@ -36,13 +36,15 @@ const configValidator = {
|
|
|
36
36
|
export function createAlert(config) {
|
|
37
37
|
const [{ transition$, animationOnInit$, animation$, visible$: requestedVisible$, onVisibleChange$, onHidden$, onShown$, ...stateProps }, patch,] = writablesForProps(defaultConfig, config, configValidator);
|
|
38
38
|
const transition = createTransition({
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
39
|
+
props: {
|
|
40
|
+
transition: transition$,
|
|
41
|
+
visible: requestedVisible$,
|
|
42
|
+
animation: animation$,
|
|
43
|
+
animationOnInit: animationOnInit$,
|
|
44
|
+
onVisibleChange: onVisibleChange$,
|
|
45
|
+
onHidden: onHidden$,
|
|
46
|
+
onShown: onShown$,
|
|
47
|
+
},
|
|
46
48
|
});
|
|
47
49
|
const close = () => {
|
|
48
50
|
patch({ visible: false });
|
package/config.d.ts
CHANGED
|
@@ -5,6 +5,7 @@ import type { PaginationProps } from './pagination';
|
|
|
5
5
|
import type { RatingProps } from './rating';
|
|
6
6
|
import type { SelectProps } from './select';
|
|
7
7
|
import type { AccordionProps } from './accordion';
|
|
8
|
+
import type { ProgressbarProps } from './progressbar';
|
|
8
9
|
export type Partial2Levels<T> = Partial<{
|
|
9
10
|
[Level1 in keyof T]: Partial<T[Level1]>;
|
|
10
11
|
}>;
|
|
@@ -68,4 +69,8 @@ export interface WidgetsConfig {
|
|
|
68
69
|
* the accordion widget config
|
|
69
70
|
*/
|
|
70
71
|
accordion: AccordionProps;
|
|
72
|
+
/**
|
|
73
|
+
* the progress bar widget config
|
|
74
|
+
*/
|
|
75
|
+
progressbar: ProgressbarProps;
|
|
71
76
|
}
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
import type { ConfigValidator } from './services/stores';
|
|
2
|
+
import type { Widget, WidgetFactory, WidgetProps, WidgetState } from './types';
|
|
3
|
+
export declare const extendWidgetProps: <W extends Widget<object, object, object, object, object>, ExtraProps extends object>(factory: WidgetFactory<W>, extraPropsDefaults: ExtraProps, extraPropsConfig?: ConfigValidator<ExtraProps> | undefined) => WidgetFactory<Widget<WidgetProps<W> & ExtraProps, WidgetState<W> & ExtraProps, W["api"], W["actions"], W["directives"]>>;
|
package/extendWidget.js
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { batch } from '@amadeus-it-group/tansu';
|
|
2
|
+
import { stateStores, writablesWithDefault } from './services/stores';
|
|
3
|
+
export const extendWidgetProps = (factory, extraPropsDefaults, extraPropsConfig) => (propsConfig) => {
|
|
4
|
+
const extraPropsWritables = writablesWithDefault(extraPropsDefaults, propsConfig, extraPropsConfig);
|
|
5
|
+
const widget = factory(propsConfig);
|
|
6
|
+
return {
|
|
7
|
+
...widget,
|
|
8
|
+
...stateStores({ ...widget.stores, ...extraPropsWritables }),
|
|
9
|
+
patch: (storesValues) => batch(() => {
|
|
10
|
+
let widgetProps;
|
|
11
|
+
for (const [name, value] of Object.entries(storesValues ?? {})) {
|
|
12
|
+
const extraPropsStore = extraPropsWritables[`${name}$`];
|
|
13
|
+
if (extraPropsStore) {
|
|
14
|
+
extraPropsStore.set(value);
|
|
15
|
+
}
|
|
16
|
+
else {
|
|
17
|
+
if (!widgetProps) {
|
|
18
|
+
widgetProps = {};
|
|
19
|
+
}
|
|
20
|
+
widgetProps[name] = value;
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
if (widgetProps) {
|
|
24
|
+
widget.patch(widgetProps);
|
|
25
|
+
}
|
|
26
|
+
}),
|
|
27
|
+
};
|
|
28
|
+
};
|
package/index.d.ts
CHANGED
package/index.js
CHANGED
package/modal/modal.js
CHANGED
|
@@ -65,22 +65,26 @@ const modalsAction$ = computed(() => {
|
|
|
65
65
|
export const createModal = (config$) => {
|
|
66
66
|
const [{ animation$, backdrop$, backdropTransition$, closeOnOutsideClick$, container$, modalTransition$, onBeforeClose$, onVisibleChange$, onHidden$, onShown$, visible$: requestedVisible$, ...stateProps }, patch,] = writablesForProps(defaultConfig, config$);
|
|
67
67
|
const modalTransition = createTransition({
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
68
|
+
props: {
|
|
69
|
+
transition: modalTransition$,
|
|
70
|
+
visible: requestedVisible$,
|
|
71
|
+
animation: animation$,
|
|
72
|
+
animationOnInit: animation$,
|
|
73
|
+
onVisibleChange: onVisibleChange$,
|
|
74
|
+
// TODO: for onHidden and onShown, should we combine with information from the backdrop transition?
|
|
75
|
+
// (especially in case one of the two transitions takes more time than the other)
|
|
76
|
+
onHidden: onHidden$,
|
|
77
|
+
onShown: onShown$,
|
|
78
|
+
},
|
|
77
79
|
});
|
|
78
80
|
const visible$ = modalTransition.stores.visible$;
|
|
79
81
|
const backdropTransition = createTransition({
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
82
|
+
props: {
|
|
83
|
+
transition: backdropTransition$,
|
|
84
|
+
visible: requestedVisible$,
|
|
85
|
+
animation: animation$,
|
|
86
|
+
animationOnInit: animation$,
|
|
87
|
+
},
|
|
84
88
|
});
|
|
85
89
|
const transitioning$ = computed(() => modalTransition.stores.transitioning$() || (backdrop$() && backdropTransition.stores.transitioning$()));
|
|
86
90
|
const hidden$ = computed(() => !transitioning$() && !visible$());
|
package/package.json
CHANGED
|
@@ -23,10 +23,10 @@
|
|
|
23
23
|
}
|
|
24
24
|
},
|
|
25
25
|
"dependencies": {
|
|
26
|
-
"@amadeus-it-group/tansu": "0.0.
|
|
26
|
+
"@amadeus-it-group/tansu": "0.0.23"
|
|
27
27
|
},
|
|
28
28
|
"sideEffects": false,
|
|
29
|
-
"version": "0.0.1-alpha.
|
|
29
|
+
"version": "0.0.1-alpha.3",
|
|
30
30
|
"homepage": "https://amadeusitgroup.github.io/AgnosUI/latest/",
|
|
31
31
|
"bugs": "https://github.com/AmadeusITGroup/AgnosUI/issues",
|
|
32
32
|
"license": "MIT",
|
package/progressbar.d.ts
ADDED
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import type { PropsConfig } from './services';
|
|
2
|
+
import type { SlotContent, Widget, WidgetSlotContext } from './types';
|
|
3
|
+
import type { WidgetsCommonPropsAndState } from './commonProps';
|
|
4
|
+
export type ProgressbarContext = WidgetSlotContext<ProgressbarWidget>;
|
|
5
|
+
export interface ProgressbarCommonPropsAndState extends WidgetsCommonPropsAndState {
|
|
6
|
+
/**
|
|
7
|
+
* The minimum value.
|
|
8
|
+
* @defaultValue 0
|
|
9
|
+
*/
|
|
10
|
+
min: number;
|
|
11
|
+
/**
|
|
12
|
+
* The maximum value.
|
|
13
|
+
* @defaultValue 100
|
|
14
|
+
*/
|
|
15
|
+
max: number;
|
|
16
|
+
/**
|
|
17
|
+
* The current value.
|
|
18
|
+
* @defaultValue 0
|
|
19
|
+
*/
|
|
20
|
+
value: number;
|
|
21
|
+
/**
|
|
22
|
+
* The aria label.
|
|
23
|
+
*/
|
|
24
|
+
ariaLabel: string;
|
|
25
|
+
/**
|
|
26
|
+
* Global template for the Progressbar content.
|
|
27
|
+
*/
|
|
28
|
+
slotContent: SlotContent<ProgressbarContext>;
|
|
29
|
+
/**
|
|
30
|
+
* Label of the progress.
|
|
31
|
+
*/
|
|
32
|
+
slotDefault: SlotContent<ProgressbarContext>;
|
|
33
|
+
/**
|
|
34
|
+
* Height of the progressbar, can be any valid css height value.
|
|
35
|
+
*/
|
|
36
|
+
height: string;
|
|
37
|
+
/**
|
|
38
|
+
* If `true`, shows a striped progressbar.
|
|
39
|
+
*/
|
|
40
|
+
striped: boolean;
|
|
41
|
+
/**
|
|
42
|
+
* If `true`, animates a striped progressbar.
|
|
43
|
+
* Takes effect only for browsers supporting CSS3 animations, and if `striped` is `true`.
|
|
44
|
+
*/
|
|
45
|
+
animated: boolean;
|
|
46
|
+
}
|
|
47
|
+
export interface ProgressbarState extends ProgressbarCommonPropsAndState {
|
|
48
|
+
/**
|
|
49
|
+
* Percentage of completion.
|
|
50
|
+
*/
|
|
51
|
+
percentage: number;
|
|
52
|
+
/**
|
|
53
|
+
* `true` if the value is above its minimum value.
|
|
54
|
+
*/
|
|
55
|
+
started: boolean;
|
|
56
|
+
/**
|
|
57
|
+
* `true` if the value has reached its maximum value.
|
|
58
|
+
*/
|
|
59
|
+
finished: boolean;
|
|
60
|
+
/**
|
|
61
|
+
* The aria value text.
|
|
62
|
+
*/
|
|
63
|
+
ariaValueText: string | undefined;
|
|
64
|
+
}
|
|
65
|
+
export interface ProgressbarProps extends ProgressbarCommonPropsAndState {
|
|
66
|
+
/**
|
|
67
|
+
* Return the value for the 'aria-valuetext' attribute.
|
|
68
|
+
* @param value - current value
|
|
69
|
+
* @param minimum - minimum value
|
|
70
|
+
* @param maximum - maximum value
|
|
71
|
+
*/
|
|
72
|
+
ariaValueTextFn: (value: number, minimum: number, maximum: number) => string | undefined;
|
|
73
|
+
}
|
|
74
|
+
export interface ProgressbarApi {
|
|
75
|
+
}
|
|
76
|
+
export type ProgressbarWidget = Widget<ProgressbarProps, ProgressbarState, ProgressbarApi, object, object>;
|
|
77
|
+
/**
|
|
78
|
+
* Retrieve a shallow copy of the default Progressbar config
|
|
79
|
+
* @returns the default Progressbar config
|
|
80
|
+
*/
|
|
81
|
+
export declare function getProgressbarDefaultConfig(): ProgressbarProps;
|
|
82
|
+
/**
|
|
83
|
+
* Create an ProgressbarWidget with given config props
|
|
84
|
+
* @param config - an optional progress bar config
|
|
85
|
+
* @returns an ProgressbarWidget
|
|
86
|
+
*/
|
|
87
|
+
export declare function createProgressbar(config?: PropsConfig<ProgressbarProps>): ProgressbarWidget;
|
package/progressbar.js
ADDED
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import { clamp } from './services/checks';
|
|
2
|
+
import { bindableDerived, stateStores, typeBoolean, typeFunction, typeNumber, typeString, writablesForProps } from './services';
|
|
3
|
+
import { computed, readable } from '@amadeus-it-group/tansu';
|
|
4
|
+
import { noop } from './utils';
|
|
5
|
+
const defaultConfig = {
|
|
6
|
+
min: 0,
|
|
7
|
+
max: 100,
|
|
8
|
+
value: 0,
|
|
9
|
+
ariaLabel: 'Progressbar',
|
|
10
|
+
className: '',
|
|
11
|
+
slotContent: undefined,
|
|
12
|
+
slotDefault: undefined,
|
|
13
|
+
height: '',
|
|
14
|
+
striped: false,
|
|
15
|
+
animated: false,
|
|
16
|
+
ariaValueTextFn: () => undefined,
|
|
17
|
+
};
|
|
18
|
+
/**
|
|
19
|
+
* Retrieve a shallow copy of the default Progressbar config
|
|
20
|
+
* @returns the default Progressbar config
|
|
21
|
+
*/
|
|
22
|
+
export function getProgressbarDefaultConfig() {
|
|
23
|
+
return { ...defaultConfig };
|
|
24
|
+
}
|
|
25
|
+
const configValidator = {
|
|
26
|
+
min: typeNumber,
|
|
27
|
+
max: typeNumber,
|
|
28
|
+
value: typeNumber,
|
|
29
|
+
ariaLabel: typeString,
|
|
30
|
+
className: typeString,
|
|
31
|
+
height: typeString,
|
|
32
|
+
striped: typeBoolean,
|
|
33
|
+
animated: typeBoolean,
|
|
34
|
+
ariaValueTextFn: typeFunction,
|
|
35
|
+
};
|
|
36
|
+
/**
|
|
37
|
+
* Create an ProgressbarWidget with given config props
|
|
38
|
+
* @param config - an optional progress bar config
|
|
39
|
+
* @returns an ProgressbarWidget
|
|
40
|
+
*/
|
|
41
|
+
export function createProgressbar(config) {
|
|
42
|
+
const [{
|
|
43
|
+
// dirty inputs that need adjustment:
|
|
44
|
+
max$: _dirtyMaximum$, value$: _dirtyValue$,
|
|
45
|
+
// clean inputs
|
|
46
|
+
min$, ariaValueTextFn$, ...stateProps }, patch,] = writablesForProps(defaultConfig, config, configValidator);
|
|
47
|
+
const max$ = bindableDerived(readable(noop), [_dirtyMaximum$, min$], ([dirtyMaximum, minimum]) => Math.max(minimum, dirtyMaximum));
|
|
48
|
+
const value$ = bindableDerived(readable(noop), [_dirtyValue$, min$, max$], ([dirtyValue, min, max]) => clamp(dirtyValue, max, min));
|
|
49
|
+
const percentage$ = computed(() => {
|
|
50
|
+
const max = max$();
|
|
51
|
+
const min = min$();
|
|
52
|
+
if (max > min) {
|
|
53
|
+
return clamp(((value$() - min) * 100) / (max - min), 100, 0);
|
|
54
|
+
}
|
|
55
|
+
else {
|
|
56
|
+
return 0;
|
|
57
|
+
}
|
|
58
|
+
});
|
|
59
|
+
const started$ = computed(() => value$() > min$());
|
|
60
|
+
const finished$ = computed(() => value$() === max$());
|
|
61
|
+
const ariaValueText$ = computed(() => ariaValueTextFn$()(value$(), min$(), max$()));
|
|
62
|
+
return {
|
|
63
|
+
...stateStores({
|
|
64
|
+
min$,
|
|
65
|
+
max$,
|
|
66
|
+
value$,
|
|
67
|
+
percentage$,
|
|
68
|
+
started$,
|
|
69
|
+
finished$,
|
|
70
|
+
ariaValueText$,
|
|
71
|
+
...stateProps,
|
|
72
|
+
}),
|
|
73
|
+
patch,
|
|
74
|
+
api: {},
|
|
75
|
+
directives: {},
|
|
76
|
+
actions: {},
|
|
77
|
+
};
|
|
78
|
+
}
|
package/rating.d.ts
CHANGED
|
@@ -54,7 +54,7 @@ export interface RatingCommonPropsAndState extends WidgetsCommonPropsAndState {
|
|
|
54
54
|
}
|
|
55
55
|
export interface RatingProps extends RatingCommonPropsAndState {
|
|
56
56
|
/**
|
|
57
|
-
* Return the value for the 'aria-
|
|
57
|
+
* Return the value for the 'aria-valuetext' attribute.
|
|
58
58
|
* @param rating - Current rating value.
|
|
59
59
|
* @param maxRating - maxRating value.
|
|
60
60
|
*/
|
|
@@ -131,7 +131,7 @@ export type RatingWidget = Widget<RatingProps, RatingState, object, RatingAction
|
|
|
131
131
|
*/
|
|
132
132
|
export declare function getRatingDefaultConfig(): {
|
|
133
133
|
/**
|
|
134
|
-
* Return the value for the 'aria-
|
|
134
|
+
* Return the value for the 'aria-valuetext' attribute.
|
|
135
135
|
* @param rating - Current rating value.
|
|
136
136
|
* @param maxRating - maxRating value.
|
|
137
137
|
*/
|
package/services/checks.d.ts
CHANGED
|
@@ -22,6 +22,11 @@ export declare function isFunction(value: any): value is (...args: any[]) => any
|
|
|
22
22
|
* @returns true if the value is a string
|
|
23
23
|
*/
|
|
24
24
|
export declare function isString(value: any): value is string;
|
|
25
|
+
/**
|
|
26
|
+
* an array type guard
|
|
27
|
+
* @returns true if the value is an array
|
|
28
|
+
*/
|
|
29
|
+
export declare const isArray: (arg: any) => arg is any[];
|
|
25
30
|
/**
|
|
26
31
|
* Clamp the value based on a maximum and optional minimum
|
|
27
32
|
* @param value the value to check
|
package/services/checks.js
CHANGED
|
@@ -30,6 +30,11 @@ export function isFunction(value) {
|
|
|
30
30
|
export function isString(value) {
|
|
31
31
|
return typeof value === 'string';
|
|
32
32
|
}
|
|
33
|
+
/**
|
|
34
|
+
* an array type guard
|
|
35
|
+
* @returns true if the value is an array
|
|
36
|
+
*/
|
|
37
|
+
export const isArray = Array.isArray;
|
|
33
38
|
// TODO should we check that max > min?
|
|
34
39
|
/**
|
|
35
40
|
* Clamp the value based on a maximum and optional minimum
|
package/services/index.d.ts
CHANGED
package/services/index.js
CHANGED
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { compareDomOrder } from './sortUtils';
|
|
2
|
+
import { registrationArray } from './directiveUtils';
|
|
3
|
+
import { computed } from '@amadeus-it-group/tansu';
|
|
4
|
+
// cf https://html.spec.whatwg.org/multipage/input.html#concept-input-apply
|
|
5
|
+
const textInputTypes = new Set(['text', 'search', 'url', 'tel', 'password']);
|
|
6
|
+
const isTextInput = (element) => element instanceof HTMLInputElement && textInputTypes.has(element.type);
|
|
7
|
+
export const createNavManager = () => {
|
|
8
|
+
const array$ = registrationArray();
|
|
9
|
+
const sortedArray$ = computed(() => [...array$()].sort(compareDomOrder));
|
|
10
|
+
const directive = (element) => {
|
|
11
|
+
const onKeyDown = (event) => {
|
|
12
|
+
let move = 0;
|
|
13
|
+
switch (event.key) {
|
|
14
|
+
case 'ArrowLeft':
|
|
15
|
+
move = -1;
|
|
16
|
+
break;
|
|
17
|
+
case 'ArrowRight':
|
|
18
|
+
move = 1;
|
|
19
|
+
break;
|
|
20
|
+
}
|
|
21
|
+
if (isTextInput(event.target)) {
|
|
22
|
+
const cursorPosition = event.target.selectionStart === event.target.selectionEnd ? event.target.selectionStart : null;
|
|
23
|
+
if ((cursorPosition !== 0 && move < 0) || (cursorPosition !== event.target.value.length && move > 0)) {
|
|
24
|
+
move = 0;
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
if (move != 0) {
|
|
28
|
+
const array = sortedArray$();
|
|
29
|
+
const currentIndex = array.indexOf(element);
|
|
30
|
+
const newIndex = currentIndex + move;
|
|
31
|
+
if (newIndex < array.length && newIndex >= 0) {
|
|
32
|
+
const newItem = array[newIndex];
|
|
33
|
+
event.preventDefault();
|
|
34
|
+
newItem.focus();
|
|
35
|
+
if (isTextInput(newItem)) {
|
|
36
|
+
const position = move < 0 ? newItem.value.length : 0;
|
|
37
|
+
newItem.setSelectionRange(position, position);
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
};
|
|
42
|
+
element.addEventListener('keydown', onKeyDown);
|
|
43
|
+
const unregister = array$.register(element);
|
|
44
|
+
return {
|
|
45
|
+
destroy() {
|
|
46
|
+
element.removeEventListener('keydown', onKeyDown);
|
|
47
|
+
unregister();
|
|
48
|
+
},
|
|
49
|
+
};
|
|
50
|
+
};
|
|
51
|
+
return { directive };
|
|
52
|
+
};
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
export const compareDefault = (a, b) => (a < b ? -1 : a > b ? 1 : 0);
|
|
2
|
+
export const compareDomOrder = (element1, element2) => {
|
|
3
|
+
if (element1 === element2) {
|
|
4
|
+
return 0;
|
|
5
|
+
}
|
|
6
|
+
const result = element1.compareDocumentPosition(element2);
|
|
7
|
+
if (result & Node.DOCUMENT_POSITION_FOLLOWING) {
|
|
8
|
+
return -1;
|
|
9
|
+
}
|
|
10
|
+
else if (result & Node.DOCUMENT_POSITION_PRECEDING) {
|
|
11
|
+
return 1;
|
|
12
|
+
}
|
|
13
|
+
throw new Error('failed to compare elements');
|
|
14
|
+
};
|
package/services/stores.d.ts
CHANGED
|
@@ -1,9 +1,15 @@
|
|
|
1
|
-
import type { ReadableSignal,
|
|
2
|
-
export type ToWritableSignal<P
|
|
3
|
-
[K in keyof P
|
|
1
|
+
import type { ReadableSignal, StoreInput, StoreOptions, StoresInputValues, WritableSignal } from '@amadeus-it-group/tansu';
|
|
2
|
+
export type ToWritableSignal<P> = {
|
|
3
|
+
[K in keyof P as `${K & string}$`]-?: WritableSignal<P[K], P[K] | undefined>;
|
|
4
4
|
};
|
|
5
|
-
export type
|
|
6
|
-
[K in keyof T]?: ReadableSignal<T[K]
|
|
5
|
+
export type ReadableSignals<T extends object> = {
|
|
6
|
+
[K in keyof T]?: ReadableSignal<T[K] | undefined>;
|
|
7
|
+
};
|
|
8
|
+
export type ValuesOrReadableSignals<T extends object> = {
|
|
9
|
+
[K in keyof T]?: ReadableSignal<T[K] | undefined> | T[K];
|
|
10
|
+
};
|
|
11
|
+
export type ValuesOrWritableSignals<T extends object> = {
|
|
12
|
+
[K in keyof T]?: WritableSignal<T[K] | undefined> | T[K];
|
|
7
13
|
};
|
|
8
14
|
export type WithoutDollar<S extends `${string}$`> = S extends `${infer U}$` ? U : never;
|
|
9
15
|
export type ValueOfStore<S extends ReadableSignal<any>> = S extends ReadableSignal<infer U> ? U : never;
|
|
@@ -32,7 +38,7 @@ export type ToState<S extends {
|
|
|
32
38
|
* @param stores - object of stores
|
|
33
39
|
* @returns the patch function
|
|
34
40
|
*/
|
|
35
|
-
export declare function createPatch<T extends object
|
|
41
|
+
export declare function createPatch<T extends object>(stores: ToWritableSignal<T>): <U extends Partial<T>>(storesValues?: void | U | undefined) => void;
|
|
36
42
|
/**
|
|
37
43
|
* This utility function is designed to compare the first level of two objects.
|
|
38
44
|
*
|
|
@@ -46,12 +52,12 @@ export declare function createPatch<T extends object, V extends object = T>(stor
|
|
|
46
52
|
*/
|
|
47
53
|
export declare function findChangedProperties<T extends Record<string, any>>(obj1: Partial<T>, obj2: Partial<T>): Partial<T> | null;
|
|
48
54
|
export declare const INVALID_VALUE: unique symbol;
|
|
49
|
-
export type NormalizeValue<T
|
|
50
|
-
export interface WritableWithDefaultOptions<T
|
|
55
|
+
export type NormalizeValue<T> = (value: T) => T | typeof INVALID_VALUE;
|
|
56
|
+
export interface WritableWithDefaultOptions<T> {
|
|
51
57
|
/**
|
|
52
58
|
* the normalize value function. should return the invalidValue symbol when the provided value is invalid
|
|
53
59
|
*/
|
|
54
|
-
normalizeValue?: NormalizeValue<T
|
|
60
|
+
normalizeValue?: NormalizeValue<T>;
|
|
55
61
|
/**
|
|
56
62
|
* the equal function, allowing to compare two values. used to check if a previous and current values are equals.
|
|
57
63
|
*/
|
|
@@ -66,14 +72,35 @@ export interface WritableWithDefaultOptions<T, U = T> {
|
|
|
66
72
|
* `set` or `update` functions), or the `defValue` is used instead (if the invalid value comes from the `config$` store).
|
|
67
73
|
*
|
|
68
74
|
* @param defValue - Default value used when both the own value and the config$ value are undefined.
|
|
69
|
-
* @param config$ -
|
|
75
|
+
* @param config$ - Store containing the default value used when the own value is undefined
|
|
70
76
|
* @param options - Object which can contain the following optional functions: normalizeValue and equal
|
|
77
|
+
* @param own$ - Store containing the own value
|
|
71
78
|
* @returns a writable store with the extra default value and normalization logic described above
|
|
72
79
|
*/
|
|
73
|
-
export declare function writableWithDefault<T
|
|
74
|
-
export type ConfigValidator<T extends object
|
|
75
|
-
[K in keyof T
|
|
80
|
+
export declare function writableWithDefault<T>(defValue: T, config$?: ReadableSignal<T | undefined>, options?: WritableWithDefaultOptions<T>, own$?: WritableSignal<T | undefined>): WritableSignal<T, T | undefined>;
|
|
81
|
+
export type ConfigValidator<T extends object> = {
|
|
82
|
+
[K in keyof T]?: WritableWithDefaultOptions<T[K]>;
|
|
76
83
|
};
|
|
84
|
+
/**
|
|
85
|
+
* Returns true if the provided argument is a store (ReadableSignal).
|
|
86
|
+
* @param x - argument that is tested
|
|
87
|
+
* @returns true if the argument is a store (ReadableSignal)
|
|
88
|
+
*/
|
|
89
|
+
export declare const isStore: (x: any) => x is ReadableSignal<any>;
|
|
90
|
+
/**
|
|
91
|
+
* If the provided argument is already a store, it is returned as is, otherwise, a readable store is created with the provided argument as its initial value.
|
|
92
|
+
* @param x - either a store or a simple value
|
|
93
|
+
* @returns either x if x is already a store, or readable(x) otherwise
|
|
94
|
+
*/
|
|
95
|
+
export declare const toReadableStore: <T>(x: T | ReadableSignal<T>) => ReadableSignal<T>;
|
|
96
|
+
/**
|
|
97
|
+
* If the provided argument is already a store, it is returned as is, otherwise, a writable store is created with the provided argument as its initial value.
|
|
98
|
+
* @param x - either a writable store or a simple value
|
|
99
|
+
* @returns either x if x is already a store, or writable(x) otherwise
|
|
100
|
+
*/
|
|
101
|
+
export declare const toWritableStore: <T>(x: T | WritableSignal<T, T>) => WritableSignal<T, T>;
|
|
102
|
+
export declare const normalizeConfigStores: <T extends object>(keys: (keyof T)[], config?: ReadableSignal<Partial<T>> | ValuesOrReadableSignals<T> | undefined) => ReadableSignals<T>;
|
|
103
|
+
export declare const mergeConfigStores: <T extends object>(keys: (keyof T)[], config1?: ReadableSignals<T> | undefined, config2?: ReadableSignals<T> | undefined) => ReadableSignals<T>;
|
|
77
104
|
/**
|
|
78
105
|
* Returns an object containing, for each property of `defConfig`, a corresponding writable with the normalization and default value logic
|
|
79
106
|
* described in {@link writableWithDefault}. Keys in the returned object are the same as the ones present in `defConfig`,
|
|
@@ -81,8 +108,7 @@ export type ConfigValidator<T extends object, U extends object = T> = {
|
|
|
81
108
|
*
|
|
82
109
|
* @param defConfig - object containing, for each property, a default value to use in case `config$` does not provide the suitable default
|
|
83
110
|
* value for that property
|
|
84
|
-
* @param
|
|
85
|
-
* for each property of `defConfig` either a store containing the default value or the default value itself
|
|
111
|
+
* @param propsConfig - object defining the config and props
|
|
86
112
|
* @param options - object containing, for each property of `defConfig`, an optional object with the following optional functions: normalizeValue and equal
|
|
87
113
|
* @returns an object containing writables
|
|
88
114
|
*
|
|
@@ -90,8 +116,8 @@ export type ConfigValidator<T extends object, U extends object = T> = {
|
|
|
90
116
|
* ```ts
|
|
91
117
|
* const defConfig = {propA: 1};
|
|
92
118
|
* const validation = {propA: {normalizeValue: value => +value}};
|
|
93
|
-
* const config
|
|
94
|
-
* const {propA$} = writablesWithDefault(defConfig, config
|
|
119
|
+
* const config = writable({propA: 5});
|
|
120
|
+
* const {propA$} = writablesWithDefault(defConfig, {config}, validation);
|
|
95
121
|
* ```
|
|
96
122
|
*
|
|
97
123
|
* @example With an object containing a value and a store
|
|
@@ -99,15 +125,15 @@ export type ConfigValidator<T extends object, U extends object = T> = {
|
|
|
99
125
|
* const defConfig = {propA: 1, propB: 2};
|
|
100
126
|
* const validation = {propA: {normalizeValue: value => +value}};
|
|
101
127
|
* const config = {propA: 5, propB: writable(3)};
|
|
102
|
-
* const {propA$, propB$} = writablesWithDefault(defConfig, config, validation);
|
|
128
|
+
* const {propA$, propB$} = writablesWithDefault(defConfig, {config}, validation);
|
|
103
129
|
* ```
|
|
104
130
|
*/
|
|
105
|
-
export declare const writablesWithDefault: <T extends object
|
|
131
|
+
export declare const writablesWithDefault: <T extends object>(defConfig: T, propsConfig?: PropsConfig<T> | undefined, options?: ConfigValidator<T> | undefined) => ToWritableSignal<T>;
|
|
106
132
|
/**
|
|
107
133
|
* Shortcut for calling both {@link writablesWithDefault} and {@link createPatch} in one call.
|
|
108
134
|
* @param defConfig - object containing, for each property, a default value to use in case `config` does not provide the suitable default
|
|
109
135
|
* value for that property
|
|
110
|
-
* @param
|
|
136
|
+
* @param propsConfig - either a store of objects containing, for each property of `defConfig`, the default value or an object containing
|
|
111
137
|
* for each property of `defConfig` either a store containing the default value or the default value itself
|
|
112
138
|
* @param options - object containing, for each property of `defConfig`, an optional object with the following optional functions: normalizeValue and equal
|
|
113
139
|
* @returns an array with two items: the first one containing the writables (returned by {@link writablesWithDefault}),
|
|
@@ -129,12 +155,23 @@ export declare const writablesWithDefault: <T extends object, U extends object =
|
|
|
129
155
|
* const [{propA$, propB$}, patch] = writablesForProps(defConfig, config, validation);
|
|
130
156
|
* ```
|
|
131
157
|
*/
|
|
132
|
-
export declare const writablesForProps: <T extends object
|
|
133
|
-
export
|
|
158
|
+
export declare const writablesForProps: <T extends object>(defConfig: T, propsConfig?: PropsConfig<T> | undefined, options?: { [K in keyof T]?: WritableWithDefaultOptions<T[K]> | undefined; } | undefined) => [ToWritableSignal<T>, <U extends Partial<T>>(storesValues?: void | U | undefined) => void];
|
|
159
|
+
export interface PropsConfig<U extends object> {
|
|
160
|
+
/**
|
|
161
|
+
* Object containing, for each property, either its initial value, or a store that will contain the value at any time.
|
|
162
|
+
* When the value of a property is undefined or invalid, the value from the config is used.
|
|
163
|
+
*/
|
|
164
|
+
props?: ValuesOrWritableSignals<U>;
|
|
165
|
+
/**
|
|
166
|
+
* Either a store of objects containing, for each property, the default value,
|
|
167
|
+
* or an object containing, for each property, either a store containing the default value or the default value itself.
|
|
168
|
+
*/
|
|
169
|
+
config?: ReadableSignal<Partial<U>> | ValuesOrReadableSignals<Partial<U>>;
|
|
170
|
+
}
|
|
134
171
|
export declare const stateStores: <A extends {
|
|
135
172
|
[x: `${string}$`]: ReadableSignal<any>;
|
|
136
173
|
}>(inputStores: A) => {
|
|
137
174
|
state$: ReadableSignal<ToState<A>>;
|
|
138
175
|
stores: { [key in `${string}$` & keyof A]: ReadableSignal<ValueOfStore<A[key]>>; };
|
|
139
176
|
};
|
|
140
|
-
export declare const bindableDerived: <T, U extends [WritableSignal<T, T>, ...StoreInput<any>[]]>(onChange$: ReadableSignal<(value: T) => void>, stores: U, adjustValue: (arg: StoresInputValues<U>) => T) => ReadableSignal<T>;
|
|
177
|
+
export declare const bindableDerived: <T, U extends [WritableSignal<T, T>, ...StoreInput<any>[]]>(onChange$: ReadableSignal<(value: T) => void>, stores: U, adjustValue: (arg: StoresInputValues<U>) => T, equal?: (currentValue: T, newValue: T) => boolean) => ReadableSignal<T>;
|
package/services/stores.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { batch, computed, derived, get, readable, writable
|
|
1
|
+
import { asReadable, batch, computed, derived, get, readable, writable } from '@amadeus-it-group/tansu';
|
|
2
2
|
import { identity } from '../utils';
|
|
3
3
|
/**
|
|
4
4
|
*
|
|
@@ -60,7 +60,6 @@ const update = function (updater) {
|
|
|
60
60
|
this.set(updater(this()));
|
|
61
61
|
};
|
|
62
62
|
export const INVALID_VALUE = Symbol();
|
|
63
|
-
/* eslint-disable jsdoc/check-param-names, jsdoc/require-param */
|
|
64
63
|
/**
|
|
65
64
|
* Returns a writable store whose value is either its own value (when it is not undefined) or a default value
|
|
66
65
|
* that comes either from the `config$` store (when it is not undefined) or from `defValue`.
|
|
@@ -70,41 +69,81 @@ export const INVALID_VALUE = Symbol();
|
|
|
70
69
|
* `set` or `update` functions), or the `defValue` is used instead (if the invalid value comes from the `config$` store).
|
|
71
70
|
*
|
|
72
71
|
* @param defValue - Default value used when both the own value and the config$ value are undefined.
|
|
73
|
-
* @param config$ -
|
|
72
|
+
* @param config$ - Store containing the default value used when the own value is undefined
|
|
74
73
|
* @param options - Object which can contain the following optional functions: normalizeValue and equal
|
|
74
|
+
* @param own$ - Store containing the own value
|
|
75
75
|
* @returns a writable store with the extra default value and normalization logic described above
|
|
76
76
|
*/
|
|
77
|
-
export function writableWithDefault(defValue, config$ = readable(undefined),
|
|
78
|
-
const
|
|
79
|
-
const
|
|
80
|
-
|
|
77
|
+
export function writableWithDefault(defValue, config$ = readable(undefined), options = {}, own$ = writable(undefined)) {
|
|
78
|
+
const { normalizeValue = identity, equal = Object.is } = options;
|
|
79
|
+
const getDefValue = () => defValue;
|
|
80
|
+
const callNormalizeValue = (value, defValue = getDefValue) => {
|
|
81
81
|
const normalizedValue = value === undefined ? undefined : normalizeValue(value);
|
|
82
82
|
if (normalizedValue === INVALID_VALUE) {
|
|
83
|
-
console.error('Not using invalid value
|
|
84
|
-
return defValue;
|
|
83
|
+
console.error('Not using invalid value', value);
|
|
84
|
+
return defValue();
|
|
85
85
|
}
|
|
86
86
|
if (normalizedValue === undefined) {
|
|
87
|
-
return defValue;
|
|
87
|
+
return defValue();
|
|
88
88
|
}
|
|
89
89
|
return normalizedValue;
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
}, { equal }), {
|
|
90
|
+
};
|
|
91
|
+
const validatedDefConfig$ = computed(() => callNormalizeValue(config$()), { equal });
|
|
92
|
+
const validatedOwnValue$ = computed(() => callNormalizeValue(own$(), validatedDefConfig$), { equal });
|
|
93
|
+
return asReadable(validatedOwnValue$, {
|
|
95
94
|
set(value) {
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
95
|
+
if (value !== undefined) {
|
|
96
|
+
const normalizedValue = normalizeValue(value);
|
|
97
|
+
if (normalizedValue === INVALID_VALUE) {
|
|
98
|
+
console.error('Not setting invalid value', value);
|
|
99
|
+
return;
|
|
100
|
+
}
|
|
101
|
+
value = normalizedValue;
|
|
102
102
|
}
|
|
103
|
+
own$.set(value);
|
|
103
104
|
},
|
|
104
105
|
update,
|
|
105
106
|
});
|
|
106
107
|
}
|
|
107
|
-
|
|
108
|
+
/**
|
|
109
|
+
* Returns true if the provided argument is a store (ReadableSignal).
|
|
110
|
+
* @param x - argument that is tested
|
|
111
|
+
* @returns true if the argument is a store (ReadableSignal)
|
|
112
|
+
*/
|
|
113
|
+
export const isStore = (x) => !!(x && typeof x === 'function' && 'subscribe' in x);
|
|
114
|
+
/**
|
|
115
|
+
* If the provided argument is already a store, it is returned as is, otherwise, a readable store is created with the provided argument as its initial value.
|
|
116
|
+
* @param x - either a store or a simple value
|
|
117
|
+
* @returns either x if x is already a store, or readable(x) otherwise
|
|
118
|
+
*/
|
|
119
|
+
export const toReadableStore = (x) => (isStore(x) ? x : readable(x));
|
|
120
|
+
/**
|
|
121
|
+
* If the provided argument is already a store, it is returned as is, otherwise, a writable store is created with the provided argument as its initial value.
|
|
122
|
+
* @param x - either a writable store or a simple value
|
|
123
|
+
* @returns either x if x is already a store, or writable(x) otherwise
|
|
124
|
+
*/
|
|
125
|
+
export const toWritableStore = (x) => (isStore(x) ? x : writable(x));
|
|
126
|
+
export const normalizeConfigStores = (keys, config) => {
|
|
127
|
+
const res = {};
|
|
128
|
+
if (config) {
|
|
129
|
+
const configIsStore = isStore(config);
|
|
130
|
+
for (const key of keys) {
|
|
131
|
+
res[key] = configIsStore
|
|
132
|
+
? computed(() => config()[key])
|
|
133
|
+
: toReadableStore(config[key]);
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
return res;
|
|
137
|
+
};
|
|
138
|
+
export const mergeConfigStores = (keys, config1, config2) => {
|
|
139
|
+
const res = {};
|
|
140
|
+
for (const key of keys) {
|
|
141
|
+
const config1Store = config1?.[key];
|
|
142
|
+
const config2Store = config2?.[key];
|
|
143
|
+
res[key] = config1Store && config2Store ? computed(() => config1Store() ?? config2Store()) : config1Store || config2Store;
|
|
144
|
+
}
|
|
145
|
+
return res;
|
|
146
|
+
};
|
|
108
147
|
/**
|
|
109
148
|
* Returns an object containing, for each property of `defConfig`, a corresponding writable with the normalization and default value logic
|
|
110
149
|
* described in {@link writableWithDefault}. Keys in the returned object are the same as the ones present in `defConfig`,
|
|
@@ -112,8 +151,7 @@ const isStore = (x) => !!(x && typeof x === 'function' && 'subscribe' in x);
|
|
|
112
151
|
*
|
|
113
152
|
* @param defConfig - object containing, for each property, a default value to use in case `config$` does not provide the suitable default
|
|
114
153
|
* value for that property
|
|
115
|
-
* @param
|
|
116
|
-
* for each property of `defConfig` either a store containing the default value or the default value itself
|
|
154
|
+
* @param propsConfig - object defining the config and props
|
|
117
155
|
* @param options - object containing, for each property of `defConfig`, an optional object with the following optional functions: normalizeValue and equal
|
|
118
156
|
* @returns an object containing writables
|
|
119
157
|
*
|
|
@@ -121,8 +159,8 @@ const isStore = (x) => !!(x && typeof x === 'function' && 'subscribe' in x);
|
|
|
121
159
|
* ```ts
|
|
122
160
|
* const defConfig = {propA: 1};
|
|
123
161
|
* const validation = {propA: {normalizeValue: value => +value}};
|
|
124
|
-
* const config
|
|
125
|
-
* const {propA$} = writablesWithDefault(defConfig, config
|
|
162
|
+
* const config = writable({propA: 5});
|
|
163
|
+
* const {propA$} = writablesWithDefault(defConfig, {config}, validation);
|
|
126
164
|
* ```
|
|
127
165
|
*
|
|
128
166
|
* @example With an object containing a value and a store
|
|
@@ -130,19 +168,17 @@ const isStore = (x) => !!(x && typeof x === 'function' && 'subscribe' in x);
|
|
|
130
168
|
* const defConfig = {propA: 1, propB: 2};
|
|
131
169
|
* const validation = {propA: {normalizeValue: value => +value}};
|
|
132
170
|
* const config = {propA: 5, propB: writable(3)};
|
|
133
|
-
* const {propA$, propB$} = writablesWithDefault(defConfig, config, validation);
|
|
171
|
+
* const {propA$, propB$} = writablesWithDefault(defConfig, {config}, validation);
|
|
134
172
|
* ```
|
|
135
173
|
*/
|
|
136
|
-
export const writablesWithDefault = (defConfig,
|
|
174
|
+
export const writablesWithDefault = (defConfig, propsConfig, options) => {
|
|
137
175
|
const res = {};
|
|
138
|
-
const
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
}
|
|
145
|
-
res[`${key}$`] = writableWithDefault(defConfig[key], store, options?.[key]);
|
|
176
|
+
const keys = Object.keys(defConfig);
|
|
177
|
+
const configStores = normalizeConfigStores(keys, propsConfig?.config);
|
|
178
|
+
const props = propsConfig?.props;
|
|
179
|
+
for (const key of keys) {
|
|
180
|
+
const propValue = props?.[key];
|
|
181
|
+
res[`${key}$`] = writableWithDefault(defConfig[key], configStores[key], options?.[key], toWritableStore(propValue));
|
|
146
182
|
}
|
|
147
183
|
return res;
|
|
148
184
|
};
|
|
@@ -150,7 +186,7 @@ export const writablesWithDefault = (defConfig, config, options) => {
|
|
|
150
186
|
* Shortcut for calling both {@link writablesWithDefault} and {@link createPatch} in one call.
|
|
151
187
|
* @param defConfig - object containing, for each property, a default value to use in case `config` does not provide the suitable default
|
|
152
188
|
* value for that property
|
|
153
|
-
* @param
|
|
189
|
+
* @param propsConfig - either a store of objects containing, for each property of `defConfig`, the default value or an object containing
|
|
154
190
|
* for each property of `defConfig` either a store containing the default value or the default value itself
|
|
155
191
|
* @param options - object containing, for each property of `defConfig`, an optional object with the following optional functions: normalizeValue and equal
|
|
156
192
|
* @returns an array with two items: the first one containing the writables (returned by {@link writablesWithDefault}),
|
|
@@ -172,8 +208,8 @@ export const writablesWithDefault = (defConfig, config, options) => {
|
|
|
172
208
|
* const [{propA$, propB$}, patch] = writablesForProps(defConfig, config, validation);
|
|
173
209
|
* ```
|
|
174
210
|
*/
|
|
175
|
-
export const writablesForProps = (defConfig,
|
|
176
|
-
const stores = writablesWithDefault(defConfig,
|
|
211
|
+
export const writablesForProps = (defConfig, propsConfig, options) => {
|
|
212
|
+
const stores = writablesWithDefault(defConfig, propsConfig, options);
|
|
177
213
|
return [stores, createPatch(stores)];
|
|
178
214
|
};
|
|
179
215
|
export const stateStores = (inputStores) => {
|
|
@@ -200,20 +236,23 @@ export const stateStores = (inputStores) => {
|
|
|
200
236
|
}),
|
|
201
237
|
};
|
|
202
238
|
};
|
|
203
|
-
export const bindableDerived = (onChange$, stores, adjustValue) => {
|
|
239
|
+
export const bindableDerived = (onChange$, stores, adjustValue, equal = (currentValue, newValue) => newValue === currentValue) => {
|
|
204
240
|
let currentValue = stores[0]();
|
|
205
|
-
return derived(stores,
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
currentValue
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
241
|
+
return derived(stores, {
|
|
242
|
+
derive(values) {
|
|
243
|
+
const newValue = adjustValue(values);
|
|
244
|
+
const rectifiedValue = !equal(values[0], newValue);
|
|
245
|
+
if (rectifiedValue) {
|
|
246
|
+
stores[0].set(newValue);
|
|
247
|
+
}
|
|
248
|
+
if (rectifiedValue || !equal(currentValue, newValue)) {
|
|
249
|
+
currentValue = newValue;
|
|
250
|
+
// TODO check if we should do this async to avoid issue
|
|
251
|
+
// with angular and react only when rectifiedValue is true?
|
|
252
|
+
onChange$()(newValue);
|
|
253
|
+
}
|
|
254
|
+
return newValue;
|
|
255
|
+
},
|
|
256
|
+
equal,
|
|
218
257
|
});
|
|
219
258
|
};
|
package/services/writables.d.ts
CHANGED
|
@@ -3,5 +3,6 @@ import { INVALID_VALUE } from './stores';
|
|
|
3
3
|
export declare const testToNormalizeValue: <T>(filter: (value: T) => boolean) => (value: T) => typeof INVALID_VALUE | T;
|
|
4
4
|
export declare const typeNumber: WritableWithDefaultOptions<number>;
|
|
5
5
|
export declare const typeBoolean: WritableWithDefaultOptions<boolean>;
|
|
6
|
-
export declare const typeString: WritableWithDefaultOptions<string
|
|
6
|
+
export declare const typeString: WritableWithDefaultOptions<string>;
|
|
7
7
|
export declare const typeFunction: WritableWithDefaultOptions<(...args: any[]) => any>;
|
|
8
|
+
export declare const typeArray: WritableWithDefaultOptions<any[]>;
|
package/services/writables.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { isBoolean, isFunction, isNumber, isString } from './checks';
|
|
1
|
+
import { isArray, isBoolean, isFunction, isNumber, isString } from './checks';
|
|
2
2
|
import { INVALID_VALUE } from './stores';
|
|
3
3
|
export const testToNormalizeValue = (filter) => (value) => filter(value) ? value : INVALID_VALUE;
|
|
4
4
|
export const typeNumber = {
|
|
@@ -14,3 +14,17 @@ export const typeFunction = {
|
|
|
14
14
|
normalizeValue: testToNormalizeValue(isFunction),
|
|
15
15
|
equal: Object.is,
|
|
16
16
|
};
|
|
17
|
+
export const typeArray = {
|
|
18
|
+
normalizeValue: testToNormalizeValue(isArray),
|
|
19
|
+
equal: (a, b) => {
|
|
20
|
+
if (a === b) {
|
|
21
|
+
return true;
|
|
22
|
+
}
|
|
23
|
+
else {
|
|
24
|
+
if (a?.length !== b?.length) {
|
|
25
|
+
return false;
|
|
26
|
+
}
|
|
27
|
+
return a.every((val, index) => val === b[index]);
|
|
28
|
+
}
|
|
29
|
+
},
|
|
30
|
+
};
|