This commit is contained in:
Xon 2024-08-27 22:49:48 +00:00
commit 15f81a5183
32 changed files with 2648 additions and 2792 deletions

File diff suppressed because it is too large Load diff

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load diff

View file

@ -197,7 +197,7 @@
}
.choices__list--dropdown, .choices__list[aria-expanded] {
visibility: hidden;
display: none;
z-index: 1;
position: absolute;
width: 100%;
@ -209,10 +209,9 @@
border-bottom-right-radius: 2.5px;
overflow: hidden;
word-break: break-all;
will-change: visibility;
}
.is-active.choices__list--dropdown, .is-active.choices__list[aria-expanded] {
visibility: visible;
display: block;
}
.is-open .choices__list--dropdown, .is-open .choices__list[aria-expanded] {
border-color: #b7b7b7;
@ -240,10 +239,10 @@
text-align: right;
}
@media (min-width: 640px) {
.choices__list--dropdown .choices__item--selectable, .choices__list[aria-expanded] .choices__item--selectable {
.choices__list--dropdown .choices__item--selectable[data-select-text], .choices__list[aria-expanded] .choices__item--selectable[data-select-text] {
padding-right: 100px;
}
.choices__list--dropdown .choices__item--selectable::after, .choices__list[aria-expanded] .choices__item--selectable::after {
.choices__list--dropdown .choices__item--selectable[data-select-text]::after, .choices__list[aria-expanded] .choices__item--selectable[data-select-text]::after {
content: attr(data-select-text);
font-size: 12px;
opacity: 0;
@ -252,12 +251,12 @@
top: 50%;
transform: translateY(-50%);
}
[dir=rtl] .choices__list--dropdown .choices__item--selectable, [dir=rtl] .choices__list[aria-expanded] .choices__item--selectable {
[dir=rtl] .choices__list--dropdown .choices__item--selectable[data-select-text], [dir=rtl] .choices__list[aria-expanded] .choices__item--selectable[data-select-text] {
text-align: right;
padding-left: 100px;
padding-right: 10px;
}
[dir=rtl] .choices__list--dropdown .choices__item--selectable::after, [dir=rtl] .choices__list[aria-expanded] .choices__item--selectable::after {
[dir=rtl] .choices__list--dropdown .choices__item--selectable[data-select-text]::after, [dir=rtl] .choices__list[aria-expanded] .choices__item--selectable[data-select-text]::after {
right: auto;
left: 10px;
}

View file

@ -1 +1 @@
{"version":3,"sourceRoot":"","sources":["../../../src/styles/choices.scss"],"names":[],"mappings":"AAAA;AAAA;AAAA;AA2BA;EACE;EACA;EACA,eApBkB;EAqBlB,WAxBqB;;AA0BrB;EACE;;AAGF;EACE;;AAGF;EACE;;AAIA;AAAA;EAEE,kBAlCsB;EAmCtB;EACA;;AAEF;EACE;;AAIJ;EACE;;;AAIJ;EACE;;AACA;EACE;;AAEF;EACE;EACA;EACA;EACA;EACA;EACA;;AAEF;EACE,kBApDyB;EAqDzB;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;AAEA;EAEE;;AAGF;EACE;;AAGJ;EACE;;AAGF;EACE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;AAGF;EACE;EACA;;AAIA;EACE;EACA;;AAEF;EACE;EACA;EACA;EACA;;;AAOJ;AAAA;EACE;;AAEF;AAAA;EACE;EACA;EACA;EACA;EACA;EACA,aA5HoB;EA6HpB;EACA;EACA,kBA9HiB;EA+HjB,iBAjIuB;EAkIvB,OAlIuB;EAmIvB;EACA;EACA;;AAEA;AAAA;AAAA;EAEE;;;AAKN;EACE;EACA;EACA;EACA,kBA1JiB;EA2JjB;EACA;EACA,eA/JsB;EAgKtB,WAnKqB;EAoKrB;EACA;;AAEA;EAEE;;AAGF;EACE;;AAGF;EACE;;;AAIJ;EACE;EACA;EACA;;AAOF;EACE;EACA;EACA;;AAEA;EACE;EACA;;AAEF;EACE;;;AAIJ;EACE;;AACA;EACE;EACA;EACA,eA9MyB;EA+MzB;EACA,WAnNmB;EAoNnB;EACA;EACA;EACA,kBA9MoB;EA+MpB;EACA;EACA;EACA;;AAEA;EACE;;AAGF;EACE;EACA;;AAGF;EACE;EACA;;AAGF;EACE;EACA;;;AAKN;EACE;EACA,SApOgB;EAqOhB;EACA;EACA,kBAjP0B;EAkP1B;EACA;EACA;EACA,2BAzPsB;EA0PtB,4BA1PsB;EA2PtB;EACA;EACA;;AAEA;EACE;;AAGF;EACE;;AAGF;EACE;EACA;EACA;EACA;EACA;;AAEF;EACE;EACA;EACA;EACA;EACA;;AAEF;EACE;EACA;EACA,WA3RmB;;AA6RnB;EACE;;AAIF;EADF;IAEI;;EAEA;IACE;IACA,WAtSe;IAuSf;IACA;IACA;IACA;IACA;;EAGF;IACE;IACA;IACA;;EAEA;IACE;IACA;;;AAKN;EACE;;AAEA;EACE;;;AAUR;EACE;;;AAGF;EACE;;;AAGF;EACE;EACA;EACA;;;AAGF;EACE;EACA,WAxVqB;EAyVrB;EACA;EACA;;;AAGF;EACE;EACA;EACA;EACA;EACA;EACA;EACA;;AAEA;EACE;;;AAIJ;EACE;EACA;EACA,kBA3WiB;EA4WjB,WAjXqB;EAkXrB;EACA;EACA;EACA;EACA;;AAEA;EACE;;AAGF;EAIE;;AAGF;EAEE;EACA;EACA;;AAGF;EACE;EACA;;;AAIJ;EACE;;;AAGF","file":"choices.css"}
{"version":3,"sourceRoot":"","sources":["../../../src/styles/choices.scss"],"names":[],"mappings":"AAAA;AAAA;AAAA;AA2BA;EACE;EACA;EACA,eApBkB;EAqBlB,WAxBqB;;AA0BrB;EACE;;AAGF;EACE;;AAGF;EACE;;AAIA;AAAA;EAEE,kBAlCsB;EAmCtB;EACA;;AAEF;EACE;;AAIJ;EACE;;;AAIJ;EACE;;AACA;EACE;;AAEF;EACE;EACA;EACA;EACA;EACA;EACA;;AAEF;EACE,kBApDyB;EAqDzB;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;AAEA;EAEE;;AAGF;EACE;;AAGJ;EACE;;AAGF;EACE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;AAGF;EACE;EACA;;AAIA;EACE;EACA;;AAEF;EACE;EACA;EACA;EACA;;;AAOJ;AAAA;EACE;;AAEF;AAAA;EACE;EACA;EACA;EACA;EACA;EACA,aA5HoB;EA6HpB;EACA;EACA,kBA9HiB;EA+HjB,iBAjIuB;EAkIvB,OAlIuB;EAmIvB;EACA;EACA;;AAEA;AAAA;AAAA;EAEE;;;AAKN;EACE;EACA;EACA;EACA,kBA1JiB;EA2JjB;EACA;EACA,eA/JsB;EAgKtB,WAnKqB;EAoKrB;EACA;;AAEA;EAEE;;AAGF;EACE;;AAGF;EACE;;;AAIJ;EACE;EACA;EACA;;AAOF;EACE;EACA;EACA;;AAEA;EACE;EACA;;AAEF;EACE;;;AAIJ;EACE;;AACA;EACE;EACA;EACA,eA9MyB;EA+MzB;EACA,WAnNmB;EAoNnB;EACA;EACA;EACA,kBA9MoB;EA+MpB;EACA;EACA;EACA;;AAEA;EACE;;AAGF;EACE;EACA;;AAGF;EACE;EACA;;AAGF;EACE;EACA;;;AAKN;EACE;EACA,SApOgB;EAqOhB;EACA;EACA,kBAjP0B;EAkP1B;EACA;EACA;EACA,2BAzPsB;EA0PtB,4BA1PsB;EA2PtB;EACA;;AAEA;EACE;;AAGF;EACE;;AAGF;EACE;EACA;EACA;EACA;EACA;;AAEF;EACE;EACA;EACA;EACA;EACA;;AAEF;EACE;EACA;EACA,WA1RmB;;AA4RnB;EACE;;AAKA;EADF;IAEI;;EAEA;IACE;IACA,WAtSa;IAuSb;IACA;IACA;IACA;IACA;;EAGF;IACE;IACA;IACA;;EAEA;IACE;IACA;;;AAMR;EACE;;AAEA;EACE;;;AAUR;EACE;;;AAGF;EACE;;;AAGF;EACE;EACA;EACA;;;AAGF;EACE;EACA,WAzVqB;EA0VrB;EACA;EACA;;;AAGF;EACE;EACA;EACA;EACA;EACA;EACA;EACA;;AAEA;EACE;;;AAIJ;EACE;EACA;EACA,kBA5WiB;EA6WjB,WAlXqB;EAmXrB;EACA;EACA;EACA;EACA;;AAEA;EACE;;AAGF;EAIE;;AAGF;EAEE;EACA;EACA;;AAGF;EACE;EACA;;;AAIJ;EACE;;;AAGF","file":"choices.css"}

File diff suppressed because one or more lines are too long

View file

@ -47,7 +47,7 @@
<!-- End ignore these -->
<!-- Optional includes -->
<script src="https://cdnjs.cloudflare.com/polyfill/v3/polyfill.min.js?features=Array.from%2Ces5%2Ces6%2CSymbol%2CSymbol.iterator%2CDOMTokenList%2CObject.assign%2CCustomEvent%2CElement.prototype.classList%2CElement.prototype.closest%2CElement.prototype.dataset%2CArray.prototype.find%2CArray.prototype.includes%2Cfetch"></script>
<script src="https://cdnjs.cloudflare.com/polyfill/v3/polyfill.min.js?version=4.8.0&features=Array.from%2CArray.prototype.find%2CArray.prototype.includes%2CSymbol%2CSymbol.iterator%2CDOMTokenList%2CObject.assign%2CCustomEvent%2CElement.prototype.classList%2CElement.prototype.closest%2CElement.prototype.dataset%2CElement.prototype.replaceChildren%2Cfetch"></script>
<!-- End optional includes -->
<!-- Choices includes -->

View file

@ -38,7 +38,7 @@ declare class Choices {
_isSelectElement: boolean;
_hasNonChoicePlaceholder: boolean;
_canAddUserChoices: boolean;
_store: Store;
_store: Store<Options>;
_templates: Templates;
_lastAddedChoiceId: number;
_lastAddedGroupId: number;
@ -152,12 +152,9 @@ declare class Choices {
_render(changes?: StateChangeSet): void;
_renderChoices(): void;
_renderItems(): void;
_createGroupsFragment(groups: GroupFull[], choices: ChoiceFull[], fragment?: DocumentFragment): DocumentFragment;
_createChoicesFragment(choices: ChoiceFull[], fragment?: DocumentFragment, withinGroup?: boolean): DocumentFragment;
_createItemsFragment(items: InputChoice[], fragment?: DocumentFragment): DocumentFragment;
_displayNotice(text: string, type: NoticeType, openDropdown?: boolean): void;
_clearNotice(): void;
_renderNotice(): void;
_renderNotice(fragment?: DocumentFragment): void;
_getChoiceForOutput(choice?: ChoiceFull, keyCode?: number): EventChoice | undefined;
_triggerChange(value: any): void;
_handleButtonAction(element?: HTMLElement): void;

View file

@ -20,10 +20,10 @@ export default class Container {
* Determine whether container should be flipped based on passed
* dropdown position
*/
shouldFlip(dropdownPos: number): boolean;
shouldFlip(dropdownPos: number, dropdownHeight: number): boolean;
setActiveDescendant(activeDescendantID: string): void;
removeActiveDescendant(): void;
open(dropdownPos: number): void;
open(dropdownPos: number, dropdownHeight: number): void;
close(): void;
addFocusState(): void;
removeFocusState(): void;

View file

@ -10,10 +10,6 @@ export default class Dropdown {
type: PassedElementType;
classNames: ClassNames;
});
/**
* Bottom position of dropdown in viewport coordinates
*/
get distanceFromTopWindow(): number;
/**
* Show dropdown to user by adding active state class
*/

View file

@ -5,7 +5,6 @@ export default class List {
constructor({ element }: {
element: HTMLElement;
});
clear(): void;
prepend(node: Element | DocumentFragment): void;
scrollToTop(): void;
scrollToChildElement(element: HTMLElement, direction: 1 | -1): void;

View file

@ -1,5 +1,5 @@
import { ClassNames } from '../interfaces/class-names';
import { EventType } from '../interfaces/event-type';
import { EventTypes } from '../interfaces/event-type';
import { EventMap } from '../interfaces';
export default class WrappedElement<T extends HTMLInputElement | HTMLSelectElement> {
element: T;
@ -17,5 +17,5 @@ export default class WrappedElement<T extends HTMLInputElement | HTMLSelectEleme
reveal(): void;
enable(): void;
disable(): void;
triggerEvent<K extends EventType>(eventType: EventType, data?: EventMap[K]['detail']): void;
triggerEvent<K extends EventTypes>(eventType: EventTypes, data?: EventMap[K]['detail']): void;
}

View file

@ -1,4 +1 @@
export declare const TEXT_TYPE: HTMLInputElement['type'];
export declare const SELECT_ONE_TYPE: HTMLSelectElement['type'];
export declare const SELECT_MULTIPLE_TYPE: HTMLSelectElement['type'];
export declare const SCROLLING_SPEED: number;

View file

@ -1,3 +1,4 @@
import { Types } from './types';
export declare const ActionType: {
readonly ADD_CHOICE: "ADD_CHOICE";
readonly REMOVE_CHOICE: "REMOVE_CHOICE";
@ -9,4 +10,4 @@ export declare const ActionType: {
readonly REMOVE_ITEM: "REMOVE_ITEM";
readonly HIGHLIGHT_ITEM: "HIGHLIGHT_ITEM";
};
export type ActionTypes = (typeof ActionType)[keyof typeof ActionType];
export type ActionTypes = Types.ValueOf<typeof ActionType>;

View file

@ -4,6 +4,8 @@ export interface ChoiceFull {
id: number;
highlighted: boolean;
element?: HTMLOptionElement | HTMLOptGroupElement;
itemEl?: HTMLElement;
choiceEl?: HTMLElement;
labelClass?: Array<string>;
labelDescription?: string;
customProperties?: CustomProperties;

View file

@ -1,12 +1,14 @@
export declare const enum EventType {
showDropdown = "showDropdown",
hideDropdown = "hideDropdown",
change = "change",
choice = "choice",
search = "search",
addItem = "addItem",
removeItem = "removeItem",
highlightItem = "highlightItem",
highlightChoice = "highlightChoice",
unhighlightItem = "unhighlightItem"
}
import { Types } from './types';
export declare const EventType: {
readonly showDropdown: "showDropdown";
readonly hideDropdown: "hideDropdown";
readonly change: "change";
readonly choice: "choice";
readonly search: "search";
readonly addItem: "addItem";
readonly removeItem: "removeItem";
readonly highlightItem: "highlightItem";
readonly highlightChoice: "highlightChoice";
readonly unhighlightItem: "unhighlightItem";
};
export type EventTypes = Types.ValueOf<typeof EventType>;

View file

@ -3,7 +3,8 @@ export interface GroupFull {
id: number;
active: boolean;
disabled: boolean;
label: string;
label?: string;
element?: HTMLOptGroupElement;
groupEl?: HTMLElement;
choices: ChoiceFull[];
}

View file

@ -429,7 +429,7 @@ export interface Options {
*
* @default 'auto';
*/
renderSelectedChoices: 'auto' | 'always';
renderSelectedChoices: 'auto' | 'always' | boolean;
/**
* The text that is shown whilst choices are being populated via AJAX.
*
@ -455,7 +455,7 @@ export interface Options {
*/
noChoicesText: string | Types.StringFunction;
/**
* The text that is shown when a user hovers over a selectable choice.
* The text that is shown when a user hovers over a selectable choice. Set to empty to not reserve space for this text.
*
* **Input types affected:** select-multiple, select-one
*
@ -548,7 +548,7 @@ export interface Options {
* },
* choice: (data) => {
* return template(`
* <div class="${getClassNames(classNames.item).join(' ')} ${classNames.itemChoice} ${data.disabled ? classNames.itemDisabled : classNames.itemSelectable}" data-select-text="${this.config.itemSelectText}" data-choice ${data.disabled ? 'data-choice-disabled aria-disabled="true"' : 'data-choice-selectable'} data-id="${data.id}" data-value="${data.value}" ${data.groupId > 0 ? 'role="treeitem"' : 'role="option"'}>
* <div class="${getClassNames(classNames.item).join(' ')} ${classNames.itemChoice} ${data.disabled ? classNames.itemDisabled : classNames.itemSelectable}" data-select-text="${this.config.itemSelectText}" data-choice ${data.disabled ? 'data-choice-disabled aria-disabled="true"' : 'data-choice-selectable'} data-id="${data.id}" data-value="${data.value}" ${data.groupId ? 'role="treeitem"' : 'role="option"'}>
* <span>&bigstar;</span> ${data.label}
* </div>
* `);

View file

@ -1 +1,7 @@
export type PassedElementType = 'text' | 'select-one' | 'select-multiple';
import { Types } from './types';
export declare const PassedElementTypes: {
readonly Text: "text";
readonly SelectOne: "select-one";
readonly SelectMultiple: "select-multiple";
};
export type PassedElementType = Types.ValueOf<typeof PassedElementTypes>;

View file

@ -9,7 +9,7 @@ export interface StateUpdate<T> {
update: boolean;
state: T;
}
export type Reducer<T> = (state: T, action: AnyAction) => StateUpdate<T>;
export type Reducer<T> = (state: T, action: AnyAction, context?: unknown) => StateUpdate<T>;
export type StoreListener = (changes: StateChangeSet) => void;
export interface Store {
dispatch(action: AnyAction): void;

View file

@ -3,6 +3,7 @@ import { StringPreEscaped } from './string-pre-escaped';
import { ChoiceFull } from './choice-full';
import { GroupFull } from './group-full';
import { Options } from './options';
import { Types } from './types';
export type TemplateOptions = Pick<Options, 'classNames' | 'allowHTML' | 'removeItemButtonAlignLeft' | 'removeItemIconText' | 'removeItemLabelText' | 'searchEnabled' | 'labelId'>;
export declare const NoticeTypes: {
readonly noChoices: "no-choices";
@ -10,7 +11,7 @@ export declare const NoticeTypes: {
readonly addChoice: "add-choice";
readonly generic: "";
};
export type NoticeType = (typeof NoticeTypes)[keyof typeof NoticeTypes];
export type NoticeType = Types.ValueOf<typeof NoticeTypes>;
export interface Templates {
containerOuter(options: TemplateOptions, dir: HTMLElement['dir'], isSelectElement: boolean, isSelectOneElement: boolean, searchEnabled: boolean, passedElementType: PassedElementType, labelId: string): HTMLDivElement;
containerInner({ classNames: { containerInner } }: TemplateOptions): HTMLDivElement;
@ -19,7 +20,7 @@ export interface Templates {
item(options: TemplateOptions, choice: ChoiceFull, removeItemButton: boolean): HTMLDivElement;
choiceList(options: TemplateOptions, isSelectOneElement: boolean): HTMLDivElement;
choiceGroup(options: TemplateOptions, group: GroupFull): HTMLDivElement;
choice(options: TemplateOptions, choice: ChoiceFull, selectText: string): HTMLDivElement;
choice(options: TemplateOptions, choice: ChoiceFull, selectText: string, groupText?: string): HTMLDivElement;
input(options: TemplateOptions, placeholderValue: string | null): HTMLInputElement;
dropdown(options: TemplateOptions): HTMLDivElement;
notice(options: TemplateOptions, innerText: string, type: NoticeType): HTMLDivElement;

View file

@ -12,4 +12,5 @@ export declare namespace Types {
value?: StringUntrusted | string;
label?: StringUntrusted | string;
}
type ValueOf<T extends object> = T[keyof T];
}

View file

@ -1,4 +1,4 @@
import { EventType } from '../interfaces/event-type';
import { EventTypes } from '../interfaces/event-type';
import { StringUntrusted } from '../interfaces/string-untrusted';
import { StringPreEscaped } from '../interfaces/string-pre-escaped';
import { ChoiceFull } from '../interfaces/choice-full';
@ -17,7 +17,7 @@ export declare const setElementHtml: (el: HTMLElement, allowHtml: boolean, html:
export declare const sortByAlpha: ({ value, label }: Types.RecordToCompare, { value: value2, label: label2 }: Types.RecordToCompare) => number;
export declare const sortByScore: (a: Pick<ChoiceFull, "score">, b: Pick<ChoiceFull, "score">) => number;
export declare const sortByRank: (a: Pick<ChoiceFull, "rank">, b: Pick<ChoiceFull, "rank">) => number;
export declare const dispatchEvent: (element: HTMLElement, type: EventType, customArgs?: object | null) => boolean;
export declare const dispatchEvent: (element: HTMLElement, type: EventTypes, customArgs?: object | null) => boolean;
export declare const cloneObject: <T>(obj: T) => T;
/**
* Returns an array of keys present on the first but missing on the second object
@ -26,3 +26,4 @@ export declare const diff: (a: Record<string, any>, b: Record<string, any>) => s
export declare const getClassNames: (ClassNames: Array<string> | string) => Array<string>;
export declare const getClassNamesSelector: (option: string | Array<string> | null) => string;
export declare const parseCustomProperties: (customProperties?: string) => object | string;
export declare const updateClassList: (item: ChoiceFull, add: string | string[], remove: string | string[]) => void;

View file

@ -1,8 +1,8 @@
import { State } from '../interfaces';
import { Options, State } from '../interfaces';
import { StateUpdate } from '../interfaces/store';
import { ChoiceActions } from '../actions/choices';
import { ItemActions } from '../actions/items';
type ActionTypes = ChoiceActions | ItemActions;
type StateType = State['choices'];
export default function choices(s: StateType, action: ActionTypes): StateUpdate<StateType>;
export default function choices(s: StateType, action: ActionTypes, context?: Options): StateUpdate<StateType>;
export {};

View file

@ -1,8 +1,9 @@
import { ItemActions } from '../actions/items';
import { State } from '../interfaces/state';
import { ChoiceActions } from '../actions/choices';
import { Options } from '../interfaces';
import { StateUpdate } from '../interfaces/store';
type ActionTypes = ChoiceActions | ItemActions;
type StateType = State['items'];
export default function items(s: StateType, action: ActionTypes): StateUpdate<StateType>;
export default function items(s: StateType, action: ActionTypes, context?: Options): StateUpdate<StateType>;
export {};

View file

@ -2,11 +2,13 @@ import { AnyAction, Store as IStore, StoreListener } from '../interfaces/store';
import { StateChangeSet, State } from '../interfaces/state';
import { ChoiceFull } from '../interfaces/choice-full';
import { GroupFull } from '../interfaces/group-full';
export default class Store implements IStore {
export default class Store<T> implements IStore {
_state: State;
_listeners: StoreListener[];
_txn: number;
_changeSet?: StateChangeSet;
_context: T;
constructor(context: T);
get defaultState(): State;
changeSet(init: boolean): StateChangeSet;
reset(): void;