@agnos-ui/svelte-headless 0.0.1-alpha.1

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/README.md ADDED
@@ -0,0 +1,15 @@
1
+ # @agnos-ui/svelte-headless
2
+
3
+ [![npm](https://img.shields.io/npm/v/@agnos-ui/svelte-headless)](https://www.npmjs.com/package/@agnos-ui/svelte-headless)
4
+
5
+ Headless widget library for [Svelte](https://svelte.dev/).
6
+
7
+ ## Installation
8
+
9
+ ```sh
10
+ npm install @agnos-ui/svelte-headless
11
+ ```
12
+
13
+ ## Usage
14
+
15
+ Please check [our demo site](https://amadeusitgroup.github.io/AgnosUI/latest/) to see all the available widgets and how to use them.
package/Slot.svelte ADDED
@@ -0,0 +1,23 @@
1
+ <script lang="ts">
2
+ import type {SlotContent, SlotSvelteComponent} from './utils';
3
+ import {isSvelteComponent, useSvelteSlot} from './utils';
4
+ type Props = $$Generic<object>; // eslint-disable-line no-undef
5
+ // cf https://github.com/ota-meshi/eslint-plugin-svelte/issues/348
6
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
7
+ type $$Slots = {
8
+ default: {component: SlotSvelteComponent<Props>; props: Props};
9
+ slot: {props: Props};
10
+ };
11
+ export let slotContent: SlotContent<Props> = null;
12
+ export let props: Props;
13
+ </script>
14
+
15
+ {#if slotContent === useSvelteSlot}
16
+ <slot name="slot" {props} />
17
+ {:else if typeof slotContent === 'string'}
18
+ {slotContent}
19
+ {:else if slotContent && !isSvelteComponent(slotContent)}
20
+ {slotContent(props)}
21
+ {:else if slotContent}
22
+ <slot component={slotContent} {props} />
23
+ {/if}
@@ -0,0 +1,26 @@
1
+ import { SvelteComponentTyped } from "svelte";
2
+ import type { SlotContent, SlotSvelteComponent } from './utils';
3
+ declare class __sveltets_Render<Props extends object> {
4
+ props(): {
5
+ slotContent?: SlotContent<Props>;
6
+ props: Props;
7
+ };
8
+ events(): {} & {
9
+ [evt: string]: CustomEvent<any>;
10
+ };
11
+ slots(): {
12
+ default: {
13
+ component: SlotSvelteComponent<Props>;
14
+ props: Props;
15
+ };
16
+ slot: {
17
+ props: Props;
18
+ };
19
+ };
20
+ }
21
+ export type SlotProps<Props extends object> = ReturnType<__sveltets_Render<Props>['props']>;
22
+ export type SlotEvents<Props extends object> = ReturnType<__sveltets_Render<Props>['events']>;
23
+ export type SlotSlots<Props extends object> = ReturnType<__sveltets_Render<Props>['slots']>;
24
+ export default class Slot<Props extends object> extends SvelteComponentTyped<SlotProps<Props>, SlotEvents<Props>, SlotSlots<Props>> {
25
+ }
26
+ export {};
package/index.d.ts ADDED
@@ -0,0 +1,38 @@
1
+ export * from '@agnos-ui/core';
2
+ export { Slot };
3
+ import Slot from './Slot.svelte';
4
+ export * from './utils';
5
+ export type { SlotContent, WidgetsConfig } from './utils';
6
+ import type { WidgetProps, WidgetState } from '@agnos-ui/core';
7
+ import type { AdaptSlotContentProps, AdaptWidgetSlots, WidgetPropsSlots } from './utils';
8
+ export type AccordionWidget = AdaptWidgetSlots<import('@agnos-ui/core').AccordionWidget>;
9
+ export type AccordionProps = WidgetProps<AccordionWidget>;
10
+ export type AccordionState = WidgetState<AccordionWidget>;
11
+ export type AccordionSlots = WidgetPropsSlots<AccordionProps>;
12
+ export type AccordionItemWidget = AdaptWidgetSlots<import('@agnos-ui/core').AccordionItemWidget>;
13
+ export type AccordionItemProps = WidgetProps<AccordionItemWidget>;
14
+ export type AccordionItemState = WidgetState<AccordionItemWidget>;
15
+ export type AccordionItemContext = AdaptSlotContentProps<import('@agnos-ui/core').AccordionItemContext>;
16
+ export type AlertWidget = AdaptWidgetSlots<import('@agnos-ui/core').AlertWidget>;
17
+ export type AlertProps = WidgetProps<AlertWidget>;
18
+ export type AlertState = WidgetState<AlertWidget>;
19
+ export type AlertSlots = WidgetPropsSlots<AlertProps>;
20
+ export type ModalWidget = AdaptWidgetSlots<import('@agnos-ui/core').ModalWidget>;
21
+ export type ModalProps = WidgetProps<ModalWidget>;
22
+ export type ModalState = WidgetState<ModalWidget>;
23
+ export type ModalContext = AdaptSlotContentProps<import('@agnos-ui/core').ModalContext>;
24
+ export type ModalSlots = WidgetPropsSlots<ModalProps>;
25
+ export type PaginationWidget = AdaptWidgetSlots<import('@agnos-ui/core').PaginationWidget>;
26
+ export type PaginationProps = WidgetProps<PaginationWidget>;
27
+ export type PaginationState = WidgetState<PaginationWidget>;
28
+ export type PaginationContext = AdaptSlotContentProps<import('@agnos-ui/core').PaginationContext>;
29
+ export type PaginationNumberContext = AdaptSlotContentProps<import('@agnos-ui/core').PaginationNumberContext>;
30
+ export type PaginationSlots = WidgetPropsSlots<PaginationProps>;
31
+ export type RatingWidget = AdaptWidgetSlots<import('@agnos-ui/core').RatingWidget>;
32
+ export type RatingProps = WidgetProps<RatingWidget>;
33
+ export type RatingState = WidgetState<RatingWidget>;
34
+ export type RatingSlots = WidgetPropsSlots<RatingProps>;
35
+ export type SelectWidget<Item> = AdaptWidgetSlots<import('@agnos-ui/core').SelectWidget<Item>>;
36
+ export type SelectProps<Item> = WidgetProps<SelectWidget<Item>>;
37
+ export type SelectState<Item> = WidgetState<SelectWidget<Item>>;
38
+ export type SelectSlots<Item> = WidgetPropsSlots<SelectProps<Item>>;
package/index.js ADDED
@@ -0,0 +1,4 @@
1
+ export * from '@agnos-ui/core';
2
+ export { Slot };
3
+ import Slot from './Slot.svelte';
4
+ export * from './utils';
package/package.json ADDED
@@ -0,0 +1,42 @@
1
+ {
2
+ "name": "@agnos-ui/svelte-headless",
3
+ "description": "Headless widget library for Svelte.",
4
+ "type": "module",
5
+ "main": "./index.js",
6
+ "module": "./index.js",
7
+ "types": "./index.d.ts",
8
+ "exports": {
9
+ ".": {
10
+ "types": "./index.d.ts",
11
+ "svelte": "./index.js"
12
+ }
13
+ },
14
+ "dependencies": {
15
+ "@agnos-ui/core": "0.0.1-alpha.1",
16
+ "@amadeus-it-group/tansu": "0.0.22"
17
+ },
18
+ "peerDependencies": {
19
+ "svelte": "*"
20
+ },
21
+ "sideEffects": false,
22
+ "version": "0.0.1-alpha.1",
23
+ "homepage": "https://amadeusitgroup.github.io/AgnosUI/latest/",
24
+ "bugs": "https://github.com/AmadeusITGroup/AgnosUI/issues",
25
+ "license": "MIT",
26
+ "repository": {
27
+ "type": "git",
28
+ "url": "https://github.com/AmadeusITGroup/AgnosUI.git",
29
+ "directory": "svelte/headless"
30
+ },
31
+ "keywords": [
32
+ "svelte",
33
+ "headless",
34
+ "components",
35
+ "widgets",
36
+ "accordion",
37
+ "alert",
38
+ "modal",
39
+ "pagination",
40
+ "rating"
41
+ ]
42
+ }
package/utils.d.ts ADDED
@@ -0,0 +1,77 @@
1
+ import type { SlotContent as CoreSlotContent, WidgetsConfig as CoreWidgetsConfig, Partial2Levels, Widget, WidgetFactory, WidgetProps, WidgetSlotContext, WidgetState, WidgetsConfigStore } from '@agnos-ui/core';
2
+ import type { ComponentType, SvelteComponent } from 'svelte';
3
+ export declare function createPatchChangedProps<T extends object>(patchFn: (arg: Partial<T>) => void): (props: Partial<T>) => void;
4
+ /**
5
+ * Key used in the Svelte context to get or set the widgets default configuration store.
6
+ */
7
+ export declare const widgetsDefaultConfigKey: unique symbol;
8
+ /**
9
+ * Creates in the Svelte context hierarchy a new widgets default configuration store that inherits from any widgets default configuration
10
+ * store already defined at an upper level in the Svelte context hierarchy.
11
+ * It contains its own set of widgets configuration properties that override the same properties form the parent configuration.
12
+ *
13
+ * @remarks
14
+ * The configuration is computed from the parent configuration in two steps:
15
+ * - first step: the parent configuration is transformed by the adaptParentConfig function (if specified).
16
+ * If adaptParentConfig is not specified, this step is skipped.
17
+ * - second step: the configuration from step 1 is merged (2-levels deep) with the own$ store. The own$ store initially contains
18
+ * an empty object (i.e. no property from the parent is overridden). It can be changed by calling set on the store returned by this function.
19
+ *
20
+ * @param adaptParentConfig - optional function that receives a 2-levels copy of the widgets default configuration
21
+ * defined at an upper level in the Svelte context hierarchy (or an empty object if there is none) and returns the widgets
22
+ * default configuration to be used.
23
+ * It is called only if the configuration is needed, and was not yet computed for the current value of the parent configuration.
24
+ * It is called in a tansu reactive context, so it can use any tansu store and will be called again if those stores change.
25
+ *
26
+ * @returns the resulting widgets default configuration store, which contains 3 additional properties that are stores:
27
+ * parent$, adaptedParent$ (containing the value computed after the first step), and own$ (that contains only overridding properties).
28
+ * The resulting store is writable, its set function is actually the set function of the own$ store.
29
+ *
30
+ * @example
31
+ * ```ts
32
+ * const widgetsConfig = createWidgetsDefaultConfig((parentConfig) => {
33
+ * // first step configuration: transforms the parent configuration
34
+ * parentConfig.rating = parentConfig.rating ?? {};
35
+ * parentConfig.rating.className = `${parentConfig.rating.className ?? ''} my-rating-extra-class`
36
+ * return parentConfig;
37
+ * });
38
+ * widgetsConfig.set({
39
+ * // second step configuration: overrides the parent configuration
40
+ * rating: {
41
+ * slotStar: MyCustomSlotStar
42
+ * }
43
+ * });
44
+ * ```
45
+ */
46
+ export declare const createWidgetsDefaultConfig: (adaptParentConfig?: ((config: Partial2Levels<WidgetsConfig>) => Partial2Levels<WidgetsConfig>) | undefined) => WidgetsConfigStore<WidgetsConfig>;
47
+ export declare function withPatchChangedProps<T extends {
48
+ patch: (arg: object) => void;
49
+ }>(api: T): T & {
50
+ patchChangedProps: T['patch'];
51
+ };
52
+ export declare const createEventDispatcher: <T extends object>() => import("svelte").EventDispatcher<{ [K in keyof T]: T[K] extends CustomEvent<infer U> ? U : never; }>;
53
+ export type WidgetPropsProps<Props extends object> = Partial<Omit<Props, `on${string}`>>;
54
+ export type WidgetPropsEvents<Props extends object> = {
55
+ [K in keyof Props & `on${string}` as K extends `on${infer U}` ? Uncapitalize<U> : never]: NonNullable<Props[K]> extends (arg: infer U) => void ? CustomEvent<U> : never;
56
+ };
57
+ export type WidgetPropsSlots<Props extends object> = {
58
+ [K in keyof Props & `slot${string}` as K extends `slot${infer U}` ? Uncapitalize<U> : never]: Props[K] extends SlotContent<infer U> ? U : never;
59
+ };
60
+ export type SlotsPresent<Props extends object> = {
61
+ [K in keyof Props & `slot${string}` as K extends `slot${infer U}` ? Uncapitalize<U> : never]: boolean;
62
+ };
63
+ export declare const callWidgetFactory: <W extends Widget<object, object, object, object, object>>(factory: WidgetFactory<W>, widgetName: keyof WidgetsConfig | null, slots: SlotsPresent<Omit<WidgetProps<W>, `slot${string}`> & { [K in keyof WidgetProps<W> & `slot${string}`]: WidgetProps<W>[K] extends CoreSlotContent<infer U extends object> ? SlotContent<AdaptSlotContentProps<U>> : WidgetProps<W>[K]; }>, defaultConfig?: Partial<Omit<WidgetProps<W>, `slot${string}`> & { [K in keyof WidgetProps<W> & `slot${string}`]: WidgetProps<W>[K] extends CoreSlotContent<infer U extends object> ? SlotContent<AdaptSlotContentProps<U>> : WidgetProps<W>[K]; }> | undefined) => AdaptWidgetSlots<W> & {
64
+ patchChangedProps: (parameters: Partial<AdaptPropsSlots<WidgetProps<W>>>) => void;
65
+ };
66
+ export declare const useSvelteSlot: unique symbol;
67
+ export type SlotSvelteComponent<Props extends object = object> = ComponentType<SvelteComponent<Props, any, Props extends WidgetSlotContext<infer U> ? WidgetPropsSlots<WidgetProps<U>> : any>>;
68
+ export type SlotContent<Props extends object = object> = CoreSlotContent<Props> | SlotSvelteComponent<Props> | typeof useSvelteSlot;
69
+ export type AdaptSlotContentProps<Props extends Record<string, any>> = Props extends WidgetSlotContext<infer U> ? WidgetSlotContext<AdaptWidgetSlots<U>> & AdaptPropsSlots<Omit<Props, keyof WidgetSlotContext<any>>> : AdaptPropsSlots<Props>;
70
+ export type AdaptPropsSlots<Props> = Omit<Props, `slot${string}`> & {
71
+ [K in keyof Props & `slot${string}`]: Props[K] extends CoreSlotContent<infer U> ? SlotContent<AdaptSlotContentProps<U>> : Props[K];
72
+ };
73
+ export type WidgetsConfig = {
74
+ [WidgetName in keyof CoreWidgetsConfig]: AdaptPropsSlots<CoreWidgetsConfig[WidgetName]>;
75
+ };
76
+ export type AdaptWidgetSlots<W extends Widget> = Widget<AdaptPropsSlots<WidgetProps<W>>, AdaptPropsSlots<WidgetState<W>>, W['api'], W['actions'], W['directives']>;
77
+ export declare const isSvelteComponent: <Props extends object>(content: SlotContent<Props>) => content is SlotSvelteComponent<Props>;
package/utils.js ADDED
@@ -0,0 +1,86 @@
1
+ import { createWidgetsConfig, findChangedProperties } from '@agnos-ui/core';
2
+ import { computed } from '@amadeus-it-group/tansu';
3
+ import { getContext, setContext, createEventDispatcher as svelteCreateEventDispatcher } from 'svelte';
4
+ export function createPatchChangedProps(patchFn) {
5
+ let previousProps = {};
6
+ return (props) => {
7
+ const changedProps = findChangedProperties(previousProps, props);
8
+ previousProps = props;
9
+ if (changedProps) {
10
+ patchFn(changedProps);
11
+ }
12
+ };
13
+ }
14
+ /**
15
+ * Key used in the Svelte context to get or set the widgets default configuration store.
16
+ */
17
+ export const widgetsDefaultConfigKey = Symbol('widgetsConfig');
18
+ /**
19
+ * Creates in the Svelte context hierarchy a new widgets default configuration store that inherits from any widgets default configuration
20
+ * store already defined at an upper level in the Svelte context hierarchy.
21
+ * It contains its own set of widgets configuration properties that override the same properties form the parent configuration.
22
+ *
23
+ * @remarks
24
+ * The configuration is computed from the parent configuration in two steps:
25
+ * - first step: the parent configuration is transformed by the adaptParentConfig function (if specified).
26
+ * If adaptParentConfig is not specified, this step is skipped.
27
+ * - second step: the configuration from step 1 is merged (2-levels deep) with the own$ store. The own$ store initially contains
28
+ * an empty object (i.e. no property from the parent is overridden). It can be changed by calling set on the store returned by this function.
29
+ *
30
+ * @param adaptParentConfig - optional function that receives a 2-levels copy of the widgets default configuration
31
+ * defined at an upper level in the Svelte context hierarchy (or an empty object if there is none) and returns the widgets
32
+ * default configuration to be used.
33
+ * It is called only if the configuration is needed, and was not yet computed for the current value of the parent configuration.
34
+ * It is called in a tansu reactive context, so it can use any tansu store and will be called again if those stores change.
35
+ *
36
+ * @returns the resulting widgets default configuration store, which contains 3 additional properties that are stores:
37
+ * parent$, adaptedParent$ (containing the value computed after the first step), and own$ (that contains only overridding properties).
38
+ * The resulting store is writable, its set function is actually the set function of the own$ store.
39
+ *
40
+ * @example
41
+ * ```ts
42
+ * const widgetsConfig = createWidgetsDefaultConfig((parentConfig) => {
43
+ * // first step configuration: transforms the parent configuration
44
+ * parentConfig.rating = parentConfig.rating ?? {};
45
+ * parentConfig.rating.className = `${parentConfig.rating.className ?? ''} my-rating-extra-class`
46
+ * return parentConfig;
47
+ * });
48
+ * widgetsConfig.set({
49
+ * // second step configuration: overrides the parent configuration
50
+ * rating: {
51
+ * slotStar: MyCustomSlotStar
52
+ * }
53
+ * });
54
+ * ```
55
+ */
56
+ export const createWidgetsDefaultConfig = (adaptParentConfig) => {
57
+ const parent$ = getContext(widgetsDefaultConfigKey);
58
+ const child$ = createWidgetsConfig(parent$, adaptParentConfig);
59
+ setContext(widgetsDefaultConfigKey, child$);
60
+ return child$;
61
+ };
62
+ export function withPatchChangedProps(api) {
63
+ const patchChangedProps = createPatchChangedProps(api.patch);
64
+ return { ...api, patchChangedProps };
65
+ }
66
+ export const createEventDispatcher = () => svelteCreateEventDispatcher();
67
+ const getContextWidgetConfig = (widgetName, slots, defaultConfig) => {
68
+ const config$ = widgetName ? getContext(widgetsDefaultConfigKey) : undefined;
69
+ const processedSlots = {};
70
+ for (const [name, present] of Object.entries(slots)) {
71
+ if (present) {
72
+ processedSlots[`slot${name[0].toUpperCase()}${name.substring(1)}`] = useSvelteSlot;
73
+ }
74
+ }
75
+ return computed(() => ({ ...defaultConfig, ...(widgetName ? config$?.()[widgetName] : undefined), ...processedSlots }));
76
+ };
77
+ export const callWidgetFactory = (factory, widgetName, slots, defaultConfig) => withPatchChangedProps(factory(getContextWidgetConfig(widgetName, slots, defaultConfig)));
78
+ export const useSvelteSlot = Symbol('useSvelteSlot');
79
+ export const isSvelteComponent = (content) => {
80
+ // in prod mode, a svelte component has $set on its prototype
81
+ // in dev mode with hmr (hot module reload), a svelte component has nothing on its prototype, but its name starts with Proxy<
82
+ return ((typeof content === 'function' && !!content.prototype && (content.prototype.$set || /^Proxy</.test(content.name))) ||
83
+ // when using Server Side Rendering, a svelte component is an object with a render function:
84
+ // (cf https://svelte.dev/docs/server-side-component-api)
85
+ typeof content?.render === 'function');
86
+ };