horizon-layout 1.0.3 → 1.0.4

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.
@@ -22,7 +22,7 @@
22
22
  validateConfig
23
23
  } from './utils.js';
24
24
  import { DEFAULT_MIN_WIDTH_RATIO, DEFAULT_MIN_HEIGHT_RATIO } from './internal-utils.js';
25
- import { dropTargetType } from './internal-utils.js';
25
+ import { dropTargetType, roundSplitPoint } from './internal-utils.js';
26
26
  import type { DropTarget } from './internal-types.js';
27
27
 
28
28
  interface Popout {
@@ -364,7 +364,7 @@
364
364
  const minRatio = split.direction === 'horizontal' ? minWidthRatio : minHeightRatio;
365
365
  const min = (split.splitPoints[index - 1] ?? 0) + minRatio;
366
366
  const max = (split.splitPoints[index + 1] ?? 1) - minRatio;
367
- const clamped = Number(Math.min(Math.max(ratio, min), max).toFixed(4));
367
+ const clamped = roundSplitPoint(Math.min(Math.max(ratio, min), max));
368
368
  if (clamped !== split.splitPoints[index]) split.splitPoints[index] = clamped;
369
369
  }
370
370
 
@@ -2,5 +2,6 @@ import type { DropTarget } from './internal-types.ts';
2
2
  import type { Modifier } from './types.ts';
3
3
  export declare const DEFAULT_MIN_WIDTH_RATIO = 0.1;
4
4
  export declare const DEFAULT_MIN_HEIGHT_RATIO = 0.2;
5
+ export declare function roundSplitPoint(ratio: number): number;
5
6
  export declare function getModifier(event: KeyboardEvent): Modifier | null;
6
7
  export declare function dropTargetType(target: DropTarget): 'side' | 'tab';
@@ -1,5 +1,10 @@
1
1
  export const DEFAULT_MIN_WIDTH_RATIO = 0.1;
2
2
  export const DEFAULT_MIN_HEIGHT_RATIO = 0.2;
3
+ /// rounds a split point to the precision it is stored with, so that values
4
+ /// computed with floating-point arithmetic can be compared reliably
5
+ export function roundSplitPoint(ratio) {
6
+ return Number(ratio.toFixed(4));
7
+ }
3
8
  /// returns the modifier (ctrl, alt, shift, or a combination of them)
4
9
  /// associated with the keyboard event, or null if no modifier keys were pressed
5
10
  export function getModifier(event) {
package/dist/utils.js CHANGED
@@ -1,5 +1,5 @@
1
1
  import { SvelteMap } from 'svelte/reactivity';
2
- import { DEFAULT_MIN_WIDTH_RATIO, DEFAULT_MIN_HEIGHT_RATIO } from "./internal-utils.js";
2
+ import { DEFAULT_MIN_WIDTH_RATIO, DEFAULT_MIN_HEIGHT_RATIO, roundSplitPoint } from "./internal-utils.js";
3
3
  /// Remove a tab group that has become empty, then collapse any resulting
4
4
  /// single-child splits up the tree.
5
5
  export function simplifyTabGroup(tabGroup, nodeParentMap, config) {
@@ -115,11 +115,11 @@ export function areConfigsEquivalent(config1, config2) {
115
115
  /// split-point constraints are checked with the same thresholds.
116
116
  export function validateConfig(config, views, options = {}) {
117
117
  const { minWidthRatio = DEFAULT_MIN_WIDTH_RATIO, minHeightRatio = DEFAULT_MIN_HEIGHT_RATIO } = options;
118
- if (config.maximizedView && !views.get(config.maximizedView)) {
118
+ if (config.maximizedView && !views.has(config.maximizedView)) {
119
119
  throw new Error(`unknown maximizedView id "${config.maximizedView}"`);
120
120
  }
121
121
  for (const id of config.popouts ?? []) {
122
- if (!views.get(id))
122
+ if (!views.has(id))
123
123
  throw new Error(`unknown popout id "${id}"`);
124
124
  }
125
125
  if (!config.root)
@@ -130,7 +130,7 @@ export function validateConfig(config, views, options = {}) {
130
130
  throw new Error('activeTabIndex out of range');
131
131
  }
132
132
  for (const id of tabGroup.tabs) {
133
- if (!views.get(id))
133
+ if (!views.has(id))
134
134
  throw new Error(`unknown tab id "${id}"`);
135
135
  if (seenIds.has(id))
136
136
  throw new Error(`duplicate tab id "${id}"`);
@@ -143,9 +143,9 @@ export function validateConfig(config, views, options = {}) {
143
143
  }
144
144
  const minRatio = split.direction === 'horizontal' ? minWidthRatio : minHeightRatio;
145
145
  for (let i = 0; i < split.splitPoints.length; i++) {
146
- const point = split.splitPoints[i];
146
+ const point = roundSplitPoint(split.splitPoints[i]);
147
147
  const prev = split.splitPoints[i - 1] ?? 0;
148
- if (point < prev + minRatio || point > 1 - minRatio) {
148
+ if (point < roundSplitPoint(prev + minRatio) || point > roundSplitPoint(1 - minRatio)) {
149
149
  throw new Error('splitPoints violate minRatio constraints');
150
150
  }
151
151
  }
@@ -159,12 +159,10 @@ export function validateConfig(config, views, options = {}) {
159
159
  validateNode(config.root);
160
160
  }
161
161
  function parseNodeConfig(object, path) {
162
- try {
163
- return parseSplitConfig(object, path);
164
- }
165
- catch {
166
- return parseTabGroupConfig(object, path);
167
- }
162
+ if (typeof object !== 'object' || object === null)
163
+ throw new Error(`${path}: expected an object`);
164
+ const isSplit = 'direction' in object || 'views' in object || 'splitPoints' in object;
165
+ return isSplit ? parseSplitConfig(object, path) : parseTabGroupConfig(object, path);
168
166
  }
169
167
  function parseSplitConfig(object, path) {
170
168
  if (typeof object !== 'object' || object === null)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "horizon-layout",
3
- "version": "1.0.3",
3
+ "version": "1.0.4",
4
4
  "keywords": [
5
5
  "svelte"
6
6
  ],
@@ -40,6 +40,7 @@
40
40
  "check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json",
41
41
  "check:watch": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --watch",
42
42
  "lint": "oxlint . && oxfmt --check .",
43
+ "test": "vitest run",
43
44
  "format": "oxfmt ."
44
45
  },
45
46
  "devDependencies": {
@@ -54,7 +55,8 @@
54
55
  "svelte": "^5.57.1",
55
56
  "svelte-check": "^4.7.6",
56
57
  "typescript": "^6.0.3",
57
- "vite": "^8.3.2"
58
+ "vite": "^8.3.2",
59
+ "vitest": "^5.0.3"
58
60
  },
59
61
  "peerDependencies": {
60
62
  "svelte": "^5.0.0"