@featherk/composables 0.13.4 → 0.13.5

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/dist/index.d.ts CHANGED
@@ -6,6 +6,7 @@ export * from "./id";
6
6
  export * from "./menu";
7
7
  export * from "./observer";
8
8
  export * from "./registry";
9
+ export * from "./window";
9
10
  export { useMaskedDateInput } from "./date";
10
11
  export type { ChangePayload as DateChangePayload } from "./date";
11
12
  export { useMaskedDateRangeInput } from "./range";
@@ -0,0 +1 @@
1
+ export * from "./useWindow";
@@ -0,0 +1,100 @@
1
+ import { type ComponentPublicInstance, type ComputedRef, type Ref } from "vue";
2
+ /** Kendo Window's stage values. Distinct from browser fullscreen APIs. */
3
+ export type WindowStage = "DEFAULT" | "MINIMIZED" | "FULLSCREEN";
4
+ export interface WindowMoveEventPayload {
5
+ drag: boolean;
6
+ end: boolean;
7
+ left: number;
8
+ top: number;
9
+ width: number;
10
+ height: number;
11
+ }
12
+ export type WindowResizeEventPayload = WindowMoveEventPayload;
13
+ export interface WindowSettings {
14
+ title: string;
15
+ draggable: boolean;
16
+ resizable: boolean;
17
+ /** Hidden via `windowClass`, not a real Kendo prop - see docs "Inert Props". */
18
+ closable: boolean;
19
+ fixed: boolean;
20
+ /** Hidden via `windowClass`, not a real Kendo prop - see docs "Inert Props". */
21
+ minimizeButton: boolean;
22
+ /** Hidden via `windowClass`, not a real Kendo prop - see docs "Inert Props". */
23
+ maximizeButton: boolean;
24
+ initialWidth: number;
25
+ initialHeight: number;
26
+ width?: number;
27
+ height?: number;
28
+ left?: number;
29
+ top?: number;
30
+ }
31
+ export declare const WINDOW_SETTINGS_DEFAULTS: WindowSettings;
32
+ export interface UseWindowOptions {
33
+ /**
34
+ * Overrides for one or more settings; any field you omit falls back to
35
+ * `WINDOW_SETTINGS_DEFAULTS`, merged internally - you never need to spread
36
+ * the defaults yourself. Pass a `Ref` (e.g. one already wired to your own
37
+ * persistence layer) to have `useWindow` read and mutate it directly
38
+ * instead of creating its own internal ref; missing fields on that ref are
39
+ * filled in from the defaults once, in place, at setup time.
40
+ */
41
+ settings?: Partial<WindowSettings> | Ref<Partial<WindowSettings>>;
42
+ initialStage?: WindowStage;
43
+ onMove?: (payload: WindowMoveEventPayload) => void;
44
+ onResize?: (payload: WindowResizeEventPayload) => void;
45
+ onStageChange?: (stage: WindowStage) => void;
46
+ onOpen?: () => void;
47
+ onClose?: () => void;
48
+ }
49
+ /** The subset of Kendo `Window` props useWindow keeps functional - see docs "Inert Props". */
50
+ export interface WindowBindableProps {
51
+ title: string;
52
+ draggable: boolean;
53
+ resizable: boolean;
54
+ left?: number;
55
+ top?: number;
56
+ initialLeft: number;
57
+ initialTop: number;
58
+ initialWidth: number;
59
+ initialHeight: number;
60
+ width?: number;
61
+ height?: number;
62
+ stage: WindowStage;
63
+ windowClass: string;
64
+ }
65
+ export interface WindowEvents {
66
+ move: (...args: unknown[]) => void;
67
+ resize: (...args: unknown[]) => void;
68
+ stagechange: (...args: unknown[]) => void;
69
+ close: (...args: unknown[]) => void;
70
+ }
71
+ export interface UseWindowOpenOptions {
72
+ /**
73
+ * Discards any existing left/top/width/height (including one restored from
74
+ * an externally-persisted `settings` ref) and recomputes the initial inset
75
+ * position and `initialWidth`/`initialHeight` size. Defaults to `false`, so
76
+ * a previously dragged/resized/persisted position survives close + reopen.
77
+ */
78
+ resetPosition?: boolean;
79
+ }
80
+ export interface UseWindowReturn {
81
+ windowRef: Ref<ComponentPublicInstance | null>;
82
+ settings: Ref<WindowSettings>;
83
+ visible: Ref<boolean>;
84
+ stage: Ref<WindowStage>;
85
+ isDragging: Ref<boolean>;
86
+ isResizing: Ref<boolean>;
87
+ /** Spread via `v-bind` onto `<Window>`. */
88
+ props: ComputedRef<WindowBindableProps>;
89
+ /** Spread via `v-on` onto `<Window>`. */
90
+ events: WindowEvents;
91
+ open: (openOptions?: UseWindowOpenOptions) => Promise<void>;
92
+ close: () => void;
93
+ }
94
+ /**
95
+ * Wraps Kendo UI for Vue's `Window` component (not the browser `window`
96
+ * object) with zero-config defaults, viewport-clamped dragging, and the
97
+ * pointerdown-capture resize/text-selection workaround from
98
+ * docs/design/composable-native-event-listener-tactic.md.
99
+ */
100
+ export declare function useWindow(options?: UseWindowOptions): UseWindowReturn;
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,107 @@
1
+ # useWindow
2
+
3
+ [Back to Composables README](https://github.com/NantHealth/featherk/blob/integration/packages/composables/README.md)
4
+
5
+ > Wraps Kendo UI for Vue's `Window` component - not the browser `window` object. If you were expecting something like VueUse's `useWindowSize`/`useWindowScroll`, this is unrelated to those.
6
+
7
+ Composable for a zero/low-config Kendo `Window`: viewport-clamped dragging, resize tracking, stage transitions, and the pointerdown-capture resize/text-selection workaround are all handled internally. Every setting has a sensible default, so `useWindow()` with no arguments is a complete, working Window.
8
+
9
+ ## Quick Start
10
+
11
+ 1. Import `useWindow` and Kendo's `Window` component.
12
+ 2. Call `useWindow()` - accept every default setting.
13
+ 3. Wire a trigger (e.g. a Button) to `open()`.
14
+ 4. Bind `<Window>` via `v-if`, `ref`, `v-bind`, and `v-on` - never wire individual Window props by hand.
15
+
16
+ ```vue
17
+ <template>
18
+ <!-- Step 3: trigger opens the Window using its defaults -->
19
+ <Button @click="open">Open Window</Button>
20
+
21
+ <!-- Step 4: v-if/ref/v-bind/v-on is the entire binding surface -->
22
+ <Window v-if="visible" ref="windowRef" v-bind="props" v-on="events">
23
+ <p>This is the content of the window.</p>
24
+ </Window>
25
+ </template>
26
+
27
+ <script setup lang="ts">
28
+ // Step 1: import the composable and the Kendo component it augments
29
+ import { useWindow } from "@featherk/composables/window";
30
+ import { Window } from "@progress/kendo-vue-dialogs";
31
+ import { Button } from "@progress/kendo-vue-buttons";
32
+
33
+ // Step 2: no options - every setting uses useWindow's built-in defaults
34
+ const { windowRef, visible, props, events, open } = useWindow();
35
+ </script>
36
+ ```
37
+
38
+ ## Overriding Defaults
39
+
40
+ Pass `settings` with only the fields you want to change; `useWindow` merges it over `WINDOW_SETTINGS_DEFAULTS` internally, so you never spread the defaults yourself.
41
+
42
+ ```ts
43
+ const { windowRef, visible, props, events, open } = useWindow({
44
+ settings: {
45
+ title: "Preferences",
46
+ initialWidth: 600,
47
+ initialHeight: 400,
48
+ resizable: false,
49
+ },
50
+ });
51
+ ```
52
+
53
+ ## Persisting Settings Externally
54
+
55
+ Pass an existing `Ref` (for example, one already wired to your own persistence layer) as `settings` instead of a plain object. `useWindow` fills in any missing fields from `WINDOW_SETTINGS_DEFAULTS` once, in place, then reads and mutates that same ref directly (position/size updates from dragging and resizing land on it) - persistence stays entirely your concern, `useWindow` has no knowledge of how or whether you persist it.
56
+
57
+ ```ts
58
+ const persistedSettings = ref<Partial<WindowSettings>>({ ...myLoadedSettings });
59
+ const win = useWindow({ settings: persistedSettings });
60
+ ```
61
+
62
+ A partially-populated ref is fine - any field `myLoadedSettings` doesn't have falls back to the built-in default, exactly like the plain-object form above.
63
+
64
+ `open()` respects this: by default it reopens at whatever `left`/`top`/`width`/`height` are already on `settings` (a prior drag/resize, or a value you restored from persistence), rather than recomputing the initial inset position. Pass `open({ resetPosition: true })` to force the Window back to its computed initial position/size instead - see `UseWindowReturn.open` in the API table below.
65
+
66
+ ## API
67
+
68
+ ### `UseWindowOptions`
69
+
70
+ | Option | Type | Description |
71
+ | --------------- | --------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------ |
72
+ | `settings` | `Partial<WindowSettings> \| Ref<Partial<WindowSettings>>` | Overrides merged over `WINDOW_SETTINGS_DEFAULTS` internally. Pass a `Ref` for externally-owned/persisted settings. |
73
+ | `initialStage` | `WindowStage` | Initial `stage` value. Defaults to `"DEFAULT"`. |
74
+ | `onMove` | `(payload) => void` | Called after internal move handling. |
75
+ | `onResize` | `(payload) => void` | Called after internal resize handling. |
76
+ | `onStageChange` | `(stage) => void` | Called after a valid stage transition. |
77
+ | `onOpen` | `() => void` | Called after `open()` finishes sizing the Window. |
78
+ | `onClose` | `() => void` | Called after `close()` hides the Window. |
79
+
80
+ ### `UseWindowReturn`
81
+
82
+ | Member | Type | Description |
83
+ | --------------------------- | -------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
84
+ | `windowRef` | `Ref<ComponentPublicInstance \| null>` | Bind via `ref="windowRef"`. |
85
+ | `settings` | `Ref<WindowSettings>` | Reactive settings - bind checkboxes/inputs directly for a settings panel. |
86
+ | `visible` | `Ref<boolean>` | Bind via `v-if="visible"`. |
87
+ | `stage` | `Ref<WindowStage>` | Current `"DEFAULT" \| "MINIMIZED" \| "FULLSCREEN"` stage. |
88
+ | `isDragging` / `isResizing` | `Ref<boolean>` | Live drag/resize state, already folded into `windowClass`. |
89
+ | `props` | `ComputedRef<WindowBindableProps>` | Spread via `v-bind="props"` onto `<Window>`. |
90
+ | `events` | `WindowEvents` | Spread via `v-on="events"` onto `<Window>`. |
91
+ | `open` | `(openOptions?: { resetPosition?: boolean }) => Promise<void>` | Shows the Window. By default, reopens at the current `settings` position/size; pass `{ resetPosition: true }` to recompute the initial inset position/size instead. |
92
+ | `close` | `() => void` | Resets `stage` to `"DEFAULT"` and hides the Window. |
93
+
94
+ ## Inert Props
95
+
96
+ `props` only contains Kendo `Window` props that actually work: `title`, `draggable`, `resizable`, `left`, `top`, `initialLeft`, `initialTop`, `initialWidth`, `initialHeight`, `width`, `height`, `stage`, and `windowClass`. `settings.closable`, `settings.minimizeButton`, and `settings.maximizeButton` are deliberately **not** included in `props`.
97
+
98
+ If you hand-wire raw Kendo props like `closeButton`/`minimizeButton`/`maximizeButton` directly on `<Window>` alongside `v-bind="props"`, they are **inert** - they will not hide the corresponding titlebar button. `useWindow` only ever suppresses those buttons via CSS (`fk-window-hide-minimize`/`fk-window-hide-maximize`/`fk-window-hide-close` classes folded into `windowClass`), never through Kendo's own button props. Toggle `settings.minimizeButton`/`settings.maximizeButton`/`settings.closable` instead.
99
+
100
+ ## Kendo Defects Worked Around
101
+
102
+ - **Titlebar button props are non-functional.** Kendo's `Window` types `minimizeButton`/`maximizeButton`/`closeButton`/`restoreButton` as `[String, Function]`, but the child `WindowTitlebar` that actually renders them types the same props `[String, Function, Object, Boolean]` and renders each button unconditionally regardless of value. There is no supported way to hide these buttons through Kendo's own props - see "Inert Props" above for the CSS-class workaround `useWindow` uses instead.
103
+ - **Resize-handle drag vs. text-selection race.** Kendo Window's resize handles use Kendo's own draggable implementation, which does not prevent the native browser default until after pointer movement begins. If text inside the Window is already selected, native selection-dragging can compete with a resize started from an inward-overlapping handle. `useWindow` prevents this internally with a capture-phase `pointerdown` listener scoped to `.k-resize-handle` elements - see [docs/design/composable-native-event-listener-tactic.md](https://github.com/NantHealth/featherk/blob/integration/docs/design/composable-native-event-listener-tactic.md) for the general pattern. This is fully internal; no `@pointerdown.capture` binding is needed (or exposed) in consumer templates.
104
+
105
+ ## Styling
106
+
107
+ All `windowClass` output (`fk-window`, `fk-window-can-drag`, `fk-window-is-dragging`, `fk-window-is-resizing`, `fk-window-fixed`, `fk-window-hide-minimize`, `fk-window-hide-maximize`, `fk-window-hide-close`) is styled in `@featherk/styles` - consumers do not need to write any Window CSS themselves. The titlebar button-hiding workaround, the Kendo defect fixes, and drag/resize motion are all shipped as part of the external stylesheet; header background, text color, icon color, and padding are configured through ThemeBuilder's Window component panel. Just make sure the app includes `@featherk/styles`.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@featherk/composables",
3
- "version": "0.13.4",
3
+ "version": "0.13.5",
4
4
  "main": "dist/featherk-composables.umd.js",
5
5
  "module": "dist/featherk-composables.es.js",
6
6
  "types": "dist/index.d.ts",
@@ -70,6 +70,11 @@
70
70
  "import": "./dist/featherk-composables.es.js",
71
71
  "require": "./dist/featherk-composables.umd.js"
72
72
  },
73
+ "./window": {
74
+ "types": "./dist/window/index.d.ts",
75
+ "import": "./dist/featherk-composables.es.js",
76
+ "require": "./dist/featherk-composables.umd.js"
77
+ },
73
78
  "./docs-manifest": {
74
79
  "types": "./dist/docs-manifest.d.ts",
75
80
  "import": "./dist/docs-manifest.js",