@glasshome/widget-sdk 0.1.0
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/LICENSE +21 -0
- package/README.md +79 -0
- package/dist/create-entity.d.ts +22 -0
- package/dist/define-widget.d.ts +10 -0
- package/dist/framework/backgrounds/Glow.d.ts +24 -0
- package/dist/framework/backgrounds/WidgetSliderFill.d.ts +33 -0
- package/dist/framework/components/WidgetContent.d.ts +29 -0
- package/dist/framework/components/WidgetEmptyState.d.ts +34 -0
- package/dist/framework/components/WidgetIcon.d.ts +48 -0
- package/dist/framework/components/WidgetMetrics.d.ts +47 -0
- package/dist/framework/components/WidgetStatus.d.ts +26 -0
- package/dist/framework/components/WidgetSubtitle.d.ts +28 -0
- package/dist/framework/components/WidgetTitle.d.ts +29 -0
- package/dist/framework/components/WidgetValue.d.ts +31 -0
- package/dist/framework/core/Widget.d.ts +88 -0
- package/dist/framework/design-system/index.d.ts +8 -0
- package/dist/framework/design-system/spacing.d.ts +40 -0
- package/dist/framework/design-system/typography.d.ts +40 -0
- package/dist/framework/design-system/z-index.d.ts +23 -0
- package/dist/framework/dialogs/WidgetDialog.d.ts +47 -0
- package/dist/framework/dialogs/index.d.ts +4 -0
- package/dist/framework/gestures/cursors.d.ts +17 -0
- package/dist/framework/gestures/use-widget-gestures.d.ts +53 -0
- package/dist/framework/hooks/index.d.ts +15 -0
- package/dist/framework/hooks/use-debug-data.d.ts +46 -0
- package/dist/framework/hooks/use-widget-config.d.ts +48 -0
- package/dist/framework/hooks/use-widget-context.d.ts +46 -0
- package/dist/framework/hooks/use-widget-dialog.d.ts +45 -0
- package/dist/framework/hooks/use-widget-entity-group.d.ts +67 -0
- package/dist/framework/hooks/use-widget-entity.d.ts +53 -0
- package/dist/framework/hooks/use-widget-form.d.ts +79 -0
- package/dist/framework/hooks/use-widget-responsive.d.ts +41 -0
- package/dist/framework/index.d.ts +55 -0
- package/dist/framework/layout/WidgetStack.d.ts +28 -0
- package/dist/framework/theming/adaptive-color.d.ts +28 -0
- package/dist/framework/theming/colors.d.ts +59 -0
- package/dist/framework/theming/index.d.ts +8 -0
- package/dist/framework/types.d.ts +335 -0
- package/dist/framework/utils/cn.d.ts +28 -0
- package/dist/framework/utils/empty-state.d.ts +49 -0
- package/dist/framework/utils/entity-aggregation.d.ts +73 -0
- package/dist/framework/utils/entity-state.d.ts +103 -0
- package/dist/framework/utils/format-value.d.ts +22 -0
- package/dist/framework/utils/index.d.ts +12 -0
- package/dist/framework/utils/interpret-value.d.ts +17 -0
- package/dist/framework/variants/built-in-variants.d.ts +39 -0
- package/dist/framework/variants/index.d.ts +7 -0
- package/dist/framework/variants/variant-utils.d.ts +105 -0
- package/dist/index.d.ts +7 -0
- package/dist/index.js +1955 -0
- package/dist/theme.d.ts +21 -0
- package/dist/types.d.ts +51 -0
- package/dist/version.d.ts +5 -0
- package/dist/vite/index.d.ts +56 -0
- package/dist/vite/index.js +184 -0
- package/package.json +76 -0
- package/preview/host.tsx +89 -0
- package/preview/preview.html +49 -0
- package/tailwind-sources.css +1 -0
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { type JSX, type ParentComponent } from "solid-js";
|
|
2
|
+
export interface WidgetDialogTab {
|
|
3
|
+
id: string;
|
|
4
|
+
label: string;
|
|
5
|
+
icon: JSX.Element;
|
|
6
|
+
content: JSX.Element;
|
|
7
|
+
}
|
|
8
|
+
export interface WidgetDialogProps {
|
|
9
|
+
open: boolean;
|
|
10
|
+
onOpenChange: (open: boolean) => void;
|
|
11
|
+
title: string;
|
|
12
|
+
onSave?: () => void;
|
|
13
|
+
hasUnsavedChanges?: boolean;
|
|
14
|
+
onDelete?: () => void;
|
|
15
|
+
editContent?: JSX.Element;
|
|
16
|
+
controlsContent?: JSX.Element;
|
|
17
|
+
debugContent?: JSX.Element;
|
|
18
|
+
debugData?: string | Record<string, unknown>;
|
|
19
|
+
tabs?: WidgetDialogTab[];
|
|
20
|
+
class?: string;
|
|
21
|
+
maxWidth?: "sm" | "md" | "lg" | "xl" | "2xl" | "3xl" | "4xl";
|
|
22
|
+
defaultTab?: string;
|
|
23
|
+
headerActions?: JSX.Element;
|
|
24
|
+
ResponsiveDialog: ParentComponent<{
|
|
25
|
+
open: boolean;
|
|
26
|
+
onOpenChange: (open: boolean) => void;
|
|
27
|
+
}>;
|
|
28
|
+
ResponsiveDialogContent: ParentComponent<{
|
|
29
|
+
class?: string;
|
|
30
|
+
}>;
|
|
31
|
+
ResponsiveDialogHeader: ParentComponent<{
|
|
32
|
+
class?: string;
|
|
33
|
+
}>;
|
|
34
|
+
ResponsiveDialogTitle: ParentComponent<{
|
|
35
|
+
class?: string;
|
|
36
|
+
}>;
|
|
37
|
+
ResponsiveDialogDescription: ParentComponent<{
|
|
38
|
+
class?: string;
|
|
39
|
+
}>;
|
|
40
|
+
Button: ParentComponent<{
|
|
41
|
+
size?: string;
|
|
42
|
+
variant?: string;
|
|
43
|
+
onClick?: () => void;
|
|
44
|
+
class?: string;
|
|
45
|
+
}>;
|
|
46
|
+
}
|
|
47
|
+
export declare function WidgetDialog(props: WidgetDialogProps): JSX.Element;
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Custom SVG cursors for widget gestures.
|
|
3
|
+
* Each cursor is a data URI embedded inline — no external files needed.
|
|
4
|
+
* Hotspot coordinates are included for proper click positioning.
|
|
5
|
+
*/
|
|
6
|
+
export interface CursorDef {
|
|
7
|
+
css: string;
|
|
8
|
+
hotspotX: number;
|
|
9
|
+
hotspotY: number;
|
|
10
|
+
}
|
|
11
|
+
export declare const cursors: {
|
|
12
|
+
readonly tap: CursorDef;
|
|
13
|
+
readonly hold: CursorDef;
|
|
14
|
+
readonly slideHorizontal: CursorDef;
|
|
15
|
+
readonly slideVertical: CursorDef;
|
|
16
|
+
readonly slideAuto: CursorDef;
|
|
17
|
+
};
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Widget Gestures Hook
|
|
3
|
+
*
|
|
4
|
+
* Provides gesture handling for widgets with automatic conflict resolution.
|
|
5
|
+
* Ported from React reference, adapted for SolidJS:
|
|
6
|
+
* - Uses mutable state object instead of useRef (imperative gesture tracking)
|
|
7
|
+
* - Accepts config/orientation as accessors for reactivity
|
|
8
|
+
* - Returns native PointerEvent handlers (not React.PointerEvent)
|
|
9
|
+
* - Callers bind via on:pointerdown={handlers.onPointerDown}
|
|
10
|
+
*
|
|
11
|
+
* @example
|
|
12
|
+
* ```tsx
|
|
13
|
+
* const gestures = useWidgetGestures(
|
|
14
|
+
* () => ({
|
|
15
|
+
* tap: handleTap,
|
|
16
|
+
* hold: { action: handleHold, delay: 300 },
|
|
17
|
+
* slide: {
|
|
18
|
+
* value: brightness(),
|
|
19
|
+
* onChange: setBrightness,
|
|
20
|
+
* min: 0,
|
|
21
|
+
* max: 100,
|
|
22
|
+
* orientation: "auto",
|
|
23
|
+
* },
|
|
24
|
+
* }),
|
|
25
|
+
* () => ctx.orientation(),
|
|
26
|
+
* );
|
|
27
|
+
*
|
|
28
|
+
* return <div on:pointerdown={gestures.onPointerDown} on:pointermove={gestures.onPointerMove} on:pointerup={gestures.onPointerUp} on:pointercancel={gestures.onPointerCancel}>...</div>;
|
|
29
|
+
* ```
|
|
30
|
+
*/
|
|
31
|
+
import type { GestureConfig, WidgetOrientation } from "../types";
|
|
32
|
+
export interface GestureHandlers {
|
|
33
|
+
onPointerDown: (e: PointerEvent) => void;
|
|
34
|
+
onPointerMove: (e: PointerEvent) => void;
|
|
35
|
+
onPointerUp: (e: PointerEvent) => void;
|
|
36
|
+
onPointerCancel: (e: PointerEvent) => void;
|
|
37
|
+
/** Sets the correct cursor on the element. Bind via on:pointerenter. */
|
|
38
|
+
onPointerEnter: (e: PointerEvent) => void;
|
|
39
|
+
/** Cancel any pending hold/slide timers. Call on component unmount via onCleanup. */
|
|
40
|
+
dispose: () => void;
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Widget gestures hook with conflict resolution
|
|
44
|
+
*
|
|
45
|
+
* Features:
|
|
46
|
+
* - Axis locking: Once slide direction is detected, locks to that axis
|
|
47
|
+
* - Scroll prevention: Blocks page scroll when sliding along widget axis
|
|
48
|
+
* - Tap/hold/slide gesture support with automatic conflict resolution
|
|
49
|
+
*
|
|
50
|
+
* @param config - Accessor returning gesture configuration
|
|
51
|
+
* @param orientation - Accessor returning widget orientation (for auto slide orientation)
|
|
52
|
+
*/
|
|
53
|
+
export declare function useWidgetGestures(config: () => GestureConfig, orientation?: () => WidgetOrientation): GestureHandlers;
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Framework Hooks - Barrel Export
|
|
3
|
+
*
|
|
4
|
+
* All hooks for widget development: context, responsive, dialog, form,
|
|
5
|
+
* config, entity, entity group, debug data, and gestures.
|
|
6
|
+
*/
|
|
7
|
+
export { type GestureHandlers, useWidgetGestures } from "../gestures/use-widget-gestures";
|
|
8
|
+
export { type UseDebugDataOptions, useDebugData } from "./use-debug-data";
|
|
9
|
+
export { type UseWidgetConfigOptions, type UseWidgetConfigReturn, useWidgetConfig, } from "./use-widget-config";
|
|
10
|
+
export { type ReactiveWidgetContext, useWidgetContext, WidgetCtx } from "./use-widget-context";
|
|
11
|
+
export { useWidgetDialog, type WidgetDialogReturn } from "./use-widget-dialog";
|
|
12
|
+
export { type UseWidgetEntityOptions, type UseWidgetEntityResult, useWidgetEntity, } from "./use-widget-entity";
|
|
13
|
+
export { type AggregationPreset, type UseWidgetEntityGroupOptions, type UseWidgetEntityGroupResult, useWidgetEntityGroup, } from "./use-widget-entity-group";
|
|
14
|
+
export { type UseWidgetFormOptions, type UseWidgetFormReturn, useWidgetForm, } from "./use-widget-form";
|
|
15
|
+
export { useWidgetResponsive, type WidgetResponsiveUtils } from "./use-widget-responsive";
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Debug Data Hook
|
|
3
|
+
*
|
|
4
|
+
* Generates standardized debug data structure for widget dialogs.
|
|
5
|
+
* Provides a consistent format for troubleshooting widget configuration and entity state.
|
|
6
|
+
*
|
|
7
|
+
* @example
|
|
8
|
+
* ```tsx
|
|
9
|
+
* const debugData = useDebugData({
|
|
10
|
+
* widgetConfig: {
|
|
11
|
+
* id: config.id,
|
|
12
|
+
* type: config.type,
|
|
13
|
+
* title: config.title,
|
|
14
|
+
* entityIds: config.entityIds,
|
|
15
|
+
* },
|
|
16
|
+
* entities: () => coverEntities,
|
|
17
|
+
* customEntityFields: (entity) => ({
|
|
18
|
+
* current_position: entity.attributes?.current_position,
|
|
19
|
+
* }),
|
|
20
|
+
* });
|
|
21
|
+
* ```
|
|
22
|
+
*/
|
|
23
|
+
import { type Accessor } from "solid-js";
|
|
24
|
+
import type { EntityView } from "../types";
|
|
25
|
+
export interface UseDebugDataOptions {
|
|
26
|
+
/** Widget configuration summary */
|
|
27
|
+
widgetConfig: {
|
|
28
|
+
id: string;
|
|
29
|
+
type: string;
|
|
30
|
+
title?: string;
|
|
31
|
+
entityIds?: string[];
|
|
32
|
+
};
|
|
33
|
+
/** Reactive entities accessor */
|
|
34
|
+
entities: Accessor<EntityView[]>;
|
|
35
|
+
/** Additional data to include in debug output */
|
|
36
|
+
additionalData?: Record<string, unknown>;
|
|
37
|
+
/** Custom entity fields to include per entity */
|
|
38
|
+
customEntityFields?: (entity: EntityView) => Record<string, unknown>;
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Hook for generating standardized debug data structure
|
|
42
|
+
*
|
|
43
|
+
* @param options - Debug data configuration
|
|
44
|
+
* @returns Reactive debug data object or undefined if no entities
|
|
45
|
+
*/
|
|
46
|
+
export declare function useDebugData(options: UseDebugDataOptions): Accessor<Record<string, unknown> | undefined>;
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Widget Config Hook
|
|
3
|
+
*
|
|
4
|
+
* Generalized config management that accepts save/delete callbacks.
|
|
5
|
+
* Host-agnostic: the host application provides the actual persistence logic.
|
|
6
|
+
*
|
|
7
|
+
* @example
|
|
8
|
+
* ```tsx
|
|
9
|
+
* const { save, remove, isSaving, isDeleting } = useWidgetConfig({
|
|
10
|
+
* onSave: async (config) => {
|
|
11
|
+
* await api.saveWidgetConfig(widgetId, config);
|
|
12
|
+
* },
|
|
13
|
+
* onDelete: async () => {
|
|
14
|
+
* await api.deleteWidget(widgetId);
|
|
15
|
+
* },
|
|
16
|
+
* });
|
|
17
|
+
*
|
|
18
|
+
* // In a form submit handler:
|
|
19
|
+
* save({ title: "My Widget", entityIds: ["sensor.temp"] });
|
|
20
|
+
* ```
|
|
21
|
+
*/
|
|
22
|
+
export interface UseWidgetConfigOptions<TConfig = Record<string, unknown>> {
|
|
23
|
+
/** Callback to save widget configuration */
|
|
24
|
+
onSave?: (config: TConfig) => void | Promise<void>;
|
|
25
|
+
/** Callback to delete widget */
|
|
26
|
+
onDelete?: () => void | Promise<void>;
|
|
27
|
+
/** Callback after successful save */
|
|
28
|
+
onSaveSuccess?: () => void;
|
|
29
|
+
/** Callback after successful delete */
|
|
30
|
+
onDeleteSuccess?: () => void;
|
|
31
|
+
}
|
|
32
|
+
export interface UseWidgetConfigReturn<TConfig = Record<string, unknown>> {
|
|
33
|
+
/** Save widget configuration */
|
|
34
|
+
save: (config: TConfig) => Promise<void>;
|
|
35
|
+
/** Delete widget */
|
|
36
|
+
remove: () => Promise<void>;
|
|
37
|
+
/** Whether save is in progress */
|
|
38
|
+
isSaving: () => boolean;
|
|
39
|
+
/** Whether delete is in progress */
|
|
40
|
+
isDeleting: () => boolean;
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Hook for managing widget configuration save/delete operations
|
|
44
|
+
*
|
|
45
|
+
* @param options - Configuration with save/delete callbacks
|
|
46
|
+
* @returns Save and delete functions with loading states
|
|
47
|
+
*/
|
|
48
|
+
export declare function useWidgetConfig<TConfig = Record<string, unknown>>(options?: UseWidgetConfigOptions<TConfig>): UseWidgetConfigReturn<TConfig>;
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Widget Context Hook
|
|
3
|
+
*
|
|
4
|
+
* Provides access to widget context (size, orientation, dimensions, edit mode).
|
|
5
|
+
*
|
|
6
|
+
* @example
|
|
7
|
+
* ```tsx
|
|
8
|
+
* const ctx = useWidgetContext();
|
|
9
|
+
*
|
|
10
|
+
* return (
|
|
11
|
+
* <div>
|
|
12
|
+
* {ctx.size() !== "xs" && <DetailedMetrics />}
|
|
13
|
+
* {ctx.orientation() === "vertical" && <VerticalLayout />}
|
|
14
|
+
* </div>
|
|
15
|
+
* );
|
|
16
|
+
* ```
|
|
17
|
+
*/
|
|
18
|
+
import type { WidgetDimensions, WidgetOrientation, WidgetSize } from "../types";
|
|
19
|
+
/**
|
|
20
|
+
* Reactive widget context value
|
|
21
|
+
* Uses accessor functions for SolidJS fine-grained reactivity
|
|
22
|
+
*/
|
|
23
|
+
export interface ReactiveWidgetContext {
|
|
24
|
+
/** Widget size classification accessor */
|
|
25
|
+
size: () => WidgetSize;
|
|
26
|
+
/** Widget orientation accessor (for gestures - pure aspect ratio) */
|
|
27
|
+
orientation: () => WidgetOrientation;
|
|
28
|
+
/** Content layout direction accessor (for UI arrangement - considers height) */
|
|
29
|
+
contentLayout: () => WidgetOrientation;
|
|
30
|
+
/** Widget dimensions accessor */
|
|
31
|
+
dimensions: () => WidgetDimensions;
|
|
32
|
+
/** Whether widget is in edit mode */
|
|
33
|
+
isEditMode: () => boolean;
|
|
34
|
+
/** Update widget config (persists to host) */
|
|
35
|
+
updateConfig: (config: Record<string, unknown>) => void;
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Widget context
|
|
39
|
+
* Uses reactive accessor pattern for SolidJS
|
|
40
|
+
*/
|
|
41
|
+
export declare const WidgetCtx: import("solid-js").Context<ReactiveWidgetContext | undefined>;
|
|
42
|
+
/**
|
|
43
|
+
* Access widget context
|
|
44
|
+
* @throws Error if used outside Widget component
|
|
45
|
+
*/
|
|
46
|
+
export declare function useWidgetContext(): ReactiveWidgetContext;
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Widget Dialog Hook
|
|
3
|
+
*
|
|
4
|
+
* Provides signal-based dialog state management with optional tab tracking.
|
|
5
|
+
*
|
|
6
|
+
* @example
|
|
7
|
+
* ```tsx
|
|
8
|
+
* function MyWidget() {
|
|
9
|
+
* const { showDialog, openDialog, closeDialog } = useWidgetDialog();
|
|
10
|
+
*
|
|
11
|
+
* return (
|
|
12
|
+
* <>
|
|
13
|
+
* <Widget gestures={{ hold: { action: openDialog } }}>
|
|
14
|
+
* {/* widget content *\/}
|
|
15
|
+
* </Widget>
|
|
16
|
+
* <WidgetDialog
|
|
17
|
+
* open={showDialog()}
|
|
18
|
+
* onOpenChange={setShowDialog}
|
|
19
|
+
* />
|
|
20
|
+
* </>
|
|
21
|
+
* );
|
|
22
|
+
* }
|
|
23
|
+
* ```
|
|
24
|
+
*/
|
|
25
|
+
export interface WidgetDialogReturn {
|
|
26
|
+
/** Current dialog open state accessor */
|
|
27
|
+
showDialog: () => boolean;
|
|
28
|
+
/** Set dialog state directly */
|
|
29
|
+
setShowDialog: (open: boolean) => void;
|
|
30
|
+
/** Open the dialog */
|
|
31
|
+
openDialog: () => void;
|
|
32
|
+
/** Close the dialog */
|
|
33
|
+
closeDialog: () => void;
|
|
34
|
+
/** Current active tab accessor */
|
|
35
|
+
activeTab: () => string;
|
|
36
|
+
/** Set active tab */
|
|
37
|
+
setActiveTab: (tab: string) => void;
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Hook for managing widget dialog state
|
|
41
|
+
*
|
|
42
|
+
* @param defaultTab - Default active tab (default: "edit")
|
|
43
|
+
* @returns Dialog state and control functions
|
|
44
|
+
*/
|
|
45
|
+
export declare function useWidgetDialog(defaultTab?: string): WidgetDialogReturn;
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Widget Entity Group Hook
|
|
3
|
+
*
|
|
4
|
+
* Generalized multi-entity data binding that accepts a signal-based array data source.
|
|
5
|
+
* Supports aggregation presets for common entity domains (lights, sensors, etc.).
|
|
6
|
+
*
|
|
7
|
+
* @example
|
|
8
|
+
* ```tsx
|
|
9
|
+
* // Host provides entities as a signal array
|
|
10
|
+
* const entities = () => syncLayer.getEntities(config.entityIds);
|
|
11
|
+
*
|
|
12
|
+
* const { groupData, emptyState, hasEntities, count } = useWidgetEntityGroup({
|
|
13
|
+
* entities,
|
|
14
|
+
* aggregationMode: () => "light",
|
|
15
|
+
* emptyStateConfig: {
|
|
16
|
+
* icon: <Lightbulb />,
|
|
17
|
+
* title: "No lights",
|
|
18
|
+
* message: "Configure this widget to add lights",
|
|
19
|
+
* },
|
|
20
|
+
* });
|
|
21
|
+
* ```
|
|
22
|
+
*/
|
|
23
|
+
import { type Accessor } from "solid-js";
|
|
24
|
+
import type { EntityView } from "../types";
|
|
25
|
+
import type { WidgetEmptyStateConfig } from "../utils/empty-state";
|
|
26
|
+
import { type LightGroupResult, type SensorGroupResult, type SensorGroupType } from "../utils/entity-aggregation";
|
|
27
|
+
/**
|
|
28
|
+
* Aggregation preset types for common entity domains
|
|
29
|
+
*/
|
|
30
|
+
export type AggregationPreset = "binary-sensor" | "light" | "sensor" | "switch" | "none";
|
|
31
|
+
export interface UseWidgetEntityGroupOptions<TData = unknown> {
|
|
32
|
+
/** Reactive entities array accessor */
|
|
33
|
+
entities: Accessor<EntityView[]>;
|
|
34
|
+
/** Optional custom data calculation function */
|
|
35
|
+
calculateGroupData?: (entities: EntityView[]) => TData;
|
|
36
|
+
/** Aggregation mode accessor (optional, alternative to calculateGroupData) */
|
|
37
|
+
aggregationMode?: Accessor<AggregationPreset | undefined>;
|
|
38
|
+
/** Sensor group calculation mode (for "sensor" preset) */
|
|
39
|
+
sensorGroupType?: Accessor<SensorGroupType>;
|
|
40
|
+
/** All entities mode - all must be on for group to be "on" (for "light"/"switch" presets) */
|
|
41
|
+
allEntitiesMode?: boolean;
|
|
42
|
+
/** Empty state configuration when no entities are available */
|
|
43
|
+
emptyStateConfig: WidgetEmptyStateConfig;
|
|
44
|
+
/** Minimum entities required (default: 1) */
|
|
45
|
+
minEntities?: number;
|
|
46
|
+
}
|
|
47
|
+
export interface UseWidgetEntityGroupResult<TData = unknown> {
|
|
48
|
+
/** All entities accessor */
|
|
49
|
+
entities: Accessor<EntityView[]>;
|
|
50
|
+
/** Custom calculated data (null if insufficient entities or no calculateGroupData provided) */
|
|
51
|
+
groupData: Accessor<TData | null>;
|
|
52
|
+
/** Aggregated data from preset (undefined if no aggregation preset used) */
|
|
53
|
+
aggregatedData: Accessor<LightGroupResult | SensorGroupResult | undefined>;
|
|
54
|
+
/** Empty state config (undefined if entities exist) */
|
|
55
|
+
emptyState: Accessor<WidgetEmptyStateConfig | undefined>;
|
|
56
|
+
/** Whether minimum entities are present */
|
|
57
|
+
hasEntities: Accessor<boolean>;
|
|
58
|
+
/** Entity count */
|
|
59
|
+
count: Accessor<number>;
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* Hook for multi-entity widgets with integrated empty state support and aggregation presets
|
|
63
|
+
*
|
|
64
|
+
* @param options - Entities source, aggregation config, and empty state
|
|
65
|
+
* @returns Reactive group data, aggregation, empty state, and counts
|
|
66
|
+
*/
|
|
67
|
+
export declare function useWidgetEntityGroup<TData = unknown>(options: UseWidgetEntityGroupOptions<TData>): UseWidgetEntityGroupResult<TData>;
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Widget Entity Hook
|
|
3
|
+
*
|
|
4
|
+
* Generalized single-entity data binding that accepts a signal-based data source.
|
|
5
|
+
* The SDK does NOT import sync-layer -- the host provides entity data via Accessor.
|
|
6
|
+
*
|
|
7
|
+
* @example
|
|
8
|
+
* ```tsx
|
|
9
|
+
* // Host provides entity data as a signal
|
|
10
|
+
* const entity = () => syncLayer.getEntity("sensor.temperature");
|
|
11
|
+
*
|
|
12
|
+
* const { data, emptyState, hasEntity } = useWidgetEntity({
|
|
13
|
+
* entity,
|
|
14
|
+
* calculateData: (e) => ({
|
|
15
|
+
* temperature: parseFloat(e.state),
|
|
16
|
+
* unit: e.unitOfMeasurement ?? "C",
|
|
17
|
+
* }),
|
|
18
|
+
* emptyStateConfig: {
|
|
19
|
+
* icon: <Thermometer />,
|
|
20
|
+
* title: "No climate entity",
|
|
21
|
+
* message: "Configure this widget to add a climate device",
|
|
22
|
+
* },
|
|
23
|
+
* });
|
|
24
|
+
* ```
|
|
25
|
+
*/
|
|
26
|
+
import { type Accessor } from "solid-js";
|
|
27
|
+
import type { EntityView } from "../types";
|
|
28
|
+
import type { WidgetEmptyStateConfig } from "../utils/empty-state";
|
|
29
|
+
export interface UseWidgetEntityOptions<TData> {
|
|
30
|
+
/** Reactive entity accessor (null when not available) */
|
|
31
|
+
entity: Accessor<EntityView | null>;
|
|
32
|
+
/** Function to calculate widget-specific data from entity */
|
|
33
|
+
calculateData: (entity: EntityView) => TData;
|
|
34
|
+
/** Empty state configuration when entity is not available */
|
|
35
|
+
emptyStateConfig: WidgetEmptyStateConfig;
|
|
36
|
+
}
|
|
37
|
+
export interface UseWidgetEntityResult<TData> {
|
|
38
|
+
/** The entity (null if not found) */
|
|
39
|
+
entity: Accessor<EntityView | null>;
|
|
40
|
+
/** Calculated widget data (null if no entity) */
|
|
41
|
+
data: Accessor<TData | null>;
|
|
42
|
+
/** Empty state config (undefined if entity exists) */
|
|
43
|
+
emptyState: Accessor<WidgetEmptyStateConfig | undefined>;
|
|
44
|
+
/** Whether entity exists */
|
|
45
|
+
hasEntity: Accessor<boolean>;
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Hook for single-entity widgets with integrated empty state support
|
|
49
|
+
*
|
|
50
|
+
* @param options - Entity source, data calculation, and empty state config
|
|
51
|
+
* @returns Reactive entity data, empty state, and existence check
|
|
52
|
+
*/
|
|
53
|
+
export declare function useWidgetEntity<TData>(options: UseWidgetEntityOptions<TData>): UseWidgetEntityResult<TData>;
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Widget Form Hook
|
|
3
|
+
*
|
|
4
|
+
* Wraps @modular-forms/solid with zodForm validation for widget configuration forms.
|
|
5
|
+
* This is the SolidJS equivalent of the React useWidgetForm that used react-hook-form.
|
|
6
|
+
*
|
|
7
|
+
* @example
|
|
8
|
+
* ```tsx
|
|
9
|
+
* import { z } from "zod";
|
|
10
|
+
*
|
|
11
|
+
* const schema = z.object({
|
|
12
|
+
* title: z.string().optional(),
|
|
13
|
+
* entityIds: z.array(z.string()).min(1),
|
|
14
|
+
* showIcon: z.boolean(),
|
|
15
|
+
* });
|
|
16
|
+
*
|
|
17
|
+
* type FormValues = z.infer<typeof schema>;
|
|
18
|
+
*
|
|
19
|
+
* function MyWidgetSettings() {
|
|
20
|
+
* const { form, Form, Field, isDirty, handleSubmit } = useWidgetForm<FormValues>({
|
|
21
|
+
* schema,
|
|
22
|
+
* initialValues: {
|
|
23
|
+
* title: config.title ?? "",
|
|
24
|
+
* entityIds: config.entityIds ?? [],
|
|
25
|
+
* showIcon: true,
|
|
26
|
+
* },
|
|
27
|
+
* onSubmit: async (values) => {
|
|
28
|
+
* await saveConfig(values);
|
|
29
|
+
* },
|
|
30
|
+
* });
|
|
31
|
+
*
|
|
32
|
+
* return (
|
|
33
|
+
* <Form onSubmit={handleSubmit}>
|
|
34
|
+
* <Field name="title">
|
|
35
|
+
* {(field, props) => (
|
|
36
|
+
* <input {...props} value={field.value} />
|
|
37
|
+
* )}
|
|
38
|
+
* </Field>
|
|
39
|
+
* </Form>
|
|
40
|
+
* );
|
|
41
|
+
* }
|
|
42
|
+
* ```
|
|
43
|
+
*/
|
|
44
|
+
import { type FieldValues, type FormStore, getValue, reset, type SubmitHandler, setValue } from "@modular-forms/solid";
|
|
45
|
+
import type { z } from "zod";
|
|
46
|
+
export interface UseWidgetFormOptions<TValues extends FieldValues> {
|
|
47
|
+
/** Zod schema for form validation */
|
|
48
|
+
schema: z.ZodType<TValues, any, any>;
|
|
49
|
+
/** Initial form values */
|
|
50
|
+
initialValues: TValues;
|
|
51
|
+
/** Submit handler */
|
|
52
|
+
onSubmit?: (values: TValues) => void | Promise<void>;
|
|
53
|
+
}
|
|
54
|
+
export interface UseWidgetFormReturn<TValues extends FieldValues> {
|
|
55
|
+
/** The form store */
|
|
56
|
+
form: FormStore<TValues, undefined>;
|
|
57
|
+
/** Whether form has unsaved changes */
|
|
58
|
+
isDirty: () => boolean;
|
|
59
|
+
/** Whether form is currently submitting */
|
|
60
|
+
isSubmitting: () => boolean;
|
|
61
|
+
/** Handle form submission */
|
|
62
|
+
handleSubmit: SubmitHandler<TValues>;
|
|
63
|
+
/** Get a field value */
|
|
64
|
+
getValue: typeof getValue;
|
|
65
|
+
/** Set a field value */
|
|
66
|
+
setValue: typeof setValue;
|
|
67
|
+
/** Reset form to initial values */
|
|
68
|
+
reset: typeof reset;
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* Hook for widget configuration forms using @modular-forms/solid
|
|
72
|
+
*
|
|
73
|
+
* Callers use the returned form store with `<Form>` and `<Field>` components
|
|
74
|
+
* imported directly from @modular-forms/solid.
|
|
75
|
+
*
|
|
76
|
+
* @param options - Form configuration with schema, initial values, and submit handler
|
|
77
|
+
* @returns Form store, derived state, and utility functions
|
|
78
|
+
*/
|
|
79
|
+
export declare function useWidgetForm<TValues extends FieldValues>(options: UseWidgetFormOptions<TValues>): UseWidgetFormReturn<TValues>;
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Widget Responsive Hook
|
|
3
|
+
*
|
|
4
|
+
* Provides convenience utilities for responsive behavior based on widget size.
|
|
5
|
+
* Uses createMemo for each derived boolean to avoid unnecessary recalculation.
|
|
6
|
+
*
|
|
7
|
+
* @example
|
|
8
|
+
* ```tsx
|
|
9
|
+
* const responsive = useWidgetResponsive();
|
|
10
|
+
*
|
|
11
|
+
* return (
|
|
12
|
+
* <div>
|
|
13
|
+
* <Show when={responsive.showDetail()}>
|
|
14
|
+
* <DetailedMetrics />
|
|
15
|
+
* </Show>
|
|
16
|
+
* <Show when={responsive.isCompact()}>
|
|
17
|
+
* <CompactView />
|
|
18
|
+
* </Show>
|
|
19
|
+
* </div>
|
|
20
|
+
* );
|
|
21
|
+
* ```
|
|
22
|
+
*/
|
|
23
|
+
import type { WidgetSize } from "../types";
|
|
24
|
+
export interface WidgetResponsiveUtils {
|
|
25
|
+
/** xs or sm */
|
|
26
|
+
isCompact: () => boolean;
|
|
27
|
+
/** lg or xl */
|
|
28
|
+
isLarge: () => boolean;
|
|
29
|
+
/** md or larger */
|
|
30
|
+
showDetail: () => boolean;
|
|
31
|
+
/** Check if current size matches any of the provided sizes */
|
|
32
|
+
showOn: (sizes: WidgetSize[]) => boolean;
|
|
33
|
+
/** Check if current size does NOT match any of the provided sizes */
|
|
34
|
+
hideOn: (sizes: WidgetSize[]) => boolean;
|
|
35
|
+
/** Check if a size condition matches (e.g., "md+", "sm-") */
|
|
36
|
+
matches: (condition: string) => boolean;
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Get responsive utilities based on widget context size
|
|
40
|
+
*/
|
|
41
|
+
export declare function useWidgetResponsive(): WidgetResponsiveUtils;
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Widget Framework - Main Barrel Export
|
|
3
|
+
*
|
|
4
|
+
* A SolidJS framework for building GlassHome widgets with minimal boilerplate.
|
|
5
|
+
* This is the single entry point for the entire framework module.
|
|
6
|
+
*
|
|
7
|
+
* @example
|
|
8
|
+
* ```tsx
|
|
9
|
+
* import {
|
|
10
|
+
* Widget,
|
|
11
|
+
* useWidgetGestures,
|
|
12
|
+
* useWidgetDialog,
|
|
13
|
+
* spacing,
|
|
14
|
+
* cn,
|
|
15
|
+
* } from "@glasshome/widget-sdk";
|
|
16
|
+
*
|
|
17
|
+
* export function MyWidget(props) {
|
|
18
|
+
* const { showDialog, openDialog } = useWidgetDialog();
|
|
19
|
+
* const gestures = useWidgetGestures(
|
|
20
|
+
* () => ({ tap: handleTap, hold: { action: openDialog } }),
|
|
21
|
+
* () => ctx.orientation(),
|
|
22
|
+
* );
|
|
23
|
+
*
|
|
24
|
+
* return (
|
|
25
|
+
* <Widget>
|
|
26
|
+
* <Widget.Icon icon={<Power />} color="blue" />
|
|
27
|
+
* <Widget.Title>My Widget</Widget.Title>
|
|
28
|
+
* <Widget.Status>Active</Widget.Status>
|
|
29
|
+
* </Widget>
|
|
30
|
+
* );
|
|
31
|
+
* }
|
|
32
|
+
* ```
|
|
33
|
+
*/
|
|
34
|
+
export { Widget } from "./core/Widget";
|
|
35
|
+
export { WidgetContent } from "./components/WidgetContent";
|
|
36
|
+
export { WidgetEmptyState } from "./components/WidgetEmptyState";
|
|
37
|
+
export { WidgetIcon } from "./components/WidgetIcon";
|
|
38
|
+
export { WidgetMetrics } from "./components/WidgetMetrics";
|
|
39
|
+
export { WidgetStatus } from "./components/WidgetStatus";
|
|
40
|
+
export { WidgetSubtitle } from "./components/WidgetSubtitle";
|
|
41
|
+
export { WidgetTitle } from "./components/WidgetTitle";
|
|
42
|
+
export { WidgetValue } from "./components/WidgetValue";
|
|
43
|
+
export { Glow } from "./backgrounds/Glow";
|
|
44
|
+
export { WidgetSliderFill } from "./backgrounds/WidgetSliderFill";
|
|
45
|
+
export { WidgetStack } from "./layout/WidgetStack";
|
|
46
|
+
export { WidgetDialog, type WidgetDialogProps, type WidgetDialogTab } from "./dialogs";
|
|
47
|
+
export { type AggregationPreset, type ReactiveWidgetContext, type UseDebugDataOptions, type UseWidgetConfigOptions, type UseWidgetConfigReturn, type UseWidgetEntityGroupOptions, type UseWidgetEntityGroupResult, type UseWidgetEntityOptions, type UseWidgetEntityResult, type UseWidgetFormOptions, type UseWidgetFormReturn, useDebugData, useWidgetConfig, useWidgetContext, useWidgetDialog, useWidgetEntity, useWidgetEntityGroup, useWidgetForm, useWidgetResponsive, WidgetCtx, type WidgetDialogReturn, type WidgetResponsiveUtils, } from "./hooks";
|
|
48
|
+
export { type GestureHandlers, useWidgetGestures } from "./gestures/use-widget-gestures";
|
|
49
|
+
export { getSpacingClass, spacing } from "./design-system/spacing";
|
|
50
|
+
export { typography } from "./design-system/typography";
|
|
51
|
+
export { WIDGET_Z, type WidgetZIndex } from "./design-system/z-index";
|
|
52
|
+
export { type AdaptiveIconColors, deriveAdaptiveIconColors, GRADIENT_NAMES, GRADIENT_PRESET_KEYS, GRADIENT_PRESETS, type GradientPreset, getGradient, getGradientFromString, gradientColorPresets, stateColors, type WidgetColorPreset, } from "./theming";
|
|
53
|
+
export { applyCssVars, applyLayout, builtInVariants, classicGlass, compactHorizontal, composeVariants, createFlexLayout, extendVariant, getBuiltInVariant, getBuiltInVariantIds, isBuiltInVariant, mergeVariants, minimal, } from "./variants";
|
|
54
|
+
export { allEntitiesInState, anyEntityInState, calculateLightGroup, calculateSensorGroup, cn, countActiveEntities, countAvailableEntities, countEntitiesByState, createEmptyStateConfig, type EmptyStateConfigOptions, formatValue, getEntityAttribute, getEntityState, interpretValue, isEntityActive, isEntityAvailable, type LightGroupResult, type SensorGroupResult, type SensorGroupType, type WidgetEmptyStateConfig, } from "./utils";
|
|
55
|
+
export type { AbsoluteLayoutStrategy, BaseComponentProps, ColorVariant, CustomLayoutStrategy, ElementConfig, EntityView, FlexLayoutStrategy, GestureConfig, GradientConfig, GridLayoutStrategy, HoldGestureConfig, ImageOverlay, InteractionConfig, LayoutStrategy, PositionConfig, SlideGestureConfig, SpacingScale, VariantPlugins, VariantRegistry, WidgetContextValue, WidgetDimensions, WidgetElement, WidgetOrientation, WidgetSize, WidgetStyles, WidgetVariant, WidgetVariantConfig, } from "./types";
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* WidgetStack Component
|
|
3
|
+
*
|
|
4
|
+
* Vertical flex layout with automatic responsive spacing.
|
|
5
|
+
*
|
|
6
|
+
* @example
|
|
7
|
+
* ```tsx
|
|
8
|
+
* <WidgetStack spacing="S2">
|
|
9
|
+
* <Widget.Icon icon={<Power />} />
|
|
10
|
+
* <Widget.Title>{title}</Widget.Title>
|
|
11
|
+
* <Widget.Status>{status}</Widget.Status>
|
|
12
|
+
* </WidgetStack>
|
|
13
|
+
* ```
|
|
14
|
+
*/
|
|
15
|
+
import type { JSX } from "solid-js";
|
|
16
|
+
import type { SpacingScale } from "../types";
|
|
17
|
+
export interface WidgetStackProps {
|
|
18
|
+
/** Spacing between items (default: "S2") */
|
|
19
|
+
spacing?: SpacingScale;
|
|
20
|
+
/** Additional CSS classes */
|
|
21
|
+
class?: string;
|
|
22
|
+
/** Child elements */
|
|
23
|
+
children: JSX.Element;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Vertical stack layout with responsive spacing
|
|
27
|
+
*/
|
|
28
|
+
export declare function WidgetStack(props: WidgetStackProps): JSX.Element;
|