@vuu-ui/vuu-ui-controls 4.0.0-alpha.2 → 4.0.0-alpha.20

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.
@@ -1,75 +1,85 @@
1
- import { isValidTimeString } from "@vuu-ui/vuu-utils";
2
- import { useCallback, useMemo, useRef } from "react";
1
+ import { isValidTimeString, isValidTimeStringMillis } from "@vuu-ui/vuu-utils";
2
+ import { useCallback, useRef } from "react";
3
3
  import { MaskedInput } from "./MaskedInput.js";
4
4
  const isDigit = (char)=>1 === char.length && /[0-9]/.test(char);
5
- const useTimeInput = ({ defaultValue, onChange, onCommit, value })=>{
5
+ const useTimeInput = ({ defaultValue, milliseconds = false, onChange, onCommit: onCommitProp, value })=>{
6
+ const onCommit = onCommitProp;
6
7
  const mousedDownRef = useRef(false);
8
+ const onChangeRef = useRef(onChange);
9
+ onChangeRef.current = onChange;
7
10
  const maskedInputRef = useRef(void 0);
8
- useMemo(()=>{
9
- if (void 0 === maskedInputRef.current) {
10
- maskedInputRef.current = new MaskedInput(defaultValue, null);
11
- maskedInputRef.current.on("change", (e)=>{
12
- onChange?.(e);
13
- });
14
- }
15
- if (value && value !== maskedInputRef.current.value) maskedInputRef.current.value = value;
11
+ if (void 0 === maskedInputRef.current || maskedInputRef.current.milliseconds !== milliseconds) {
12
+ maskedInputRef.current = new MaskedInput(value ?? maskedInputRef.current?.value ?? defaultValue, null, {
13
+ milliseconds
14
+ });
15
+ maskedInputRef.current.on("change", (e)=>{
16
+ onChangeRef.current?.(e);
17
+ });
18
+ }
19
+ const maskedInput = maskedInputRef.current;
20
+ if (void 0 !== value) maskedInput.value = value;
21
+ const setInputEl = useCallback((el)=>{
22
+ maskedInput.input = el;
16
23
  }, [
17
- defaultValue,
18
- onChange,
19
- value
24
+ maskedInput
20
25
  ]);
21
- const setInputEl = useCallback((el)=>{
22
- if (el && maskedInputRef.current) maskedInputRef.current.input = el;
23
- }, []);
24
- const back = useRef(false);
25
- const commitValue = useCallback((evt, value)=>{
26
- if (isValidTimeString(value)) onCommit(evt, value, "text-input");
27
- else if ("hh:mm:ss" === value) console.log("no value set");
28
- else console.log("value is not valid");
26
+ const commitValue = useCallback((evt)=>{
27
+ if ("" !== evt.currentTarget.value) {
28
+ const { value } = maskedInput;
29
+ if (milliseconds ? isValidTimeStringMillis(value) : isValidTimeString(value)) onCommit(evt, value, "text-input");
30
+ }
29
31
  }, [
32
+ maskedInput,
33
+ milliseconds,
30
34
  onCommit
31
35
  ]);
32
36
  const handleKeyDown = useCallback((e)=>{
33
- const { current: maskedInput } = maskedInputRef;
34
- if (maskedInput) {
35
- if ("Backspace" === e.key) {
36
- maskedInput.backspace();
37
- back.current = true;
38
- } else if (isDigit(e.key)) maskedInput.update(e.key);
39
- else if ("ArrowLeft" === e.key) maskedInput.moveFocus("left");
40
- else if ("ArrowRight" === e.key) maskedInput.moveFocus("right");
41
- else if ("ArrowUp" === e.key) maskedInput.incrementValue();
42
- else if ("ArrowDown" === e.key) maskedInput.decrementValue();
43
- else if ("v" === e.key && e.metaKey) return;
44
- else if ("Tab" === e.key) return;
45
- else if ("Enter" === e.key) commitValue(e, maskedInput.value);
46
- }
37
+ if (e.ctrlKey || e.metaKey || e.altKey || "Tab" === e.key) return;
38
+ if ("Backspace" === e.key) maskedInput.backspace();
39
+ else if (isDigit(e.key)) maskedInput.update(e.key);
40
+ else if ("ArrowLeft" === e.key) maskedInput.moveFocus("left");
41
+ else if ("ArrowRight" === e.key) maskedInput.moveFocus("right");
42
+ else if ("ArrowUp" === e.key) maskedInput.incrementValue();
43
+ else if ("ArrowDown" === e.key) maskedInput.decrementValue();
44
+ else if ("Home" === e.key) maskedInput.selectFirst();
45
+ else if ("End" === e.key) maskedInput.selectLast();
46
+ else if ("Enter" === e.key) commitValue(e);
47
+ else if ("Escape" === e.key) return;
47
48
  e.preventDefault();
48
49
  }, [
49
- commitValue
50
+ commitValue,
51
+ maskedInput
50
52
  ]);
51
53
  const handleDoubleClick = useCallback(()=>{
52
- maskedInputRef.current?.doubleClick();
53
- }, []);
54
+ maskedInput.doubleClick();
55
+ }, [
56
+ maskedInput
57
+ ]);
54
58
  const handlePaste = useCallback((e)=>{
55
- const value = e.clipboardData.getData("text");
56
- if (isValidTimeString(value)) maskedInputRef.current?.pasteValue(value);
57
- }, []);
59
+ e.preventDefault();
60
+ maskedInput.pasteValue(e.clipboardData.getData("text"));
61
+ }, [
62
+ maskedInput
63
+ ]);
58
64
  const handleFocus = useCallback(()=>{
59
65
  if (mousedDownRef.current) mousedDownRef.current = false;
60
- else maskedInputRef.current?.focus();
61
- }, []);
66
+ else maskedInput.focus();
67
+ }, [
68
+ maskedInput
69
+ ]);
62
70
  const handleMouseDown = useCallback(()=>{
63
71
  mousedDownRef.current = true;
64
72
  }, []);
