@camstack/ui-library 1.2.344 → 1.2.345
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +9 -0
- package/dist/composites/data-table-filter-bar.d.ts +1 -1
- package/dist/composites/device-controls/climate-panel.d.ts +9 -0
- package/dist/composites/device-picker/device-provider-label.d.ts +22 -0
- package/dist/hotkeys/combo.d.ts +24 -0
- package/dist/hotkeys/hotkey-provider.d.ts +37 -0
- package/dist/hotkeys/index.d.ts +6 -0
- package/dist/hotkeys/use-search-field-hotkey.d.ts +13 -0
- package/dist/index.cjs +355 -16
- package/dist/index.d.ts +1 -0
- package/dist/index.js +348 -17
- package/dist/primitives/input.d.ts +6 -0
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,15 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [1.2.345] - 2026-10-28
|
|
11
|
+
|
|
12
|
+
### Changed
|
|
13
|
+
|
|
14
|
+
- The device selector shows which addon and integration provides each device
|
|
15
|
+
- The climate control keeps the set temperature steady when it gains decimals, and the preset menu has no inner border
|
|
16
|
+
- Scrolling inside a dialog stays inside the dialog and no longer reaches the page behind it
|
|
17
|
+
- ⌘F / Ctrl+F jumps to the page's search field instead of the browser's find (new useHotkey / HotkeyProvider registry)
|
|
18
|
+
|
|
10
19
|
## [1.2.344] - 2026-10-28
|
|
11
20
|
|
|
12
21
|
### Changed
|
|
@@ -3,6 +3,15 @@ import type { ControlPanelProps } from "./types.js";
|
|
|
3
3
|
import type { ClimateControlStatus, HvacMode } from "@camstack/types";
|
|
4
4
|
/** Step size for target ± buttons; defaults to 1 °C when unset or invalid. */
|
|
5
5
|
export declare function resolveClimateTempStep(tempStep: number | null | undefined): number;
|
|
6
|
+
/**
|
|
7
|
+
* Character count of the longest set-temperature the device can show, derived
|
|
8
|
+
* from its range and step (5-30 step 0.5 reserves "30.5" = 4). The big target
|
|
9
|
+
* number reserves this width so the − / + buttons do not move when the value
|
|
10
|
+
* gains decimals or loses a digit.
|
|
11
|
+
*/
|
|
12
|
+
export declare function climateTargetWidthChars(display: Pick<ClimateControlStatus, "tempStep" | "minTemp" | "maxTemp">): number;
|
|
13
|
+
/** `min-w-[Nch]` + right alignment for the big target number. */
|
|
14
|
+
export declare function climateTargetWidthClass(display: Pick<ClimateControlStatus, "tempStep" | "minTemp" | "maxTemp">): string;
|
|
6
15
|
export declare function nextClimateTarget(current: number, deltaSteps: number, display: Pick<ClimateControlStatus, "tempStep" | "minTemp" | "maxTemp">): number;
|
|
7
16
|
/**
|
|
8
17
|
* Exhaustive colour map — one entry per HvacMode. Build will fail if a
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/** "Home Assistant (Casa)", or "Home Assistant" without an instance name. */
|
|
2
|
+
export declare function formatProviderLabel(addonName: string, integrationName: string | null | undefined): string;
|
|
3
|
+
export interface ProviderFleetRow {
|
|
4
|
+
readonly id: number;
|
|
5
|
+
readonly addonId: string;
|
|
6
|
+
readonly integrationId?: string | undefined;
|
|
7
|
+
}
|
|
8
|
+
export interface ProviderAddonRow {
|
|
9
|
+
readonly id: string;
|
|
10
|
+
readonly name: string | undefined;
|
|
11
|
+
}
|
|
12
|
+
export interface ProviderIntegrationRow {
|
|
13
|
+
readonly id: string;
|
|
14
|
+
readonly name: string;
|
|
15
|
+
}
|
|
16
|
+
/** `deviceId → provider label`; a device whose addon is unknown is absent. */
|
|
17
|
+
export declare function providerLabelsOf(fleet: readonly ProviderFleetRow[], addons: readonly ProviderAddonRow[], integrations: readonly ProviderIntegrationRow[]): ReadonlyMap<number, string>;
|
|
18
|
+
/**
|
|
19
|
+
* The labels for every device in the fleet; empty until the reads answer (a
|
|
20
|
+
* failed read draws no provider line — it is a hint, never a gate).
|
|
21
|
+
*/
|
|
22
|
+
export declare function useDeviceProviderLabels(enabled: boolean): ReadonlyMap<number, string>;
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/** Keys a combo may name. Extend this union to support a new key. */
|
|
2
|
+
export type HotkeyKey = "a" | "b" | "c" | "d" | "e" | "f" | "g" | "h" | "i" | "j" | "k" | "l" | "m" | "n" | "o" | "p" | "q" | "r" | "s" | "t" | "u" | "v" | "w" | "x" | "y" | "z" | "0" | "1" | "2" | "3" | "4" | "5" | "6" | "7" | "8" | "9" | "escape" | "enter" | "/" | "?";
|
|
3
|
+
/** `mod` is Command on macOS and Ctrl everywhere else. */
|
|
4
|
+
export type HotkeyModifier = "mod" | "shift" | "alt";
|
|
5
|
+
/** `f`, `mod+f`, `mod+shift+k` — typed, so a typo does not compile. */
|
|
6
|
+
export type HotkeyCombo = HotkeyKey | `${HotkeyModifier}+${HotkeyKey}` | `${HotkeyModifier}+${HotkeyModifier}+${HotkeyKey}`;
|
|
7
|
+
export type HotkeyPlatform = "mac" | "other";
|
|
8
|
+
export interface ParsedCombo {
|
|
9
|
+
readonly mod: boolean;
|
|
10
|
+
readonly shift: boolean;
|
|
11
|
+
readonly alt: boolean;
|
|
12
|
+
readonly key: string;
|
|
13
|
+
}
|
|
14
|
+
/** The slice of a `KeyboardEvent` the matcher reads. */
|
|
15
|
+
export interface KeyEventLike {
|
|
16
|
+
readonly key: string;
|
|
17
|
+
readonly metaKey: boolean;
|
|
18
|
+
readonly ctrlKey: boolean;
|
|
19
|
+
readonly altKey: boolean;
|
|
20
|
+
readonly shiftKey: boolean;
|
|
21
|
+
}
|
|
22
|
+
export declare function parseCombo(combo: HotkeyCombo): ParsedCombo;
|
|
23
|
+
export declare function detectPlatform(): HotkeyPlatform;
|
|
24
|
+
export declare function matchesCombo(event: KeyEventLike, combo: HotkeyCombo, platform: HotkeyPlatform): boolean;
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { type JSX, type ReactNode } from "react";
|
|
2
|
+
import { type HotkeyCombo, type HotkeyPlatform } from "./combo";
|
|
3
|
+
/** Returning `false` declines the event: nothing is prevented and the next binding is tried. */
|
|
4
|
+
export type HotkeyHandler = (event: KeyboardEvent) => void | boolean;
|
|
5
|
+
export interface UseHotkeyOptions {
|
|
6
|
+
/** Defaults to the enclosing `HotkeyScope`, else the page. */
|
|
7
|
+
readonly scope?: string;
|
|
8
|
+
readonly enabled?: boolean;
|
|
9
|
+
/** Default true — only when the handler did not decline. */
|
|
10
|
+
readonly preventDefault?: boolean;
|
|
11
|
+
/** Fire while typing in a field. Default: only combos that carry `mod`. */
|
|
12
|
+
readonly allowInInput?: boolean;
|
|
13
|
+
}
|
|
14
|
+
export declare const PAGE_SCOPE = "page";
|
|
15
|
+
/** Asked last, after the page: a global handler that yields to whatever the page registers. */
|
|
16
|
+
export declare const FALLBACK_SCOPE = "fallback";
|
|
17
|
+
export interface HotkeyProviderProps {
|
|
18
|
+
readonly children: ReactNode;
|
|
19
|
+
/** Override platform detection (tests). */
|
|
20
|
+
readonly platform?: HotkeyPlatform;
|
|
21
|
+
}
|
|
22
|
+
export declare function HotkeyProvider({ children, platform }: HotkeyProviderProps): JSX.Element;
|
|
23
|
+
/**
|
|
24
|
+
* Bind a combo for the life of the component. Without a `HotkeyProvider` above
|
|
25
|
+
* it does nothing, so the browser keeps its own shortcut.
|
|
26
|
+
*/
|
|
27
|
+
export declare function useHotkey(combo: HotkeyCombo, handler: HotkeyHandler, options?: UseHotkeyOptions): void;
|
|
28
|
+
export interface HotkeyScopeProps {
|
|
29
|
+
readonly id: string;
|
|
30
|
+
readonly children: ReactNode;
|
|
31
|
+
/** Default true. Turn off while the surface is closed. */
|
|
32
|
+
readonly enabled?: boolean;
|
|
33
|
+
/** Shadows every scope under it, the page included. Default false. */
|
|
34
|
+
readonly modal?: boolean;
|
|
35
|
+
}
|
|
36
|
+
/** Bindings made below this component belong to `id`; while enabled it is the topmost scope. */
|
|
37
|
+
export declare function HotkeyScope({ id, children, enabled, modal }: HotkeyScopeProps): JSX.Element;
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
export type { HotkeyCombo, HotkeyKey, HotkeyModifier, HotkeyPlatform, KeyEventLike } from "./combo";
|
|
2
|
+
export { detectPlatform, matchesCombo, parseCombo } from "./combo";
|
|
3
|
+
export type { HotkeyHandler, HotkeyProviderProps, HotkeyScopeProps, UseHotkeyOptions } from "./hotkey-provider";
|
|
4
|
+
export { FALLBACK_SCOPE, HotkeyProvider, HotkeyScope, useHotkey } from "./hotkey-provider";
|
|
5
|
+
export type { UseSearchFieldHotkeyOptions } from "./use-search-field-hotkey";
|
|
6
|
+
export { useSearchFieldHotkey } from "./use-search-field-hotkey";
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { type RefObject } from "react";
|
|
2
|
+
export interface UseSearchFieldHotkeyOptions {
|
|
3
|
+
/** Pass false to leave the browser's find alone for this field. Default true. */
|
|
4
|
+
readonly enabled?: boolean;
|
|
5
|
+
/** Only the app-level search passes `FALLBACK_SCOPE`: it yields to any field on the page. */
|
|
6
|
+
readonly scope?: string;
|
|
7
|
+
}
|
|
8
|
+
/**
|
|
9
|
+
* Register a search field: ⌘F / Ctrl+F and ⌘K / Ctrl+K focus and select it.
|
|
10
|
+
* A field that is hidden or disabled declines, so the browser's find still runs.
|
|
11
|
+
* The library's search inputs call this themselves.
|
|
12
|
+
*/
|
|
13
|
+
export declare function useSearchFieldHotkey(ref: RefObject<HTMLInputElement | null>, options?: UseSearchFieldHotkeyOptions): void;
|