@agnos-ui/core
Advanced tools
Comparing version 0.1.1 to 0.2.0
@@ -6,3 +6,3 @@ import type { TransitionFn } from '../../services/transitions/baseTransitions'; | ||
/** | ||
* If `true`, only one item at the time can stay open. | ||
* If `true`, only one accordion-item at the time can stay open. | ||
*/ | ||
@@ -29,3 +29,3 @@ closeOthers: boolean; | ||
/** | ||
* If `true`, the content of the accordion-item collapse will be removed from the DOM. It will be just hidden otherwise. | ||
* If `true`, the accordion-item body container will be removed from the DOM when the accordion-item is collapsed. It will be just hidden otherwise. | ||
* | ||
@@ -55,3 +55,3 @@ * It is a prop of the accordion-item. | ||
/** | ||
* The transition to use for the accordion-item collapse when is toggled. | ||
* The transition to use for the accordion-item body-container when the accordion-item is toggled. | ||
* | ||
@@ -83,4 +83,5 @@ * It is a prop of the accordion-item. | ||
* Structure of the accordion-item. The default item structure is: accordion-item | ||
* contains accordion header and accordion collapse; the accordion header contains the accordion button | ||
* (that contains `slotItemHeader`), while the accordion collapse contains the accordion body (that contains slotItemBody). | ||
* contains accordion header and accordion-item body container; the accordion header contains the accordion button | ||
* (that contains `slotItemHeader`), while the accordion-item body container contains the accordion body (that contains `slotItemBody`). | ||
* The itemTransition it applied on this element. | ||
* | ||
@@ -103,3 +104,3 @@ * It is a prop of the accordion-item. | ||
/** | ||
* Classes to add on the accordion-item DOM element. | ||
* CSS classes to add on the accordion-item DOM element. | ||
* | ||
@@ -110,3 +111,3 @@ * It is a prop of the accordion-item. | ||
/** | ||
* Classes to add on the accordion-item header DOM element. | ||
* CSS classes to add on the accordion-item header DOM element. | ||
* | ||
@@ -117,3 +118,3 @@ * It is a prop of the accordion-item. | ||
/** | ||
* Classes to add on the accordion-item toggle button DOM element. | ||
* CSS classes to add on the accordion-item toggle button DOM element. | ||
* | ||
@@ -124,9 +125,10 @@ * It is a prop of the accordion-item. | ||
/** | ||
* Classes to add on the accordion-item collapse DOM element. | ||
* CSS classes to add on the accordion-item body container DOM element. | ||
* The accordion-item body container is the DOM element on what the itemTransition is applied. | ||
* | ||
* It is a prop of the accordion-item. | ||
*/ | ||
itemCollapseClass: string; | ||
itemBodyContainerClass: string; | ||
/** | ||
* Classes to add on the accordion-item body DOM element. | ||
* CSS classes to add on the accordion-item body DOM element. | ||
* | ||
@@ -255,24 +257,26 @@ * It is a prop of the accordion-item. | ||
* Structure of the accordion-item. The default item structure is: accordion-item | ||
* contains accordion header and accordion collapse; the accordion header contains the accordion button | ||
* (that contains `slotItemHeader`), while the accordion collapse contains the accordion body (that contains slotItemBody). | ||
* contains accordion header and accordion-item body container; the accordion header contains the accordion button | ||
* (that contains `slotItemHeader`), while the accordion-item body container contains the accordion body (that contains `slotItemBody`). | ||
* The itemTransition it applied on this element. | ||
*/ | ||
slotItemStructure: SlotContent<AccordionItemContext>; | ||
/** | ||
* Classes to add on the accordion-item DOM element. | ||
* CSS classes to add on the accordion-item DOM element. | ||
*/ | ||
itemClass: string; | ||
/** | ||
* Classes to add on the accordion-item header DOM element. | ||
* CSS classes to add on the accordion-item header DOM element. | ||
*/ | ||
itemHeaderClass: string; | ||
/** | ||
* Classes to add on the accordion-item collapse DOM element. | ||
* CSS classes to add on the accordion-item collapse DOM element. | ||
*/ | ||
itemButtonClass: string; | ||
/** | ||
* Classes to add on the accordion-item collapse DOM element. | ||
* CSS classes to add on the accordion-item body container DOM element. | ||
* The accordion-item body container is the DOM element on what the itemTransition is applied. | ||
*/ | ||
itemCollapseClass: string; | ||
itemBodyContainerClass: string; | ||
/** | ||
* Classes to add on the accordion-item body DOM element. | ||
* CSS classes to add on the accordion-item body DOM element. | ||
*/ | ||
@@ -291,7 +295,7 @@ itemBodyClass: string; | ||
/** | ||
* The transition to use for the accordion-item collapse when is toggled. | ||
* The transition to use for the accordion-item body-container when the accordion-item is toggled. | ||
*/ | ||
itemTransition: TransitionFn; | ||
/** | ||
* If `true`, the content of the accordion-item collapse will be removed from the DOM. It will be just hidden otherwise. | ||
* If `true`, the accordion-item body container will be removed from the DOM when the accordion-item is collapsed. It will be just hidden otherwise. | ||
*/ | ||
@@ -298,0 +302,0 @@ itemDestroyOnHide: boolean; |
@@ -8,6 +8,3 @@ import { stateStores, writablesForProps, normalizeConfigStores, mergeConfigStores } from '../../utils/stores'; | ||
import { bindDirectiveNoArg, directiveSubscribe, registrationArray } from '../../utils/directive'; | ||
let itemId = 0; | ||
function getItemId() { | ||
return `accordion-item-${itemId++}`; | ||
} | ||
import { generateId } from '../../utils/internal/dom'; | ||
function adjustItemsCloseOthers(items, openItems, oldOpen) { | ||
@@ -56,3 +53,3 @@ let keepOpen; | ||
itemButtonClass: '', | ||
itemCollapseClass: '', | ||
itemBodyContainerClass: '', | ||
itemBodyClass: '', | ||
@@ -76,3 +73,3 @@ }; | ||
itemButtonClass: defaultAccordionConfig.itemButtonClass, | ||
itemCollapseClass: defaultAccordionConfig.itemCollapseClass, | ||
itemBodyContainerClass: defaultAccordionConfig.itemBodyContainerClass, | ||
itemBodyClass: defaultAccordionConfig.itemBodyClass, | ||
@@ -105,3 +102,3 @@ itemHeadingTag: defaultAccordionConfig.itemHeadingTag, | ||
itemButtonClass: typeString, | ||
itemCollapseClass: typeString, | ||
itemBodyContainerClass: typeString, | ||
itemBodyClass: typeString, | ||
@@ -123,3 +120,3 @@ itemHeadingTag: typeString, | ||
itemButtonClass: typeString, | ||
itemCollapseClass: typeString, | ||
itemBodyContainerClass: typeString, | ||
itemBodyClass: typeString, | ||
@@ -131,3 +128,3 @@ itemHeadingTag: typeString, | ||
const initDone$ = writable(false); | ||
const _autoItemId$ = computed(() => getItemId()); | ||
const _autoItemId$ = computed(() => generateId()); | ||
const itemId$ = computed(() => _dirtyItemId$() || _autoItemId$()); | ||
@@ -192,3 +189,3 @@ const shouldBeInDOM$ = computed(() => itemDestroyOnHide$() === false || !itemTransition.state$().hidden); | ||
export function createAccordion(config) { | ||
const [{ closeOthers$, onShown$, onHidden$, itemId$, itemAnimation$, itemClass$, itemDisabled$, itemVisible$, itemTransition$, itemDestroyOnHide$, itemBodyClass$, itemButtonClass$, itemCollapseClass$, itemHeaderClass$, itemHeadingTag$, onItemVisibleChange$, onItemHidden$, onItemShown$, slotItemStructure$, slotItemBody$, slotItemHeader$, ...stateProps }, patch,] = writablesForProps(defaultAccordionConfig, config, configAccordionValidator); | ||
const [{ closeOthers$, onShown$, onHidden$, itemId$, itemAnimation$, itemClass$, itemDisabled$, itemVisible$, itemTransition$, itemDestroyOnHide$, itemBodyClass$, itemButtonClass$, itemBodyContainerClass$, itemHeaderClass$, itemHeadingTag$, onItemVisibleChange$, onItemHidden$, onItemShown$, slotItemStructure$, slotItemBody$, slotItemHeader$, ...stateProps }, patch,] = writablesForProps(defaultAccordionConfig, config, configAccordionValidator); | ||
const accordionItemConfig = { | ||
@@ -204,3 +201,3 @@ itemId: itemId$, | ||
itemButtonClass: itemButtonClass$, | ||
itemCollapseClass: itemCollapseClass$, | ||
itemBodyContainerClass: itemBodyContainerClass$, | ||
itemHeaderClass: itemHeaderClass$, | ||
@@ -207,0 +204,0 @@ itemHeadingTag: itemHeadingTag$, |
@@ -12,5 +12,5 @@ import type { CommonAlertApi, CommonAlertDirectives, CommonAlertProps, CommonAlertState } from './common'; | ||
} | ||
export interface AlertState extends ExtendWidgetAdaptSlotWidgetProps<CommonAlertState, AlertExtraProps> { | ||
export interface AlertState extends ExtendWidgetAdaptSlotWidgetProps<CommonAlertState, AlertExtraProps, object> { | ||
} | ||
export interface AlertProps extends ExtendWidgetAdaptSlotWidgetProps<CommonAlertProps, AlertExtraProps> { | ||
export interface AlertProps extends ExtendWidgetAdaptSlotWidgetProps<CommonAlertProps, AlertExtraProps, object> { | ||
} | ||
@@ -17,0 +17,0 @@ export interface AlertApi extends CommonAlertApi { |
@@ -90,2 +90,10 @@ import type { PropsConfig, Widget, SlotContent, WidgetSlotContext } from '../../types'; | ||
/** | ||
* The template to use for the structure of the pagination component | ||
* The default structure uses {@link PaginationCommonPropsAndState.slotEllipsis|slotEllipsis}, {@link PaginationCommonPropsAndState.slotFirst|slotFirst}, | ||
* {@link PaginationCommonPropsAndState.slotPrevious|slotPrevious}, {@link PaginationCommonPropsAndState.slotNext|slotNext}, | ||
* {@link PaginationCommonPropsAndState.slotLast|slotLast}, {@link PaginationCommonPropsAndState.slotPages|slotPages}, | ||
* {@link PaginationCommonPropsAndState.slotNumberLabel|slotNumberLabel}, | ||
*/ | ||
slotStructure: SlotContent<PaginationContext>; | ||
/** | ||
* The template to use for the ellipsis slot | ||
@@ -205,3 +213,23 @@ * for I18n, we suggest to use the global configuration | ||
ariaPageLabel: (processPage: number, pageCount: number) => string; | ||
/** | ||
* Factory function providing the href for a "Page" page anchor, | ||
* based on the current page number | ||
* @param pageNumber - The index to use in the link | ||
* @defaultValue | ||
* ```ts | ||
* (_pageNumber) => '#' | ||
* ``` | ||
*/ | ||
pageLink: (pageNumber: number) => string; | ||
} | ||
export interface DirectionsHrefs { | ||
/** | ||
* The href for the 'Previous' navigation link | ||
*/ | ||
previous: string; | ||
/** | ||
* The href for the 'Next' direction link | ||
*/ | ||
next: string; | ||
} | ||
export interface PaginationState extends PaginationCommonPropsAndState { | ||
@@ -228,2 +256,6 @@ /** | ||
pagesLabel: string[]; | ||
/** The hrefs for each "Page" page link */ | ||
pagesHrefs: string[]; | ||
/** The hrefs for the direction links */ | ||
directionsHrefs: DirectionsHrefs; | ||
} | ||
@@ -235,19 +267,19 @@ export interface PaginationActions { | ||
*/ | ||
select(page: number): void; | ||
select(page: number, event?: MouseEvent): void; | ||
/** | ||
* To "go" to the first page | ||
*/ | ||
first(): void; | ||
first(event?: MouseEvent): void; | ||
/** | ||
* To "go" to the previous page | ||
*/ | ||
previous(): void; | ||
previous(event?: MouseEvent): void; | ||
/** | ||
* To "go" to the next page | ||
*/ | ||
next(): void; | ||
next(event?: MouseEvent): void; | ||
/** | ||
* To "go" to the last page | ||
*/ | ||
last(): void; | ||
last(event?: MouseEvent): void; | ||
} | ||
@@ -329,2 +361,12 @@ export interface PaginationApi { | ||
/** | ||
* Factory function providing the href for a "Page" page anchor, | ||
* based on the current page number | ||
* @param pageNumber - The index to use in the link | ||
* @defaultValue | ||
* ```ts | ||
* (_pageNumber) => '#' | ||
* ``` | ||
*/ | ||
pageLink: (pageNumber: number) => string; | ||
/** | ||
* The current page. | ||
@@ -402,2 +444,10 @@ * | ||
/** | ||
* The template to use for the structure of the pagination component | ||
* The default structure uses {@link PaginationCommonPropsAndState.slotEllipsis|slotEllipsis}, {@link PaginationCommonPropsAndState.slotFirst|slotFirst}, | ||
* {@link PaginationCommonPropsAndState.slotPrevious|slotPrevious}, {@link PaginationCommonPropsAndState.slotNext|slotNext}, | ||
* {@link PaginationCommonPropsAndState.slotLast|slotLast}, {@link PaginationCommonPropsAndState.slotPages|slotPages}, | ||
* {@link PaginationCommonPropsAndState.slotNumberLabel|slotNumberLabel}, | ||
*/ | ||
slotStructure: SlotContent<PaginationContext>; | ||
/** | ||
* The template to use for the ellipsis slot | ||
@@ -404,0 +454,0 @@ * for I18n, we suggest to use the global configuration |
import { computed } from '@amadeus-it-group/tansu'; | ||
import { INVALID_VALUE } from '../../types'; | ||
import { bindableDerived, stateStores, writablesForProps } from '../../utils/stores'; | ||
import { bindableProp, stateStores, writablesForProps } from '../../utils/stores'; | ||
import { clamp, isNumber } from '../../utils/internal/checks'; | ||
import { typeBoolean, typeFunction, typeNumber, typeString } from '../../utils/writables'; | ||
import { noop } from '../../utils/internal/func'; | ||
const PAGE_LINK_DEFAULT = '#'; | ||
const defaultConfig = { | ||
@@ -38,3 +39,5 @@ page: 1, | ||
slotPages: undefined, | ||
slotStructure: undefined, | ||
slotNumberLabel: ({ displayedPage }) => `${displayedPage}`, | ||
pageLink: (_page) => PAGE_LINK_DEFAULT, | ||
}; | ||
@@ -66,2 +69,3 @@ /** | ||
className: typeString, | ||
pageLink: typeFunction, | ||
}; | ||
@@ -78,3 +82,3 @@ /** | ||
// clean inputs with value validation: | ||
collectionSize$, pageSize$, onPageChange$, pagesFactory$, ariaPageLabel$, ...stateProps }, patch,] = writablesForProps(defaultConfig, config, configValidator); | ||
collectionSize$, pageSize$, onPageChange$, pagesFactory$, ariaPageLabel$, pageLink$, ...stateProps }, patch,] = writablesForProps(defaultConfig, config, configValidator); | ||
// computed | ||
@@ -91,3 +95,3 @@ // nb total of Pages. | ||
// current page | ||
const page$ = bindableDerived(onPageChange$, [_dirtyPage$, pageCount$], ([dirtyPage, pageCount]) => clamp(dirtyPage, pageCount, 1)); | ||
const page$ = bindableProp(_dirtyPage$, onPageChange$, (dirtyPage) => clamp(dirtyPage, pageCount$(), 1)); | ||
const pages$ = computed(() => pagesFactory$()(page$(), pageCount$())); | ||
@@ -101,2 +105,37 @@ const nextDisabled$ = computed(() => page$() === pageCount$() || stateProps.disabled$()); | ||
}); | ||
const pagesHrefs$ = computed(() => { | ||
const pageLinkFactory = pageLink$(); | ||
const pageCount = pageCount$(); | ||
return Array.from({ length: pageCount }, (_, index) => pageLinkFactory(index + 1)); | ||
}); | ||
const directionsHrefs$ = computed(() => { | ||
const pagesHrefs = pagesHrefs$(); | ||
const pageIndex = page$() - 1; | ||
return { | ||
previous: pagesHrefs.at(pageIndex > 0 ? pageIndex - 1 : 0), | ||
next: pagesHrefs.at(pageIndex + 1) ?? pagesHrefs.at(-1), | ||
}; | ||
}); | ||
/** | ||
* Stop event propagation when href is the default value; | ||
* Update page number when navigation is in the same tab and stop the event propagation; | ||
* For navigations outside current browser tab let the default behavior, without updating the page number; | ||
* @param pageNumber current page number | ||
* @param event UI event triggered when page changed | ||
* @param pageNavigationHandler change handler callback for navigation elements | ||
*/ | ||
function handleNavigation(pageNumber, event, pageNavigationHandler) { | ||
if (pagesHrefs$()[pageNumber - 1] === PAGE_LINK_DEFAULT) { | ||
event?.preventDefault(); | ||
} | ||
if (!event || !(event.ctrlKey || event.metaKey)) { | ||
event?.preventDefault(); | ||
if (pageNavigationHandler) { | ||
page$.update(pageNavigationHandler); | ||
} | ||
else { | ||
page$.set(pageNumber); | ||
} | ||
} | ||
} | ||
return { | ||
@@ -110,2 +149,4 @@ ...stateStores({ | ||
pagesLabel$, | ||
pagesHrefs$, | ||
directionsHrefs$, | ||
...stateProps, | ||
@@ -119,29 +160,34 @@ }), | ||
* Value is normalized between 1 and the number of page | ||
* @param event UI event that triggered the select | ||
*/ | ||
select(pageNumber) { | ||
patch({ page: pageNumber }); | ||
select(pageNumber, event) { | ||
handleNavigation(pageNumber, event); | ||
}, | ||
/** | ||
* Select the first page | ||
* @param event Event triggering the action | ||
*/ | ||
first() { | ||
patch({ page: 1 }); | ||
first(event) { | ||
handleNavigation(1, event); | ||
}, | ||
/** | ||
* Select the previous page | ||
* @param event Event triggering the action | ||
*/ | ||
previous() { | ||
patch({ page: page$() - 1 }); | ||
previous(event) { | ||
handleNavigation(page$() - 1, event, (page) => page - 1); | ||
}, | ||
/** | ||
* Select the next page | ||
* @param event Event triggering the action | ||
*/ | ||
next() { | ||
patch({ page: page$() + 1 }); | ||
next(event) { | ||
handleNavigation(page$() + 1, event, (page) => page + 1); | ||
}, | ||
/** | ||
* Select the last page | ||
* @param event Event triggering the action | ||
*/ | ||
last() { | ||
patch({ page: pageCount$() }); | ||
last(event) { | ||
handleNavigation(pageCount$(), event); | ||
}, | ||
@@ -148,0 +194,0 @@ }, |
@@ -25,5 +25,5 @@ import type { PropsConfig, SlotContent, Widget, WidgetSlotContext } from '../../types'; | ||
/** | ||
* Global template for the Progressbar content. | ||
* Global template for the Progressbar. | ||
*/ | ||
slotContent: SlotContent<ProgressbarContext>; | ||
slotStructure: SlotContent<ProgressbarContext>; | ||
/** | ||
@@ -30,0 +30,0 @@ * Label of the progress. |
@@ -11,3 +11,3 @@ import { clamp } from '../../utils/internal/checks'; | ||
className: '', | ||
slotContent: undefined, | ||
slotStructure: undefined, | ||
slotDefault: undefined, | ||
@@ -14,0 +14,0 @@ height: '', |
import { computed, writable } from '@amadeus-it-group/tansu'; | ||
import { INVALID_VALUE } from '../../types'; | ||
import { bindableDerived, stateStores, writablesForProps } from '../../utils/stores'; | ||
import { bindableProp, stateStores, writablesForProps } from '../../utils/stores'; | ||
import { clamp, isNumber } from '../../utils/internal/checks'; | ||
@@ -60,3 +60,3 @@ import { typeBoolean, typeFunction, typeNumber, typeString } from '../../utils/writables'; | ||
const tabindex$ = computed(() => (disabled$() ? -1 : _dirtyTabindex$())); | ||
const rating$ = bindableDerived(onRatingChange$, [_dirtyRating$, maxRating$], ([dirtyRating, maxRating]) => clamp(dirtyRating, maxRating)); | ||
const rating$ = bindableProp(_dirtyRating$, onRatingChange$, (dirtyRating) => clamp(dirtyRating, maxRating$())); | ||
// internal inputs | ||
@@ -67,5 +67,4 @@ const _hoveredRating$ = writable(0); | ||
const visibleRating$ = computed(() => { | ||
const rating = rating$(); // call rating unconditionnally (for the bindableDerived to stay active) | ||
const hoveredRating = _hoveredRating$(); | ||
return hoveredRating !== 0 ? hoveredRating : rating; | ||
return hoveredRating !== 0 ? hoveredRating : rating$(); | ||
}); | ||
@@ -94,3 +93,3 @@ const ariaValueText$ = computed(() => ariaValueTextFn$()(visibleRating$(), maxRating$())); | ||
if (isInteractive$() && index > 0 && index <= maxRating$()) { | ||
patch({ rating: rating$() === index && resettable$() ? 0 : index }); | ||
rating$.update((rating) => (rating === index && resettable$() ? 0 : index)); | ||
} | ||
@@ -116,15 +115,15 @@ }, | ||
case 'ArrowDown': | ||
patch({ rating: rating$() - 1 }); | ||
rating$.update((rating) => rating - 1); | ||
break; | ||
case 'ArrowRight': | ||
case 'ArrowUp': | ||
patch({ rating: rating$() + 1 }); | ||
rating$.update((rating) => rating + 1); | ||
break; | ||
case 'Home': | ||
case 'PageDown': | ||
patch({ rating: 0 }); | ||
rating$.set(0); | ||
break; | ||
case 'End': | ||
case 'PageUp': | ||
patch({ rating: maxRating$() }); | ||
rating$.set(maxRating$()); | ||
break; | ||
@@ -131,0 +130,0 @@ default: |
import type { Placement } from '@floating-ui/dom'; | ||
import type { FloatingUI } from '../../services/floatingUI'; | ||
import type { HasFocus } from '../../services/focustrack'; | ||
import type { PropsConfig, SlotContent, Widget, WidgetSlotContext } from '../../types'; | ||
import type { Directive, PropsConfig, SlotContent, Widget, WidgetSlotContext } from '../../types'; | ||
import type { WidgetsCommonPropsAndState } from '../commonProps'; | ||
@@ -84,2 +84,8 @@ /** | ||
/** | ||
* Retrieves navigable elements within an HTML element containing badges and the input. | ||
* | ||
* @param node - HTMLElement that contains the badges and the input | ||
*/ | ||
navSelector(node: HTMLElement): NodeListOf<HTMLSpanElement | HTMLInputElement>; | ||
/** | ||
* Callback called dropdown open state change | ||
@@ -168,23 +174,2 @@ * @param isOpen - updated open state | ||
/** | ||
* Focus the provided item among the selected list. | ||
* The focus feature is designed to know what item must be focused in the UI, i.e. among the badge elements. | ||
*/ | ||
focus(item: Item): void; | ||
/** | ||
* Focus the first element | ||
*/ | ||
focusFirst(): void; | ||
/** | ||
* Focus the previous element. If no element was focused before the call, nothing happens. | ||
*/ | ||
focusPrevious(): void; | ||
/** | ||
* Focus the next element. If no element was focused before the call, nothing happens. | ||
*/ | ||
focusNext(): void; | ||
/** | ||
* Focus the last element. If no element was focused before the call, nothing happens. | ||
*/ | ||
focusLast(): void; | ||
/** | ||
* Select the provided item. | ||
@@ -233,4 +218,8 @@ * The selected list is used to | ||
referenceDirective: FloatingUI['directives']['referenceDirective']; | ||
/** | ||
* A directive to be applied to the element that contains the badges and the input | ||
*/ | ||
inputContainerDirective: Directive; | ||
} | ||
export interface SelectActions { | ||
export interface SelectActions<Item> { | ||
/** | ||
@@ -243,8 +232,19 @@ * Method to be plugged to on the 'input' event. The input text will be used as the filter text. | ||
/** | ||
* Method to be plugged to on an keydown event, in order to control the keyboard interactions with the highlighted item. | ||
* Method to be attached to the node element to close a badge on click. | ||
*/ | ||
onRemoveBadgeClick: (event: MouseEvent, item: Item) => void; | ||
/** | ||
* Method to be plugged to on an keydown event of the main input, in order to control the keyboard interactions with the highlighted item. | ||
* It manages arrow keys to move the highlighted item, or enter to toggle the item. | ||
*/ | ||
onInputKeydown: (e: any) => void; | ||
onInputKeydown: (event: KeyboardEvent) => void; | ||
/** | ||
* Method to be plugged to on an keydown event of a badge container, in order to manage main actions on badges. | ||
* | ||
* @param event - keyboard event | ||
* @param item - corresponding item | ||
*/ | ||
onBadgeKeydown: (event: KeyboardEvent, item: Item) => void; | ||
} | ||
export type SelectWidget<Item> = Widget<SelectProps<Item>, SelectState<Item>, SelectApi<Item>, SelectActions, SelectDirectives>; | ||
export type SelectWidget<Item> = Widget<SelectProps<Item>, SelectState<Item>, SelectApi<Item>, SelectActions<Item>, SelectDirectives>; | ||
export declare const defaultConfig: SelectProps<any>; | ||
@@ -271,2 +271,8 @@ /** | ||
/** | ||
* Retrieves navigable elements within an HTML element containing badges and the input. | ||
* | ||
* @param node - HTMLElement that contains the badges and the input | ||
*/ | ||
navSelector(node: HTMLElement): NodeListOf<HTMLInputElement | HTMLSpanElement>; | ||
/** | ||
* Callback called dropdown open state change | ||
@@ -273,0 +279,0 @@ * @param isOpen - updated open state |
@@ -1,7 +0,10 @@ | ||
import { asWritable, computed, writable } from '@amadeus-it-group/tansu'; | ||
import { asWritable, batch, computed, writable } from '@amadeus-it-group/tansu'; | ||
import { autoPlacement, offset, size } from '@floating-ui/dom'; | ||
import { createFloatingUI } from '../../services/floatingUI'; | ||
import { createHasFocus } from '../../services/focustrack'; | ||
import { createNavManager } from '../../services/navManager'; | ||
import { bindDirective } from '../../utils/directive'; | ||
import { generateId } from '../../utils/internal/dom'; | ||
import { noop } from '../../utils/internal/func'; | ||
import { bindableDerived, stateStores, writablesForProps } from '../../utils/stores'; | ||
import { bindableDerived, bindableProp, stateStores, writablesForProps } from '../../utils/stores'; | ||
const defaultItemId = (item) => '' + item; | ||
@@ -17,2 +20,3 @@ export const defaultConfig = { | ||
selected: [], | ||
navSelector: (node) => node.querySelectorAll('.au-select-badge,input'), | ||
itemIdFn: defaultItemId, | ||
@@ -44,5 +48,6 @@ onOpenChange: noop, | ||
// Props | ||
const [{ open$: _dirtyOpen$, filterText$: _dirtyFilterText$, items$, itemIdFn$, onOpenChange$, onFilterTextChange$, onSelectedChange$, allowedPlacements$, ...stateProps }, patch,] = writablesForProps(defaultConfig, config); | ||
const [{ id$: _dirtyId$, open$: _dirtyOpen$, filterText$: _dirtyFilterText$, items$, itemIdFn$, onOpenChange$, onFilterTextChange$, onSelectedChange$, allowedPlacements$, navSelector$, ...stateProps }, patch,] = writablesForProps(defaultConfig, config); | ||
const { selected$ } = stateProps; | ||
const filterText$ = bindableDerived(onFilterTextChange$, [_dirtyFilterText$]); | ||
const id$ = computed(() => _dirtyId$() ?? generateId()); | ||
const filterText$ = bindableProp(_dirtyFilterText$, onFilterTextChange$); | ||
const { hasFocus$, directive: hasFocusDirective } = createHasFocus(); | ||
@@ -117,4 +122,27 @@ const open$ = bindableDerived(onOpenChange$, [_dirtyOpen$, hasFocus$], ([_dirtyOpen, hasFocus]) => _dirtyOpen && hasFocus); | ||
}); | ||
const { directive: navDirective, refreshElements, focusFirst, focusLast, focusLeft, focusRight } = createNavManager(); | ||
const navManagerConfig$ = computed(() => ({ | ||
keys: { | ||
Home: focusFirst, | ||
End: focusLast, | ||
ArrowLeft: focusLeft, | ||
ArrowRight: focusRight, | ||
}, | ||
selector: navSelector$(), | ||
})); | ||
const onRemoveBadge = (event, item) => { | ||
const referenceElement = event.target; | ||
refreshElements(); | ||
widget.api.unselect(item); | ||
// Waiting for refresh by the framework, to have the elements inside or outside the dom | ||
if (referenceElement instanceof HTMLElement) { | ||
setTimeout(() => { | ||
focusLeft({ event, referenceElement }) || focusRight({ event, referenceElement }); | ||
}); | ||
} | ||
event.preventDefault(); | ||
}; | ||
const widget = { | ||
...stateStores({ | ||
id$, | ||
visibleItems$, | ||
@@ -187,21 +215,6 @@ highlighted$, | ||
}, | ||
focus(item) { | ||
// FIXME: not implemented yet! | ||
}, | ||
focusFirst() { | ||
// FIXME: not implemented yet! | ||
}, | ||
focusPrevious() { | ||
// FIXME: not implemented yet! | ||
}, | ||
focusNext() { | ||
// FIXME: not implemented yet! | ||
}, | ||
focusLast() { | ||
// FIXME: not implemented yet! | ||
}, | ||
open: () => widget.api.toggle(true), | ||
close: () => widget.api.toggle(false), | ||
toggle(isOpen) { | ||
_dirtyOpen$.update((value) => (isOpen != null ? isOpen : !value)); | ||
open$.update((value) => (isOpen != null ? isOpen : !value)); | ||
}, | ||
@@ -213,2 +226,3 @@ }, | ||
referenceDirective, | ||
inputContainerDirective: bindDirective(navDirective, navManagerConfig$), | ||
}, | ||
@@ -218,7 +232,10 @@ actions: { | ||
const value = target.value; | ||
patch({ | ||
open: value != null && value !== '', | ||
filterText: value, | ||
batch(() => { | ||
open$.set(value != null && value !== ''); | ||
filterText$.set(value); | ||
}); | ||
}, | ||
onRemoveBadgeClick(event, item) { | ||
onRemoveBadge(event, item); | ||
}, | ||
onInputKeydown(e) { | ||
@@ -260,3 +277,3 @@ const { ctrlKey, key } = e; | ||
case 'Escape': | ||
_dirtyOpen$.set(false); | ||
open$.set(false); | ||
break; | ||
@@ -270,2 +287,16 @@ default: | ||
}, | ||
onBadgeKeydown(event, item) { | ||
let keyManaged = false; | ||
switch (event.key) { | ||
case 'Backspace': | ||
case 'Delete': { | ||
onRemoveBadge(event, item); | ||
keyManaged = true; | ||
break; | ||
} | ||
} | ||
if (keyManaged) { | ||
event.preventDefault(); | ||
} | ||
}, | ||
}, | ||
@@ -272,0 +303,0 @@ }; |
import type { WidgetsCommonPropsAndState } from '../commonProps'; | ||
import type { Directive, PropsConfig, Widget } from '../../types'; | ||
import type { Directive, PropsConfig, SlotContent, Widget, WidgetSlotContext } from '../../types'; | ||
export type SliderContext = WidgetSlotContext<SliderWidget>; | ||
export type SliderSlotLabelContext = SliderContext & { | ||
value: number; | ||
}; | ||
export type SliderSlotHandleContext = SliderContext & { | ||
item: SliderHandle; | ||
}; | ||
export interface ProgressDisplayOptions { | ||
@@ -39,2 +46,20 @@ /** | ||
} | ||
export interface SliderHandle { | ||
/** | ||
* Value of the handle | ||
*/ | ||
value: number; | ||
/** | ||
* Handle id | ||
*/ | ||
id: number; | ||
/** | ||
* ariaLabel of the handle | ||
*/ | ||
ariaLabel: string; | ||
/** | ||
* ariaValueText of the handle | ||
*/ | ||
ariaValueText: string; | ||
} | ||
export interface SliderCommonPropsAndState extends WidgetsCommonPropsAndState { | ||
@@ -81,2 +106,14 @@ /** | ||
rtl: boolean; | ||
/** | ||
* Slot to change the default display of the slider | ||
*/ | ||
slotStructure: SlotContent<SliderContext>; | ||
/** | ||
* Slot to change the default labels of the slider | ||
*/ | ||
slotLabel: SlotContent<SliderSlotLabelContext>; | ||
/** | ||
* Slot to change the handlers | ||
*/ | ||
slotHandle: SlotContent<SliderSlotHandleContext>; | ||
} | ||
@@ -111,7 +148,3 @@ export interface SliderState extends SliderCommonPropsAndState { | ||
*/ | ||
sortedHandles: { | ||
value: number; | ||
id: number; | ||
ariaLabel: string; | ||
}[]; | ||
sortedHandles: SliderHandle[]; | ||
/** | ||
@@ -125,2 +158,7 @@ * Array of objects representing progress display options | ||
handleDisplayOptions: HandleDisplayOptions[]; | ||
/** | ||
* Check if the slider is interactable, meaning it is not disabled or readonly | ||
* TODO: rename to `interactive`, issue #510 | ||
*/ | ||
isInteractable: boolean; | ||
} | ||
@@ -136,2 +174,9 @@ export interface SliderProps extends SliderCommonPropsAndState { | ||
/** | ||
* Return the value for the 'aria-valuetext' attribute for the handle | ||
* @param value - value of the handle | ||
* @param sortedIndex - index of the handle in the sorted list | ||
* @param index - index of the handle in the original list | ||
*/ | ||
ariaValueText: (value: number, sortedIndex: number, index: number) => string; | ||
/** | ||
* An event emitted when slider values are changed | ||
@@ -198,2 +243,9 @@ * | ||
/** | ||
* Return the value for the 'aria-valuetext' attribute for the handle | ||
* @param value - value of the handle | ||
* @param sortedIndex - index of the handle in the sorted list | ||
* @param index - index of the handle in the original list | ||
*/ | ||
ariaValueText: (value: number, sortedIndex: number, index: number) => string; | ||
/** | ||
* An event emitted when slider values are changed | ||
@@ -244,2 +296,14 @@ * | ||
rtl: boolean; | ||
/** | ||
* Slot to change the default display of the slider | ||
*/ | ||
slotStructure: SlotContent<SliderContext>; | ||
/** | ||
* Slot to change the default labels of the slider | ||
*/ | ||
slotLabel: SlotContent<SliderSlotLabelContext>; | ||
/** | ||
* Slot to change the handlers | ||
*/ | ||
slotHandle: SlotContent<SliderSlotHandleContext>; | ||
className: string; | ||
@@ -246,0 +310,0 @@ }; |
@@ -1,8 +0,8 @@ | ||
import { computed, derived, writable } from '@amadeus-it-group/tansu'; | ||
import { bindableDerived, writablesForProps } from '../../utils/stores'; | ||
import { createStoreDirective, directiveSubscribe, mergeDirectives } from '../../utils/directive'; | ||
import { stateStores } from '../../utils/stores'; | ||
import { typeArray, typeBoolean, typeFunction, typeNumber, typeNumberInRangeFactory } from '../../utils/writables'; | ||
import { computed, writable } from '@amadeus-it-group/tansu'; | ||
import { createStoreDirective, mergeDirectives } from '../../utils/directive'; | ||
import { noop } from '../../utils/internal/func'; | ||
import { getDecimalPrecision } from '../../utils/internal/math'; | ||
import { bindableProp, stateStores, writablesForProps } from '../../utils/stores'; | ||
import { typeArray, typeBoolean, typeFunction, typeNumber, typeNumberInRangeFactory } from '../../utils/writables'; | ||
import { createResizeObserver } from '../../services/resizeObserver'; | ||
const defaultSliderConfig = { | ||
@@ -16,3 +16,4 @@ min: 0, | ||
className: '', | ||
ariaLabelHandle: (value, _index) => '' + value, | ||
ariaLabelHandle: (value) => '' + value, | ||
ariaValueText: (value) => '' + value, | ||
onValuesChange: noop, | ||
@@ -23,2 +24,5 @@ values: [0], | ||
rtl: false, | ||
slotStructure: undefined, | ||
slotLabel: ({ value }) => '' + value, | ||
slotHandle: undefined, | ||
}; | ||
@@ -40,2 +44,3 @@ /** | ||
ariaLabelHandle: typeFunction, | ||
ariaValueText: typeFunction, | ||
onValuesChange: typeFunction, | ||
@@ -68,13 +73,12 @@ values: typeArray, | ||
/** | ||
* Method to update the dirtyValues for the slider keyboard navigation | ||
* Method to update the values for the slider keyboard navigation | ||
* @param handleIndex - index of the handle to update | ||
* @param _dirtyValues$ - writable signal that contains dirtyValues | ||
* @param values - slider values | ||
* @param values$ - writable signal that contains values | ||
* @param stepSize - slider step size | ||
* @param updateDirection - if equals 1 - value is increased, if equals -1 values is decreased | ||
* @param updateDirection - if equals 1 value is increased, if equals -1 value is decreased | ||
*/ | ||
const updateDirtyValue = (handleIndex, _dirtyValues$, values, stepSize, updateDirection) => { | ||
_dirtyValues$.update((value) => { | ||
const updateValue = (handleIndex, values$, stepSize, updateDirection) => { | ||
values$.update((value) => { | ||
value = [...value]; | ||
value[handleIndex] = values[handleIndex] + stepSize * updateDirection; | ||
value[handleIndex] = value[handleIndex] + stepSize * updateDirection; | ||
return value; | ||
@@ -106,3 +110,3 @@ }); | ||
// dirty inputs that need adjustment: | ||
min$: _dirtyMinimum$, max$: _dirtyMaximum$, stepSize$, rtl$, values$: _dirtyValues$, ariaLabelHandle$, onValuesChange$, showValueLabels$, showMinMaxLabels$, ...stateProps }, patch,] = writablesForProps(defaultSliderConfig, config, configValidator); | ||
min$: _dirtyMinimum$, max$: _dirtyMaximum$, stepSize$, rtl$, values$: _dirtyValues$, ariaLabelHandle$, ariaValueText$, onValuesChange$, showValueLabels$, showMinMaxLabels$, ...stateProps }, patch,] = writablesForProps(defaultSliderConfig, config, configValidator); | ||
const { vertical$, disabled$, readonly$ } = stateProps; | ||
@@ -127,3 +131,9 @@ let _prevCoordinate = -1; | ||
const _intStepSize$ = computed(() => stepSize$() * Math.pow(10, _decimalPrecision$())); | ||
const values$ = bindableDerived(onValuesChange$, [_dirtyValues$, min$, max$, _intStepSize$, _decimalPrecision$], ([dirtyValues, min, max, intStepSize, decimalPrecision]) => dirtyValues.map((dv) => computeCleanValue(dv, min, max, intStepSize, decimalPrecision)), typeArray.equal); | ||
const values$ = bindableProp(_dirtyValues$, onValuesChange$, (dirtyValues) => { | ||
const min = min$(); | ||
const max = max$(); | ||
const intStepSize = _intStepSize$(); | ||
const decimalPrecision = _decimalPrecision$(); | ||
return dirtyValues.map((dv) => computeCleanValue(dv, min, max, intStepSize, decimalPrecision)); | ||
}, typeArray.equal); | ||
// computed | ||
@@ -133,16 +143,6 @@ const { directive: sliderDirective, element$: sliderDom$ } = createStoreDirective(); | ||
const { directive: maxLabelDirective, element$: maxLabelDom$ } = createStoreDirective(); | ||
const sliderResized$ = derived(sliderDom$, (sliderDom, set) => { | ||
if (!sliderDom) { | ||
set({}); | ||
return; | ||
} | ||
const resizeObserver = new ResizeObserver(() => { | ||
set({}); | ||
}); | ||
resizeObserver.observe(sliderDom); | ||
return () => resizeObserver.disconnect(); | ||
}, {}); | ||
const { directive: resizeDirective, dimensions$ } = createResizeObserver(); | ||
const updateSliderSize$ = writable({}); | ||
const sliderDomRect$ = computed(() => { | ||
sliderResized$(); | ||
dimensions$(); | ||
updateSliderSize$(); | ||
@@ -154,3 +154,3 @@ return sliderDom$()?.getBoundingClientRect() ?? {}; | ||
const minLabelDomRect$ = computed(() => { | ||
sliderResized$(); | ||
dimensions$(); | ||
updateSliderSize$(); | ||
@@ -162,3 +162,3 @@ return minLabelDom$()?.getBoundingClientRect() ?? {}; | ||
const maxLabelDomRect$ = computed(() => { | ||
sliderResized$(); | ||
dimensions$(); | ||
updateSliderSize$(); | ||
@@ -180,5 +180,9 @@ return maxLabelDom$()?.getBoundingClientRect() ?? {}; | ||
const sortedHandles$ = computed(() => { | ||
const ariaLabelHandle = ariaLabelHandle$(); | ||
const ariaLabelHandle = ariaLabelHandle$(), ariaValueText = ariaValueText$(); | ||
return _sortedHandlesValues$().map((sortedValue, index) => { | ||
return { ...sortedValue, ariaLabel: ariaLabelHandle(sortedValue.value, index, sortedValue.id) }; | ||
return { | ||
...sortedValue, | ||
ariaLabel: ariaLabelHandle(sortedValue.value, index, sortedValue.id), | ||
ariaValueText: ariaValueText(sortedValue.value, index, sortedValue.id), | ||
}; | ||
}); | ||
@@ -294,3 +298,3 @@ }); | ||
const newValue = clickedPercent * (max$() - min$()) + min$(); | ||
_dirtyValues$.update((dh) => { | ||
values$.update((dh) => { | ||
dh = [...dh]; | ||
@@ -326,3 +330,3 @@ dh[derivedHandleIndex] = newValue; | ||
directives: { | ||
sliderDirective: mergeDirectives(sliderDirective, directiveSubscribe(sliderResized$)), | ||
sliderDirective: mergeDirectives(sliderDirective, resizeDirective), | ||
minLabelDirective, | ||
@@ -337,19 +341,19 @@ maxLabelDirective, | ||
const { key } = event; | ||
const rtl = rtl$(), values = values$(), stepSize = stepSize$(), min = min$(), max = max$(), vertical = vertical$(); | ||
const rtl = rtl$(), stepSize = stepSize$(), min = min$(), max = max$(), vertical = vertical$(); | ||
if (isInteractable$()) { | ||
switch (key) { | ||
case 'ArrowDown': | ||
updateDirtyValue(handleIndex, _dirtyValues$, values, stepSize, getUpdateDirection(vertical, rtl, true)); | ||
updateValue(handleIndex, values$, stepSize, getUpdateDirection(vertical, rtl, true)); | ||
break; | ||
case 'ArrowLeft': | ||
updateDirtyValue(handleIndex, _dirtyValues$, values, stepSize, getUpdateDirection(vertical, rtl, false)); | ||
updateValue(handleIndex, values$, stepSize, getUpdateDirection(vertical, rtl, false)); | ||
break; | ||
case 'ArrowUp': | ||
updateDirtyValue(handleIndex, _dirtyValues$, values, stepSize, -1 * getUpdateDirection(vertical, rtl, true)); | ||
updateValue(handleIndex, values$, stepSize, -1 * getUpdateDirection(vertical, rtl, true)); | ||
break; | ||
case 'ArrowRight': | ||
updateDirtyValue(handleIndex, _dirtyValues$, values, stepSize, -1 * getUpdateDirection(vertical, rtl, false)); | ||
updateValue(handleIndex, values$, stepSize, -1 * getUpdateDirection(vertical, rtl, false)); | ||
break; | ||
case 'Home': | ||
_dirtyValues$.update((value) => { | ||
values$.update((value) => { | ||
value = [...value]; | ||
@@ -361,3 +365,3 @@ value[handleIndex] = min; | ||
case 'End': | ||
_dirtyValues$.update((value) => { | ||
values$.update((value) => { | ||
value = [...value]; | ||
@@ -383,6 +387,7 @@ value[handleIndex] = max; | ||
event.preventDefault(); | ||
const currentTarget = event.target; | ||
const handleDrag = (e) => { | ||
e.preventDefault(); | ||
const newCoord = vertical$() ? e.clientY : e.clientX; | ||
event.target.focus(); | ||
currentTarget.focus(); | ||
if (_prevCoordinate !== newCoord) { | ||
@@ -395,3 +400,3 @@ _prevCoordinate = newCoord; | ||
updateSliderSize$.set({}); | ||
event.target.focus(); | ||
currentTarget.focus(); | ||
document.addEventListener('mousemove', handleDrag); | ||
@@ -398,0 +403,0 @@ // TODO mouse up doesn't work outside the handle area |
@@ -10,2 +10,3 @@ import type { ReadableSignal, WritableSignal } from '@amadeus-it-group/tansu'; | ||
import type { SliderProps } from './components/slider/slider'; | ||
import type { ToastProps } from './components/toast/toast'; | ||
export type Partial2Levels<T> = Partial<{ | ||
@@ -82,2 +83,6 @@ [Level1 in keyof T]: Partial<T[Level1]>; | ||
slider: SliderProps; | ||
/** | ||
* toast widget config | ||
*/ | ||
toast: ToastProps; | ||
} |
@@ -11,2 +11,3 @@ export * from './components/commonProps'; | ||
export * from './components/slider'; | ||
export * from './components/toast'; | ||
export * from './config'; | ||
@@ -19,2 +20,3 @@ export * from './services/extendWidget'; | ||
export * from './services/portal'; | ||
export * from './services/resizeObserver'; | ||
export * from './services/siblingsInert'; | ||
@@ -21,0 +23,0 @@ export * from './services/transitions/baseTransitions'; |
@@ -13,2 +13,3 @@ // types | ||
export * from './components/slider'; | ||
export * from './components/toast'; | ||
// config | ||
@@ -23,2 +24,3 @@ export * from './config'; | ||
export * from './services/portal'; | ||
export * from './services/resizeObserver'; | ||
export * from './services/siblingsInert'; | ||
@@ -25,0 +27,0 @@ // services transitions |
@@ -50,10 +50,8 @@ { | ||
}, | ||
"dependencies": { | ||
"@amadeus-it-group/tansu": "1.0.0" | ||
}, | ||
"peerDependencies": { | ||
"@amadeus-it-group/tansu": "*", | ||
"@floating-ui/dom": "*" | ||
}, | ||
"sideEffects": false, | ||
"version": "0.1.1", | ||
"version": "0.2.0", | ||
"homepage": "https://amadeusitgroup.github.io/AgnosUI/latest/", | ||
@@ -60,0 +58,0 @@ "bugs": "https://github.com/AmadeusITGroup/AgnosUI/issues", |
@@ -5,12 +5,16 @@ import type { ConfigValidator, SlotContent, Widget, WidgetFactory, WidgetProps, WidgetSlotContext, WidgetState } from '../types'; | ||
*/ | ||
export type ExtendWidgetProps<W extends Widget, ExtraProps extends object> = Widget<ExtendWidgetAdaptSlotWidgetProps<WidgetProps<W>, ExtraProps>, ExtendWidgetAdaptSlotWidgetProps<WidgetState<W>, ExtraProps>, W['api'], W['actions'], W['directives']>; | ||
export type ExtendWidgetProps<W extends Widget, ExtraProps extends object, ExtraDirectives extends object = object> = Widget<ExtendWidgetAdaptSlotWidgetProps<WidgetProps<W>, ExtraProps, ExtraDirectives>, ExtendWidgetAdaptSlotWidgetProps<WidgetState<W>, ExtraProps, ExtraDirectives>, W['api'], W['actions'], ExtendWidgetInterfaces<W['directives'], ExtraDirectives>>; | ||
/** | ||
* Type merging the passed interfaces together | ||
*/ | ||
export type ExtendWidgetInterfaces<Interfaces, ExtraInterfaces> = Interfaces & ExtraInterfaces; | ||
/** | ||
* Type replacing the original Props with WidgetSlotContext contaning ExtraProps | ||
*/ | ||
export type ExtendWidgetAdaptSlotContentProps<Props extends Record<string, any>, ExtraProps extends object> = Props extends WidgetSlotContext<infer U> ? WidgetSlotContext<ExtendWidgetProps<U, ExtraProps>> & Omit<Props, keyof WidgetSlotContext<any>> : Props; | ||
export type ExtendWidgetAdaptSlotContentProps<Props extends Record<string, any>, ExtraProps extends object, ExtraDirectives extends object> = Props extends WidgetSlotContext<infer U> ? WidgetSlotContext<ExtendWidgetProps<U, ExtraProps, ExtraDirectives>> & Omit<Props, keyof WidgetSlotContext<any>> : Props; | ||
/** | ||
* Type enriching the original widget slot Props with ExtraProps slots | ||
*/ | ||
export type ExtendWidgetAdaptSlotWidgetProps<Props, ExtraProps extends object> = Omit<Props, `slot${string}`> & ExtraProps & { | ||
[K in keyof Props & `slot${string}`]: Props[K] extends SlotContent<infer U> ? SlotContent<ExtendWidgetAdaptSlotContentProps<U, ExtraProps>> : Props[K]; | ||
export type ExtendWidgetAdaptSlotWidgetProps<Props, ExtraProps extends object, ExtraDirectives extends object> = Omit<Props, `slot${string}`> & ExtraProps & { | ||
[K in keyof Props & `slot${string}`]: Props[K] extends SlotContent<infer U> ? SlotContent<ExtendWidgetAdaptSlotContentProps<U, ExtraProps, ExtraDirectives>> : Props[K]; | ||
}; | ||
@@ -24,2 +28,2 @@ /** | ||
*/ | ||
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<ExtendWidgetProps<W, ExtraProps>>; | ||
export declare const extendWidgetProps: <W extends Widget<object, object, object, object, object>, ExtraProps extends object, ExtraDirectives extends object = object>(factory: WidgetFactory<W>, extraPropsDefaults: ExtraProps, extraPropsConfig?: ConfigValidator<ExtraProps> | undefined) => WidgetFactory<ExtendWidgetProps<W, ExtraProps, ExtraDirectives>>; |
@@ -1,2 +0,1 @@ | ||
import type { Directive } from '../types'; | ||
export type NavManager = ReturnType<typeof createNavManager>; | ||
@@ -26,6 +25,7 @@ /** | ||
*/ | ||
export type NavManagerKeyHandler = (info: { | ||
export type NavManagerKeyHandler<T = any> = (info: { | ||
directiveElement: HTMLElement; | ||
event: KeyboardEvent; | ||
event: Event; | ||
navManager: NavManager; | ||
context?: T; | ||
}) => void; | ||
@@ -35,3 +35,3 @@ /** | ||
*/ | ||
export interface NavManagerItemConfig { | ||
export interface NavManagerItemConfig<T = any> { | ||
/** | ||
@@ -42,3 +42,3 @@ * Map of key handlers. | ||
*/ | ||
keys?: Record<string, NavManagerKeyHandler>; | ||
keys?: Record<string, NavManagerKeyHandler<T>>; | ||
/** | ||
@@ -50,2 +50,6 @@ * Function returning DOM elements to include in the navigation manager. | ||
selector?: (directiveElement: HTMLElement) => Iterable<HTMLElement>; | ||
/** | ||
* | ||
*/ | ||
context?: T; | ||
} | ||
@@ -66,33 +70,36 @@ /** | ||
elementsInDomOrder$: import("@amadeus-it-group/tansu").ReadableSignal<HTMLElement[]>; | ||
directive: Directive<NavManagerItemConfig>; | ||
directive: <T = any>(directiveElement: HTMLElement, config: NavManagerItemConfig<T>) => { | ||
update(newConfig: NavManagerItemConfig<T>): void; | ||
destroy(): void; | ||
}; | ||
focusIndex: (index: number, moveDirection?: -1 | 0 | 1) => HTMLElement | null; | ||
focusPrevious: ({ event, referenceElement, }?: { | ||
event?: KeyboardEvent | undefined; | ||
event?: Event | undefined; | ||
referenceElement?: HTMLElement | null | undefined; | ||
}) => HTMLElement | null; | ||
focusNext: ({ event, referenceElement, }?: { | ||
event?: KeyboardEvent | undefined; | ||
event?: Event | undefined; | ||
referenceElement?: HTMLElement | null | undefined; | ||
}) => HTMLElement | null; | ||
focusFirst: ({ event }?: { | ||
event?: KeyboardEvent | undefined; | ||
event?: Event | undefined; | ||
}) => HTMLElement | null; | ||
focusFirstLeft: (args_0?: { | ||
event?: KeyboardEvent | undefined; | ||
event?: Event | undefined; | ||
} | undefined) => HTMLElement | null; | ||
focusFirstRight: (args_0?: { | ||
event?: KeyboardEvent | undefined; | ||
event?: Event | undefined; | ||
} | undefined) => HTMLElement | null; | ||
focusLast: ({ event }?: { | ||
event?: KeyboardEvent | undefined; | ||
event?: Event | undefined; | ||
}) => HTMLElement | null; | ||
focusLeft: (args_0?: { | ||
event?: KeyboardEvent | undefined; | ||
event?: Event | undefined; | ||
referenceElement?: HTMLElement | null | undefined; | ||
} | undefined) => HTMLElement | null; | ||
focusRight: (args_0?: { | ||
event?: KeyboardEvent | undefined; | ||
event?: Event | undefined; | ||
referenceElement?: HTMLElement | null | undefined; | ||
} | undefined) => HTMLElement | null; | ||
refreshElements: () => void; | ||
refreshElements: (now?: boolean) => void; | ||
}; |
@@ -75,3 +75,9 @@ import { computed, writable } from '@amadeus-it-group/tansu'; | ||
const elementsRefresh$ = writable({}); | ||
const refreshElements = () => elementsRefresh$.set({}); | ||
const refreshElements = (now = true) => { | ||
elementsRefresh$.set({}); | ||
if (now) { | ||
commonAncestor$(); | ||
elementsInDomOrder$(); | ||
} | ||
}; | ||
const elements$ = computed(() => { | ||
@@ -134,4 +140,4 @@ elementsRefresh$(); | ||
if (handler) { | ||
refreshElements(); | ||
handler({ event, directiveElement, navManager }); | ||
refreshElements(false); | ||
handler({ event, directiveElement, navManager, context: config.context }); | ||
} | ||
@@ -138,0 +144,0 @@ }; |
@@ -5,4 +5,4 @@ import { createSimpleClassTransition } from '../simpleClassTransition'; | ||
animationPendingShowClasses: ['show'], | ||
showClasses: ['show'], | ||
hideClasses: ['d-none'], | ||
showClasses: ['show', 'fade'], | ||
hideClasses: ['d-none', 'fade'], | ||
}); |
@@ -26,2 +26,10 @@ import type { ReadableSignal } from '@amadeus-it-group/tansu'; | ||
/** | ||
* Maps the argument to another argument of a directive using a provided function. | ||
* | ||
* @param directive - The directive to be applied. | ||
* @param fn - The function to map the argument. | ||
* @returns A new directive that applies the mapping function to the argument. | ||
*/ | ||
export declare const mapDirectiveArg: <T, U>(directive: Directive<U>, fn: (arg: T) => U) => Directive<T>; | ||
/** | ||
* Returns a directive that subscribes to the given store while it is used on a DOM element, | ||
@@ -28,0 +36,0 @@ * and that unsubscribes from it when it is no longer used. |
@@ -47,2 +47,18 @@ import { asReadable, batch, readable, writable } from '@amadeus-it-group/tansu'; | ||
/** | ||
* Maps the argument to another argument of a directive using a provided function. | ||
* | ||
* @param directive - The directive to be applied. | ||
* @param fn - The function to map the argument. | ||
* @returns A new directive that applies the mapping function to the argument. | ||
*/ | ||
export const mapDirectiveArg = (directive, fn) => (node, arg) => { | ||
const instance = directive(node, fn(arg)); | ||
return { | ||
update: (arg) => { | ||
instance?.update?.(fn(arg)); | ||
}, | ||
destroy: () => instance?.destroy?.(), | ||
}; | ||
}; | ||
/** | ||
* Returns a directive that subscribes to the given store while it is used on a DOM element, | ||
@@ -49,0 +65,0 @@ * and that unsubscribes from it when it is no longer used. |
@@ -26,1 +26,16 @@ /** | ||
export declare const removeClasses: (element: HTMLElement, classes?: string[]) => void; | ||
/** | ||
* Adds an event listener to the specified element. | ||
* | ||
* @param element - The HTML element to which the event listener will be added. | ||
* @param type - A string representing the event type to listen for. | ||
* @param fn - The event listener function or object. | ||
* @returns A function that removes the event listener from the element. | ||
*/ | ||
export declare function addEvent<K extends keyof HTMLElementEventMap>(element: HTMLElement, type: K, fn: (this: HTMLElement, ev: HTMLElementEventMap[K]) => any): () => void; | ||
/** | ||
* Generates a unique ID with the format 'auId-[counter]'. | ||
* | ||
* @returns The generated ID. | ||
*/ | ||
export declare const generateId: () => string; |
@@ -62,1 +62,22 @@ /** | ||
}; | ||
/** | ||
* Adds an event listener to the specified element. | ||
* | ||
* @param element - The HTML element to which the event listener will be added. | ||
* @param type - A string representing the event type to listen for. | ||
* @param fn - The event listener function or object. | ||
* @returns A function that removes the event listener from the element. | ||
*/ | ||
export function addEvent(element, type, fn) { | ||
element.addEventListener(type, fn); | ||
return function () { | ||
element.removeEventListener(type, fn); | ||
}; | ||
} | ||
let idCount = 0; | ||
/** | ||
* Generates a unique ID with the format 'auId-[counter]'. | ||
* | ||
* @returns The generated ID. | ||
*/ | ||
export const generateId = () => `auId-${idCount++}`; |
@@ -35,2 +35,4 @@ const isInertOrInvisible = (element) => { | ||
*/ | ||
export const isFocusable = (element) => !isInertOrInvisible(element) && (isFocusableByTagName[element.tagName] ?? isFocusableOtherTags)(element); | ||
export const isFocusable = (element) => { | ||
return document.contains(element) && !isInertOrInvisible(element) && (isFocusableByTagName[element.tagName] ?? isFocusableOtherTags)(element); | ||
}; |
@@ -7,2 +7,2 @@ /** | ||
*/ | ||
export declare const getTextDirection: (element: HTMLElement) => "rtl" | "ltr"; | ||
export declare const getTextDirection: (element: HTMLElement) => "ltr" | "rtl"; |
@@ -167,4 +167,4 @@ import type { ReadableSignal, StoreInput, StoresInputValues, WritableSignal } from '@amadeus-it-group/tansu'; | ||
/** | ||
* Creates a computed store that binds to multiple stores and triggers a callback when the value changes. | ||
* @param onChange$ - A readable signal callback function to execute when the value changes. | ||
* Creates a derived store that binds to multiple stores and triggers a callback when the value changes for any reason. | ||
* @param onChange$ - A readable signal containing a callback function to execute when the value changes. | ||
* @param stores - An array of Svelte stores, with the main store at index 0. | ||
@@ -175,2 +175,13 @@ * @param adjustValue - A function to adjust the value of the main store. By default, the value of the main store is returned. | ||
*/ | ||
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>; | ||
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) => WritableSignal<T, T>; | ||
/** | ||
* Creates a computed store that contains the adjusted value of the given store and that triggers a callback when the value changes from the set or update | ||
* method of the returned writable store. | ||
* @param store$ - store to be bound | ||
* @param onChange$ - A readable signal containing a callback function to execute when the value changes from the set or update method of the returned writable store. | ||
* @param adjustValue - A function to adjust the value of the store, called in a reactive context each time the value changes or any called dependency changes. | ||
* By default, the value of store$ is returned. | ||
* @param equal - A function to determine if two values are equal. | ||
* @returns A writable store that contains the adjusted value of the given store, with the set or update functions that trigger the onChange$ callback. | ||
*/ | ||
export declare const bindableProp: <T>(store$: WritableSignal<T, T | undefined>, onChange$: ReadableSignal<(newValue: T) => void>, adjustValue?: (value: T) => T, equal?: (a: T, b: T) => boolean) => WritableSignal<T, T>; |
@@ -1,2 +0,2 @@ | ||
import { asReadable, asWritable, batch, computed, derived, get, readable, writable } from '@amadeus-it-group/tansu'; | ||
import { asReadable, asWritable, batch, computed, derived, equal as tansuDefaultEqual, get, readable, writable } from '@amadeus-it-group/tansu'; | ||
import { INVALID_VALUE } from '../types'; | ||
@@ -255,4 +255,4 @@ import { identity } from './internal/func'; | ||
/** | ||
* Creates a computed store that binds to multiple stores and triggers a callback when the value changes. | ||
* @param onChange$ - A readable signal callback function to execute when the value changes. | ||
* Creates a derived store that binds to multiple stores and triggers a callback when the value changes for any reason. | ||
* @param onChange$ - A readable signal containing a callback function to execute when the value changes. | ||
* @param stores - An array of Svelte stores, with the main store at index 0. | ||
@@ -265,3 +265,3 @@ * @param adjustValue - A function to adjust the value of the main store. By default, the value of the main store is returned. | ||
let currentValue = stores[0](); | ||
return derived(stores, { | ||
return asWritable(derived(stores, { | ||
derive(values) { | ||
@@ -282,3 +282,20 @@ const newValue = adjustValue(values); | ||
equal, | ||
}); | ||
}), stores[0].set.bind(stores[0])); | ||
}; | ||
/** | ||
* Creates a computed store that contains the adjusted value of the given store and that triggers a callback when the value changes from the set or update | ||
* method of the returned writable store. | ||
* @param store$ - store to be bound | ||
* @param onChange$ - A readable signal containing a callback function to execute when the value changes from the set or update method of the returned writable store. | ||
* @param adjustValue - A function to adjust the value of the store, called in a reactive context each time the value changes or any called dependency changes. | ||
* By default, the value of store$ is returned. | ||
* @param equal - A function to determine if two values are equal. | ||
* @returns A writable store that contains the adjusted value of the given store, with the set or update functions that trigger the onChange$ callback. | ||
*/ | ||
export const bindableProp = (store$, onChange$, adjustValue = identity, equal = tansuDefaultEqual) => asWritable(computed(() => adjustValue(store$()), { equal }), (newValue) => { | ||
const adjustedValue = adjustValue(newValue); | ||
if (!equal(store$(), adjustedValue)) { | ||
store$.set(adjustedValue); | ||
onChange$()(adjustedValue); | ||
} | ||
}); |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
292062
108
7580
- Removed@amadeus-it-group/tansu@1.0.0