65
73
  const handleMouseUp = useCallback((e)=>{
66
74
  e.preventDefault();
67
- maskedInputRef.current?.click();
68
- }, []);
75
+ maskedInput.click();
76
+ }, [
77
+ maskedInput
78
+ ]);
69
79
  return {
70
80
  inputRef: setInputEl,
71
81
  eventHandlers: {
72
- onBlur: maskedInputRef.current?.blur,
82
+ onBlur: maskedInput.blur,
73
83
  onDoubleClick: handleDoubleClick,
74
84
  onFocus: handleFocus,
75
85
  onKeyDown: handleKeyDown,
@@ -1,84 +1,78 @@
1
- import { createElement, useCallback, useRef, useState } from "react";
2
- import { getLocalTimeZone, isSameDay, isSameMonth, isSameYear } from "@internationalized/date";
1
+ import { createElement, useCallback, useMemo, useRef, useState } from "react";
2
+ import { EpochTimestamp, fromDateTimeFields } from "@vuu-ui/vuu-utils";
3
+ import { CalendarDate } from "@internationalized/date";
3
4
  import clsx from "clsx";
4
5
  import { DatePicker } from "../date-picker/index.js";
5
6
  const classBase = "VuuDatePicker";
6
- const isSameDate = (d1, d2)=>void 0 !== d2 && isSameDay(d1, d2) && isSameMonth(d1, d2) && isSameYear(d1, d2);
7
7
  const datePattern = /^\d{1,2} [a-z]{3} \d{4}$/i;
8
- const isValidDate = (value)=>void 0 !== value && value?.match(datePattern) !== null && !Number.isNaN(new Date(value).getDay());
9
- const localTimeZone = getLocalTimeZone();
10
- const toEpochMillis = (date, timezone = getLocalTimeZone())=>date.toDate(timezone).getTime();
11
- const getDates = (selectedDate)=>{
12
- if (selectedDate) return {
13
- datePickerKey: "controlled",
14
- defaultDate: void 0,
15
- selectedDate
16
- };
17
- return {
18
- datePickerKey: "uncontrolled",
19
- defaultDate: void 0,
20
- selectedDate
21
- };
8
+ const isValidDate = (value)=>void 0 !== value && null !== value.match(datePattern) && !Number.isNaN(new Date(value).getDay());
9
+ const isSameDate = (d1, d2)=>void 0 !== d2 && 0 === d1.compare(d2);
10
+ const toCalendarDateInTimeZone = (value, timeZone)=>{
11
+ const timestamp = value instanceof EpochTimestamp ? value : EpochTimestamp.fromWire(value, "epochMillis");
12
+ if (timestamp) {
13
+ const { year, month, day } = timestamp.getFields(timeZone);
14
+ return new CalendarDate(year, month, day);
15
+ }
22
16
  };
23
- const VuuDatePicker = ({ className, onSelectionChange, selectedDate: selectedDateProp, onCommit, preserveFocusOnSelect, ...props })=>{
24
- const [open, _setOpen] = useState(false);
25
- const valueRef = useRef("");
17
+ const startOfDateInTimeZone = (date, timeZone)=>fromDateTimeFields({
18
+ year: date.year,
19
+ month: date.month,
20
+ day: date.day,
21
+ hour: 0,
22
+ minute: 0,
23
+ second: 0,
24
+ millisecond: 0
25
+ }, timeZone);
26
+ const VuuDatePicker = ({ className, onSelectionChange, onCommit, preserveFocusOnSelect, selectedDate: selectedDateProp, timeZone, value, ...props })=>{
27
+ const [open, setOpen] = useState(false);
28
+ const inputValueRef = useRef("");
26
29
  const datePickerRef = useRef(null);
27
- const dateState = useRef(getDates(selectedDateProp));
28
- const setOpen = (o)=>{
29
- console.log(`setOpen ${o}`);
30
- _setOpen(o);
31
- };
30
+ const controlledDate = useMemo(()=>selectedDateProp ?? toCalendarDateInTimeZone(value, timeZone), [
31
+ selectedDateProp,
32
+ timeZone,
33
+ value
34
+ ]);
35
+ const [selectedDate, setSelectedDate] = useState(controlledDate);
36
+ const controlledDateRef = useRef(controlledDate);
37
+ if (void 0 !== controlledDate && (void 0 === controlledDateRef.current || 0 !== controlledDate.compare(controlledDateRef.current))) {
38
+ controlledDateRef.current = controlledDate;
39
+ if (!isSameDate(controlledDate, selectedDate)) setSelectedDate(controlledDate);
40
+ }
32
41
  const commitDateChange = useCallback((e, date)=>{
33
42
  onSelectionChange?.(e, date);
34
43
  setOpen(false);
35
- onCommit?.(e, toEpochMillis(date, localTimeZone));
44
+ onCommit?.(e, startOfDateInTimeZone(date, timeZone));
36
45
  if (preserveFocusOnSelect) requestAnimationFrame(()=>{
37
46
  datePickerRef.current?.querySelector("input")?.focus();
38
47
  });
39
48
  }, [
40
49
  onCommit,
41
50
  onSelectionChange,
42
- preserveFocusOnSelect
51
+ preserveFocusOnSelect,
52
+ timeZone
43
53
  ]);
44
54
  const handleSelectionChange = useCallback((e, date)=>{
45
- const { selectedDate } = dateState.current;
46
- if (date) {
47
- if (void 0 === selectedDate) {
48
- dateState.current = getDates(date);
49
- commitDateChange(e, date);
50
- } else if (!isSameDate(date, selectedDate)) {
51
- dateState.current.selectedDate = date;
52
- commitDateChange(e, date);
53
- }
55
+ if (date && !isSameDate(date, selectedDate)) {
56
+ setSelectedDate(date);
57
+ commitDateChange(e, date);
54
58
  }
55
59
  }, [
56
- commitDateChange
60
+ commitDateChange,
61
+ selectedDate
57
62
  ]);
58
- const handleChange = (evt, value = "")=>{
59
- console.log("handleChange", {
60
- evt,
61
- value
62
- });
63
- valueRef.current = value;
64
- console.log(`is '${value}' a valid date ? ${isValidDate(value)}`);
63
+ const handleChange = (_evt, value = "")=>{
64
+ inputValueRef.current = value;
65
65
  };
66
66
  const handleKeyDown = (e)=>{
67
67
  if ("ArrowDown" === e.key) setOpen(true);
68
68
  };
69
69
  const handleKeyDownCapture = (e)=>{
70
- if ("Enter" === e.key) if (isValidDate(valueRef.current)) console.log(`committayaface (${valueRef.current})`);
71
- else {
72
- console.log(`nice try cowboy, but '${valueRef.current}' is nota valid date`);
73
- e.stopPropagation();
74
- }
70
+ if ("Enter" === e.key && "" !== inputValueRef.current && !isValidDate(inputValueRef.current)) e.stopPropagation();
75
71
  };
76
- const { datePickerKey, defaultDate, selectedDate } = dateState.current;
77
72
  return /*#__PURE__*/ createElement(DatePicker, {
78
73
  ...props,
79
74
  className: clsx(classBase, className),
80
- defaultSelectedDate: defaultDate,
81
- key: datePickerKey,
75
+ key: selectedDate ? "controlled" : "uncontrolled",
82
76
  open: open,
83
77
  onChange: handleChange,
84
78
  onKeyDown: handleKeyDown,
@@ -5,7 +5,7 @@ import clsx from "clsx";
5
5
  import { TimeInput } from "../time-input/TimeInput.js";
6
6
  import VuuTimePicker from "./VuuTimePicker.css";
7
7
  const classBase = "vuuTimePicker";
8
- const VuuTimePicker_VuuTimePicker = ({ className, defaultValue, onChange, onCommit, value, ...htmlAttributes })=>{
8
+ const VuuTimePicker_VuuTimePicker = ({ className, defaultValue, milliseconds, onChange, onCommit, value, ...htmlAttributes })=>{
9
9
  const targetWindow = useWindow();
10
10
  useComponentCssInjection({
11
11
  testId: "vuu-time-picker",
@@ -16,10 +16,11 @@ const VuuTimePicker_VuuTimePicker = ({ className, defaultValue, onChange, onComm
16
16
  ...htmlAttributes,
17
17
  className: clsx(classBase, className),
18
18
  children: /*#__PURE__*/ jsx(TimeInput, {
19
- defaultValue: defaultValue,
20
- onChange: onChange,
21
- onCommit: onCommit,
22
- value: value
19
+ defaultValue,
20
+ milliseconds,
21
+ onChange,
22
+ onCommit,
23
+ value
23
24
  })
24
25
  });
25
26
  };
package/types/index.d.ts CHANGED
@@ -13,9 +13,8 @@ export { HoverOverlay } from "./hover-overlay/HoverOverlay";
13
13
  export * from "./icon-button";
14
14
  export * from "./instrument-picker";
15
15
  export { ItemPicker, type ItemPickerProps } from "./item-picker/ItemPicker";
16
- export { type ItemDescriptor, type CreateCustomItemProps, } from "./item-picker/useItemPicker";
16
+ export type { ItemDescriptor, SelectedItemsChangeHandler as SelectedItemChangeHandler } from "./item-picker/useItemPicker";
17
17
  export * from "./measured-container";
18
- export { ModalProvider, useModal } from "./modal-provider/ModalProvider";
19
18
  export * from "./overflow-container";
20
19
  export * from "./price-ticker";
21
20
  export { Prompt, type PromptProps } from "./prompt/Prompt";
@@ -24,10 +23,10 @@ export * from "./split-button";
24
23
  export * from "./table-search";
25
24
  export * from "./tabs-next";
26
25
  export * from "./tabstrip";
27
- export { TimeInput, type TimeInputProps } from "./time-input/TimeInput";
26
+ export { TimeInput, type TimeInputMillisecondsProps, type TimeInputProps, type TimeInputSecondsProps, } from "./time-input/TimeInput";
28
27
  export * from "./toolbar";
29
28
  export * from "./utils";
30
29
  export * from "./vuu-date-picker";
31
30
  export * from "./vuu-input";
32
- export { VuuTimePicker, type VuuTimePickerProps, } from "./vuu-time-picker/VuuTimePicker";
31
+ export { VuuTimePicker, type VuuTimePickerMillisecondsProps, type VuuTimePickerProps, type VuuTimePickerSecondsProps, } from "./vuu-time-picker/VuuTimePicker";
33
32
  export * from "./vuu-typeahead-input";
@@ -1,16 +1,34 @@
1
- import { ListBoxProps } from "@salt-ds/core";
2
- import { ItemTypeName } from "@vuu-ui/vuu-utils";
3
- import { HTMLAttributes } from "react";
4
- import { CreateCustomItemProps, ItemDescriptor, ItemPickerHookProps } from "./useItemPicker";
1
+ import { type ListBoxProps, type OptionProps } from "@salt-ds/core";
2
+ import { type ItemTypeName } from "@vuu-ui/vuu-utils";
3
+ import { type ComponentType, type HTMLAttributes, type MouseEventHandler } from "react";
4
+ import { type ItemDescriptor, type ItemPickerHookProps } from "./useItemPicker";
5
5
  export declare const classBaseListItem = "vuuItemPickerListItem";
6
+ export type ItemPickerLayout = 'v-available-selected' | 'v-selected-available';
6
7
  export interface ItemPickerProps extends ItemPickerHookProps, HTMLAttributes<HTMLDivElement>, Pick<ListBoxProps<ItemDescriptor>, "selected" | "onSelectionChange"> {
7
- itemTypeName: ItemTypeName;
8
- /** Render the search control as a form. Set false when the picker is nested in a form. */
8
+ AvailableListItem?: ComponentType<AvailableListItemProps>;
9
+ SelectedListItem?: ComponentType<SelectedListItemProps>;
10
+ itemTypeName?: ItemTypeName;
11
+ /**
12
+ * Display order of SelectedItems List / AvailableItems List
13
+ */
14
+ layout?: ItemPickerLayout;
9
15
  searchForm?: boolean;
10
- createCustomItemProps?: CreateCustomItemProps;
16
+ }
17
+ interface SelectedListItemProps extends OptionProps {
18
+ index: number;
19
+ item: ItemDescriptor;
20
+ onRemove: MouseEventHandler<HTMLButtonElement>;
21
+ searchPattern?: Lowercase<string>;
22
+ }
23
+ interface AvailableListItemProps extends OptionProps {
24
+ index: number;
25
+ item: ItemDescriptor;
26
+ onAdd: MouseEventHandler<HTMLButtonElement>;
27
+ searchPattern?: Lowercase<string>;
11
28
  }
12
29
  /** Generic controlled component that displays items in a 'picker' that are available to search, select, reorder or deselect.
13
30
  * As input props, all available items, currently selected items, search text and callback functions for changes in selected items and search text
14
31
  * must be supplied by the client.
15
32
  */
16
33
  export declare const ItemPicker: import("react").ForwardRefExoticComponent<ItemPickerProps & import("react").RefAttributes<HTMLDivElement>>;
34
+ export {};
@@ -1,4 +1,5 @@
1
- import { FormEventHandler, MouseEventHandler } from "react";
1
+ import { type ChangeEventHandler, type MouseEventHandler } from "react";
2
+ export type SelectedItemsChangeHandler = (newSelectedItems: readonly ItemDescriptor[]) => void;
2
3
  /** This is a public description of an Item that can be displayed in the ItemPicker component, defining all the
3
4
  * mandatory and option attributes that can be defined by the client. */
4
5
  export interface ItemDescriptor {
@@ -11,15 +12,11 @@ export interface ItemDescriptor {
11
12
  /** Optional string to define groups of items for client reference, not currently used for rendering */
12
13
  group?: string;
13
14
  }
14
- export interface CreateCustomItemProps {
15
- buttonLabel: string;
16
- onClickCreateCustomItem: MouseEventHandler<HTMLButtonElement>;
17
- }
18
15
  export interface ItemPickerHookProps {
19
16
  allItems: ItemDescriptor[];
20
17
  selectedItems: ItemDescriptor[];
21
18
  maxSelections?: number;
22
- onSelectedItemsChange: (newSelectedItems: ItemDescriptor[]) => void;
19
+ onSelectedItemsChange: SelectedItemsChangeHandler;
23
20
  }
24
21
  export declare const getItemLabel: (item: ItemDescriptor) => string;
25
22
  export declare const useItemPicker: ({ allItems, selectedItems, maxSelections, onSelectedItemsChange, }: ItemPickerHookProps) => {
@@ -28,7 +25,7 @@ export declare const useItemPicker: ({ allItems, selectedItems, maxSelections, o
28
25
  selectedItemsFiltered: readonly ItemDescriptor[];
29
26
  availableItemsFiltered: ItemDescriptor[];
30
27
  searchText: string;
31
- onChangeSearchInput: FormEventHandler;
28
+ onChangeSearchInput: ChangeEventHandler;
32
29
  onAddItemToSelectedList: MouseEventHandler<HTMLButtonElement>;
33
30
  onRemoveItemFromSelectedList: MouseEventHandler<HTMLButtonElement>;
34
31
  onReorderSelectedItems: (orderedItemNames: string[]) => void;
@@ -4,7 +4,7 @@ export type ShowDialog = (dialogContent: ReactElement, title: string, dialogActi
4
4
  export type ShowPrompt = (promptContent: ReactElement, promptProps: PromptProps) => void;
5
5
  export declare const ModalProvider: ({ children }: {
6
6
  children: ReactNode;
7
- }) => import("react").JSX.Element;
7
+ }) => import("react/jsx-runtime").JSX.Element;
8
8
  export declare const useModal: () => {
9
9
  showDialog: ShowDialog;
10
10
  closeDialog: () => void;
@@ -1,13 +1,32 @@
1
- import { EventEmitter, TimeString, TimeUnit } from "@vuu-ui/vuu-utils";
1
+ import { EventEmitter, TimeString, TimeStringMillis, TimeUnit } from "@vuu-ui/vuu-utils";
2
2
  import { ChangeEventHandler } from "react";
3
3
  export type Digit = "0" | "1" | "2" | "3" | "4" | "5" | "6" | "7" | "8" | "9";
4
+ export type TimeValue = TimeString | TimeStringMillis;
5
+ export declare const invalidClassName = "vuuTimeInput-invalid";
6
+ export interface MaskedInputOptions {
7
+ /**
8
+ * When true, value includes milliseconds, hh:mm:ss.SSS
9
+ */
10
+ milliseconds?: boolean;
11
+ }
4
12
  export type MaskedInputEvents = {
5
13
  change: ChangeEventHandler<HTMLInputElement>;
6
14
  };
7
15
  export declare class MaskedInput extends EventEmitter<MaskedInputEvents> {
8
16
  #private;
9
- constructor(defaultValue: TimeString | undefined, inputEl?: HTMLInputElement | null);
10
- set input(el: HTMLInputElement);
17
+ constructor(defaultValue: TimeValue | undefined, inputEl?: HTMLInputElement | null, { milliseconds }?: MaskedInputOptions);
18
+ get milliseconds(): boolean;
19
+ private get zeroValue();
20
+ /**
21
+ * Length of the formatted value, hh:mm:ss or hh:mm:ss.SSS
22
+ */
23
+ get length(): 8 | 12;
24
+ private normalise;
25
+ /**
26
+ * Attach (or re-attach) the input element. Re-attaching the same element
27
+ * is a no-op, attaching a different element detaches the previous one.
28
+ */
29
+ set input(el: HTMLInputElement | null);
11
30
  /**
12
31
  * The change event is fired programatically. This will only be handled
13
32
  * by a native event handler ( not a React handler). We handle this event
@@ -21,25 +40,56 @@ export declare class MaskedInput extends EventEmitter<MaskedInputEvents> {
21
40
  set selectionStart(value: number);
22
41
  get selectionEnd(): number;
23
42
  set selectionEnd(value: number);
43
+ private nextUnit;
44
+ private previousUnit;
45
+ private get firstUnit();
46
+ private get lastUnit();
24
47
  private setValue;
25
- private setUnitValue;
26
48
  private getUnitValue;
27
- get value(): TimeString;
49
+ private setUnitValue;
50
+ private setInvalid;
51
+ get value(): TimeValue;
28
52
  /**
29
- * Setting the value this way invokes 'controlled' mode
53
+ * Setting the value this way invokes 'controlled' mode. Value
54
+ * will be normalised to the configured precision.
30
55
  */
31
- set value(value: TimeString);
56
+ set value(value: TimeValue);
32
57
  clear(unit: TimeUnit): void;
33
- select(unit: TimeUnit, halfUnit?: boolean): void;
58
+ /**
59
+ * Select a unit. If digitIndex > 0, only the digits of the unit
60
+ * not yet entered are selected.
61
+ */
62
+ select(unit: TimeUnit, digitIndex?: number): void;
34
63
  removeSelection(): void;
35
64
  restoreSelection(): void;
36
- advanceSelection(): void;
65
+ selectFirst(): void;
66
+ selectLast(): void;
37
67
  moveFocus(direction: "left" | "right"): void;
38
- pasteValue(value: TimeString): void;
68
+ /**
69
+ * Replace the entire value, e.g. from a paste. Values with or without
70
+ * milliseconds are accepted and converted to the configured precision.
71
+ * Invalid values are ignored. Returns true if value was accepted.
72
+ */
73
+ pasteValue(value: string): boolean;
39
74
  private getUnitAtCursorPos;
75
+ private get activeUnit();
40
76
  incrementValue(): void;
41
77
  decrementValue(): void;
78
+ /**
79
+ * If a full unit is selected, it is cleared and selection moves to the
80
+ * previous unit. If we are midway through entering a unit, the unit is
81
+ * cleared and remains selected.
82
+ */
42
83
  backspace(): void;
84
+ /**
85
+ * Typing the first digit of a unit sets the unit to that digit followed
86
+ * by zeros, e.g '1' => '10' and selection moves to the next digit. If
87
+ * the digit cannot be the first digit of a valid unit, e.g '3' for hours,
88
+ * the unit is zero padded, e.g '03', and selection advances to the next unit.
89
+ * Typing the last digit completes the unit and selection advances to the
90
+ * next unit. A digit which would produce an invalid value is rejected and
91
+ * the input is marked as invalid.
92
+ */
43
93
  update(key: Digit): void;
44
94
  private getSelection;
45
95
  click(): void;
@@ -1,5 +1,6 @@
1
1
  import { HTMLAttributes } from "react";
2
- import { TimeInputHookProps } from "./useTimeInput";
3
- export interface TimeInputProps extends TimeInputHookProps, Omit<HTMLAttributes<HTMLInputElement>, "defaultValue" | "onChange" | "value">, Partial<Pick<HTMLInputElement, "placeholder">> {
4
- }
2
+ import { TimeInputHookProps, TimeInputMillisecondsProps, TimeInputSecondsProps } from "./useTimeInput";
3
+ type TimeInputHtmlAttributes = Omit<HTMLAttributes<HTMLInputElement>, "defaultValue" | "onChange" | "value"> & Partial<Pick<HTMLInputElement, "placeholder">>;
4
+ export type { TimeInputMillisecondsProps, TimeInputSecondsProps };
5
+ export type TimeInputProps = TimeInputHookProps & TimeInputHtmlAttributes;
5
6
  export declare const TimeInput: import("react").ForwardRefExoticComponent<TimeInputProps & import("react").RefAttributes<HTMLInputElement>>;
@@ -1,16 +1,34 @@
1
- import { type CommitHandler, DateStringISO, type TimeString } from "@vuu-ui/vuu-utils";
1
+ import { type CommitHandler, DateStringISO, type TimeString, type TimeStringMillis } from "@vuu-ui/vuu-utils";
2
2
  import { ChangeEventHandler, ClipboardEventHandler, FocusEventHandler, KeyboardEventHandler, MouseEventHandler, RefCallback } from "react";
3
- export interface TimeInputHookProps {
3
+ interface TimeInputCommonProps {
4
4
  date?: Date | DateStringISO;
5
- defaultValue?: TimeString;
6
5
  onChange?: ChangeEventHandler<HTMLInputElement>;
6
+ }
7
+ export interface TimeInputSecondsProps extends TimeInputCommonProps {
8
+ defaultValue?: TimeString;
9
+ /**
10
+ * When true, time is entered and displayed with millisecond
11
+ * precision, hh:mm:ss.SSS
12
+ */
13
+ milliseconds?: false;
7
14
  onCommit: CommitHandler<HTMLInputElement, TimeString>;
8
15
  value?: TimeString;
9
16
  }
10
- export declare const useTimeInput: ({ defaultValue, onChange, onCommit, value, }: TimeInputHookProps) => {
17
+ export interface TimeInputMillisecondsProps extends TimeInputCommonProps {
18
+ defaultValue?: TimeStringMillis;
19
+ /**
20
+ * When true, time is entered and displayed with millisecond
21
+ * precision, hh:mm:ss.SSS
22
+ */
23
+ milliseconds: true;
24
+ onCommit: CommitHandler<HTMLInputElement, TimeStringMillis>;
25
+ value?: TimeStringMillis;
26
+ }
27
+ export type TimeInputHookProps = TimeInputSecondsProps | TimeInputMillisecondsProps;
28
+ export declare const useTimeInput: ({ defaultValue, milliseconds, onChange, onCommit: onCommitProp, value, }: TimeInputHookProps) => {
11
29
  inputRef: RefCallback<HTMLInputElement>;
12
30
  eventHandlers: {
13
- onBlur: (() => void) | undefined;
31
+ onBlur: () => void;
14
32
  onDoubleClick: () => void;
15
33
  onFocus: FocusEventHandler<HTMLInputElement>;
16
34
  onKeyDown: KeyboardEventHandler<HTMLInputElement>;
@@ -19,3 +37,4 @@ export declare const useTimeInput: ({ defaultValue, onChange, onCommit, value, }
19
37
  onPaste: ClipboardEventHandler<HTMLInputElement>;
20
38
  };
21
39
  };
40
+ export {};
@@ -1,7 +1,23 @@
1
- import { CommitHandler } from "@vuu-ui/vuu-utils";
1
+ import { type CommitHandler, EpochTimestamp, type TimeZoneSpec } from "@vuu-ui/vuu-utils";
2
2
  import { SingleSelectionValueType } from "../calendar";
3
3
  import { DatePickerProps } from "../date-picker";
4
- export declare const VuuDatePicker: ({ className, onSelectionChange, selectedDate: selectedDateProp, onCommit, preserveFocusOnSelect, ...props }: Omit<DatePickerProps<SingleSelectionValueType>, "defaultSelectedDate"> & {
4
+ export interface VuuDatePickerProps extends Omit<DatePickerProps<SingleSelectionValueType>, "defaultSelectedDate" | "onChange"> {
5
+ /**
6
+ * Invoked when user selects a date from the calendar, or enters a valid date and
7
+ * presses Enter. Value is epoch millis at the start of the selected day, in timeZone.
8
+ */
5
9
  onCommit?: CommitHandler<HTMLElement, number>;
6
10
  preserveFocusOnSelect?: boolean;
7
- }) => import("react").JSX.Element;
11
+ /**
12
+ * The time zone used to convert between epoch values and calendar dates.
13
+ * Defaults to the application default time zone (see setDefaultTimeZone).
14
+ */
15
+ timeZone?: TimeZoneSpec;
16
+ /**
17
+ * An epoch value (millis as number, or millis/nanos as EpochTimestamp). An
18
+ * alternative to selectedDate, when the date is derived from a timestamp. The
19
+ * calendar date is determined in timeZone.
20
+ */
21
+ value?: EpochTimestamp | number;
22
+ }
23
+ export declare const VuuDatePicker: ({ className, onSelectionChange, onCommit, preserveFocusOnSelect, selectedDate: selectedDateProp, timeZone, value, ...props }: VuuDatePickerProps) => import("react").JSX.Element;
@@ -1,7 +1,9 @@
1
- import { CommitHandler, TimeString } from "@vuu-ui/vuu-utils";
2
1
  import { HTMLAttributes } from "react";
3
- import { TimeInputProps } from "../time-input/TimeInput";
4
- export interface VuuTimePickerProps extends Pick<TimeInputProps, "defaultValue" | "onChange" | "value">, Omit<HTMLAttributes<HTMLDivElement>, "defaultValue" | "onChange" | "value"> {
5
- onCommit: CommitHandler<HTMLInputElement, TimeString>;
6
- }
7
- export declare const VuuTimePicker: ({ className, defaultValue, onChange, onCommit, value, ...htmlAttributes }: VuuTimePickerProps) => import("react").JSX.Element;
2
+ import { TimeInputMillisecondsProps, TimeInputSecondsProps } from "../time-input/TimeInput";
3
+ type VuuTimePickerHtmlAttributes = Omit<HTMLAttributes<HTMLDivElement>, "defaultValue" | "onChange" | "value">;
4
+ type TimePickerInputProps = "defaultValue" | "milliseconds" | "onChange" | "onCommit" | "value";
5
+ export type VuuTimePickerSecondsProps = Pick<TimeInputSecondsProps, TimePickerInputProps> & VuuTimePickerHtmlAttributes;
6
+ export type VuuTimePickerMillisecondsProps = Pick<TimeInputMillisecondsProps, TimePickerInputProps> & VuuTimePickerHtmlAttributes;
7
+ export type VuuTimePickerProps = VuuTimePickerSecondsProps | VuuTimePickerMillisecondsProps;
8
+ export declare const VuuTimePicker: ({ className, defaultValue, milliseconds, onChange, onCommit, value, ...htmlAttributes }: VuuTimePickerProps) => import("react").JSX.Element;
9
+ export {};