@nick-skriabin/glyph 0.1.33 → 0.1.35
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.cjs +125 -65
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +2 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +125 -65
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -4347,54 +4347,7 @@ function DialogOverlay({ dialog, onDismiss }) {
|
|
|
4347
4347
|
)
|
|
4348
4348
|
);
|
|
4349
4349
|
}
|
|
4350
|
-
|
|
4351
|
-
const focusCtx = React15.useContext(FocusContext);
|
|
4352
|
-
const layoutCtx = React15.useContext(LayoutContext);
|
|
4353
|
-
const [elements, setElements] = React15.useState([]);
|
|
4354
|
-
const updateRef = React15.useRef(() => {
|
|
4355
|
-
});
|
|
4356
|
-
const updateElements = React15.useCallback(() => {
|
|
4357
|
-
if (!focusCtx) return;
|
|
4358
|
-
const registered = focusCtx.getRegisteredElements?.() ?? [];
|
|
4359
|
-
const mapped = registered.map(({ id, node }) => ({
|
|
4360
|
-
id,
|
|
4361
|
-
node,
|
|
4362
|
-
layout: layoutCtx?.getLayout(node) ?? node.layout,
|
|
4363
|
-
type: node.type
|
|
4364
|
-
}));
|
|
4365
|
-
mapped.sort((a, b) => {
|
|
4366
|
-
if (a.layout.y !== b.layout.y) {
|
|
4367
|
-
return a.layout.y - b.layout.y;
|
|
4368
|
-
}
|
|
4369
|
-
return a.layout.x - b.layout.x;
|
|
4370
|
-
});
|
|
4371
|
-
setElements(mapped);
|
|
4372
|
-
}, [focusCtx, layoutCtx]);
|
|
4373
|
-
updateRef.current = updateElements;
|
|
4374
|
-
React15.useEffect(() => {
|
|
4375
|
-
if (!focusCtx) return;
|
|
4376
|
-
updateElements();
|
|
4377
|
-
const unsubscribe = focusCtx.onFocusChange(() => {
|
|
4378
|
-
updateElements();
|
|
4379
|
-
});
|
|
4380
|
-
const timer = setTimeout(updateElements, 50);
|
|
4381
|
-
return () => {
|
|
4382
|
-
unsubscribe();
|
|
4383
|
-
clearTimeout(timer);
|
|
4384
|
-
};
|
|
4385
|
-
}, [focusCtx, layoutCtx, updateElements]);
|
|
4386
|
-
if (!focusCtx) return null;
|
|
4387
|
-
return {
|
|
4388
|
-
elements,
|
|
4389
|
-
focusedId: focusCtx.focusedId,
|
|
4390
|
-
requestFocus: focusCtx.requestFocus,
|
|
4391
|
-
focusNext: focusCtx.focusNext,
|
|
4392
|
-
focusPrev: focusCtx.focusPrev,
|
|
4393
|
-
refresh: () => updateRef.current()
|
|
4394
|
-
};
|
|
4395
|
-
}
|
|
4396
|
-
|
|
4397
|
-
// src/components/JumpNav.tsx
|
|
4350
|
+
var JumpNavContext = React15.createContext(null);
|
|
4398
4351
|
function generateHints(count, chars) {
|
|
4399
4352
|
const hints = [];
|
|
4400
4353
|
const charList = chars.split("");
|
|
@@ -4422,8 +4375,25 @@ function JumpNav({
|
|
|
4422
4375
|
}) {
|
|
4423
4376
|
const [isActive, setIsActive] = React15.useState(false);
|
|
4424
4377
|
const [inputBuffer, setInputBuffer] = React15.useState("");
|
|
4425
|
-
const
|
|
4378
|
+
const [hasChildJumpNav, setHasChildJumpNav] = React15.useState(false);
|
|
4379
|
+
const [elements, setElements] = React15.useState([]);
|
|
4426
4380
|
const inputCtx = React15.useContext(InputContext);
|
|
4381
|
+
const focusCtx = React15.useContext(FocusContext);
|
|
4382
|
+
const layoutCtx = React15.useContext(LayoutContext);
|
|
4383
|
+
const parentJumpNav = React15.useContext(JumpNavContext);
|
|
4384
|
+
const wrapperRef = React15.useRef(null);
|
|
4385
|
+
React15.useEffect(() => {
|
|
4386
|
+
if (parentJumpNav) {
|
|
4387
|
+
return parentJumpNav.registerChildJumpNav();
|
|
4388
|
+
}
|
|
4389
|
+
}, [parentJumpNav]);
|
|
4390
|
+
const contextValue = React15.useMemo(() => ({
|
|
4391
|
+
isChildActive: hasChildJumpNav,
|
|
4392
|
+
registerChildJumpNav: () => {
|
|
4393
|
+
setHasChildJumpNav(true);
|
|
4394
|
+
return () => setHasChildJumpNav(false);
|
|
4395
|
+
}
|
|
4396
|
+
}), [hasChildJumpNav]);
|
|
4427
4397
|
const parseKey = React15.useCallback((keyStr) => {
|
|
4428
4398
|
const parts = keyStr.toLowerCase().split("+");
|
|
4429
4399
|
return {
|
|
@@ -4435,28 +4405,58 @@ function JumpNav({
|
|
|
4435
4405
|
};
|
|
4436
4406
|
}, []);
|
|
4437
4407
|
const activationKeyParsed = parseKey(activationKey);
|
|
4408
|
+
const findFocusableDescendants = React15.useCallback((node) => {
|
|
4409
|
+
const result = [];
|
|
4410
|
+
function walk(n) {
|
|
4411
|
+
if (n.focusId) {
|
|
4412
|
+
result.push({
|
|
4413
|
+
id: n.focusId,
|
|
4414
|
+
node: n,
|
|
4415
|
+
layout: layoutCtx?.getLayout(n) ?? n.layout
|
|
4416
|
+
});
|
|
4417
|
+
}
|
|
4418
|
+
for (const child of n.children) {
|
|
4419
|
+
walk(child);
|
|
4420
|
+
}
|
|
4421
|
+
}
|
|
4422
|
+
walk(node);
|
|
4423
|
+
return result;
|
|
4424
|
+
}, [layoutCtx]);
|
|
4425
|
+
const refreshElements = React15.useCallback(() => {
|
|
4426
|
+
if (!wrapperRef.current) return;
|
|
4427
|
+
const descendants = findFocusableDescendants(wrapperRef.current);
|
|
4428
|
+
descendants.sort((a, b) => {
|
|
4429
|
+
if (a.layout.y !== b.layout.y) {
|
|
4430
|
+
return a.layout.y - b.layout.y;
|
|
4431
|
+
}
|
|
4432
|
+
return a.layout.x - b.layout.x;
|
|
4433
|
+
});
|
|
4434
|
+
setElements(descendants);
|
|
4435
|
+
}, [findFocusableDescendants]);
|
|
4438
4436
|
const wasActiveRef = React15.useRef(false);
|
|
4439
4437
|
React15.useEffect(() => {
|
|
4440
|
-
if (isActive && !wasActiveRef.current
|
|
4441
|
-
|
|
4438
|
+
if (isActive && !wasActiveRef.current) {
|
|
4439
|
+
refreshElements();
|
|
4442
4440
|
}
|
|
4443
4441
|
wasActiveRef.current = isActive;
|
|
4444
|
-
}, [isActive]);
|
|
4445
|
-
const elements = registry?.elements ?? [];
|
|
4442
|
+
}, [isActive, refreshElements]);
|
|
4446
4443
|
const visibleElements = elements.filter(
|
|
4447
4444
|
(el) => el.layout.width > 0 && el.layout.height > 0
|
|
4448
4445
|
);
|
|
4449
4446
|
const visibleHints = generateHints(visibleElements.length, hintChars);
|
|
4450
|
-
const visibleHintMap =
|
|
4451
|
-
|
|
4452
|
-
|
|
4453
|
-
|
|
4454
|
-
|
|
4455
|
-
|
|
4447
|
+
const visibleHintMap = React15.useMemo(() => {
|
|
4448
|
+
const map = /* @__PURE__ */ new Map();
|
|
4449
|
+
visibleElements.forEach((el, i) => {
|
|
4450
|
+
if (visibleHints[i]) {
|
|
4451
|
+
map.set(visibleHints[i], el.id);
|
|
4452
|
+
}
|
|
4453
|
+
});
|
|
4454
|
+
return map;
|
|
4455
|
+
}, [visibleElements, visibleHints]);
|
|
4456
4456
|
React15.useEffect(() => {
|
|
4457
4457
|
if (!inputCtx || !enabled) return;
|
|
4458
4458
|
const handler = (key) => {
|
|
4459
|
-
if (!isActive && key.name === activationKeyParsed.name && !!key.ctrl === activationKeyParsed.ctrl && !!key.alt === activationKeyParsed.alt && !!key.shift === activationKeyParsed.shift && !!key.meta === activationKeyParsed.meta) {
|
|
4459
|
+
if (!isActive && !hasChildJumpNav && key.name === activationKeyParsed.name && !!key.ctrl === activationKeyParsed.ctrl && !!key.alt === activationKeyParsed.alt && !!key.shift === activationKeyParsed.shift && !!key.meta === activationKeyParsed.meta) {
|
|
4460
4460
|
setIsActive(true);
|
|
4461
4461
|
setInputBuffer("");
|
|
4462
4462
|
return true;
|
|
@@ -4475,7 +4475,7 @@ function JumpNav({
|
|
|
4475
4475
|
const newBuffer = inputBuffer + key.sequence.toLowerCase();
|
|
4476
4476
|
const targetId = visibleHintMap.get(newBuffer);
|
|
4477
4477
|
if (targetId) {
|
|
4478
|
-
|
|
4478
|
+
focusCtx?.requestFocus(targetId);
|
|
4479
4479
|
setIsActive(false);
|
|
4480
4480
|
setInputBuffer("");
|
|
4481
4481
|
return true;
|
|
@@ -4493,7 +4493,7 @@ function JumpNav({
|
|
|
4493
4493
|
return false;
|
|
4494
4494
|
};
|
|
4495
4495
|
return inputCtx.subscribePriority(handler);
|
|
4496
|
-
}, [inputCtx, enabled, isActive, activationKeyParsed, inputBuffer, visibleHintMap,
|
|
4496
|
+
}, [inputCtx, enabled, isActive, activationKeyParsed, inputBuffer, visibleHintMap, focusCtx, hasChildJumpNav]);
|
|
4497
4497
|
const hintsOverlay = isActive ? React15__default.default.createElement(
|
|
4498
4498
|
React15__default.default.Fragment,
|
|
4499
4499
|
null,
|
|
@@ -4541,10 +4541,24 @@ function JumpNav({
|
|
|
4541
4541
|
}, inputBuffer ? `Jump: ${inputBuffer}_` : "Press a key to jump \u2022 ESC to cancel")
|
|
4542
4542
|
)
|
|
4543
4543
|
) : null;
|
|
4544
|
+
const wrappedChildren = React15__default.default.createElement(
|
|
4545
|
+
"box",
|
|
4546
|
+
{
|
|
4547
|
+
ref: (node) => {
|
|
4548
|
+
wrapperRef.current = node;
|
|
4549
|
+
},
|
|
4550
|
+
style: {
|
|
4551
|
+
// Invisible wrapper - takes full size of parent
|
|
4552
|
+
flexGrow: 1,
|
|
4553
|
+
flexDirection: "column"
|
|
4554
|
+
}
|
|
4555
|
+
},
|
|
4556
|
+
children
|
|
4557
|
+
);
|
|
4544
4558
|
return React15__default.default.createElement(
|
|
4545
|
-
|
|
4546
|
-
|
|
4547
|
-
|
|
4559
|
+
JumpNavContext.Provider,
|
|
4560
|
+
{ value: contextValue },
|
|
4561
|
+
wrappedChildren,
|
|
4548
4562
|
hintsOverlay
|
|
4549
4563
|
);
|
|
4550
4564
|
}
|
|
@@ -4646,6 +4660,52 @@ function useApp() {
|
|
|
4646
4660
|
}
|
|
4647
4661
|
};
|
|
4648
4662
|
}
|
|
4663
|
+
function useFocusRegistry() {
|
|
4664
|
+
const focusCtx = React15.useContext(FocusContext);
|
|
4665
|
+
const layoutCtx = React15.useContext(LayoutContext);
|
|
4666
|
+
const [elements, setElements] = React15.useState([]);
|
|
4667
|
+
const updateRef = React15.useRef(() => {
|
|
4668
|
+
});
|
|
4669
|
+
const updateElements = React15.useCallback(() => {
|
|
4670
|
+
if (!focusCtx) return;
|
|
4671
|
+
const registered = focusCtx.getRegisteredElements?.() ?? [];
|
|
4672
|
+
const mapped = registered.map(({ id, node }) => ({
|
|
4673
|
+
id,
|
|
4674
|
+
node,
|
|
4675
|
+
layout: layoutCtx?.getLayout(node) ?? node.layout,
|
|
4676
|
+
type: node.type
|
|
4677
|
+
}));
|
|
4678
|
+
mapped.sort((a, b) => {
|
|
4679
|
+
if (a.layout.y !== b.layout.y) {
|
|
4680
|
+
return a.layout.y - b.layout.y;
|
|
4681
|
+
}
|
|
4682
|
+
return a.layout.x - b.layout.x;
|
|
4683
|
+
});
|
|
4684
|
+
setElements(mapped);
|
|
4685
|
+
}, [focusCtx, layoutCtx]);
|
|
4686
|
+
updateRef.current = updateElements;
|
|
4687
|
+
React15.useEffect(() => {
|
|
4688
|
+
if (!focusCtx) return;
|
|
4689
|
+
updateElements();
|
|
4690
|
+
const unsubscribe = focusCtx.onFocusChange(() => {
|
|
4691
|
+
updateElements();
|
|
4692
|
+
});
|
|
4693
|
+
const timer = setTimeout(updateElements, 50);
|
|
4694
|
+
return () => {
|
|
4695
|
+
unsubscribe();
|
|
4696
|
+
clearTimeout(timer);
|
|
4697
|
+
};
|
|
4698
|
+
}, [focusCtx, layoutCtx, updateElements]);
|
|
4699
|
+
if (!focusCtx) return null;
|
|
4700
|
+
return {
|
|
4701
|
+
elements,
|
|
4702
|
+
focusedId: focusCtx.focusedId,
|
|
4703
|
+
requestFocus: focusCtx.requestFocus,
|
|
4704
|
+
focusNext: focusCtx.focusNext,
|
|
4705
|
+
focusPrev: focusCtx.focusPrev,
|
|
4706
|
+
refresh: () => updateRef.current()
|
|
4707
|
+
};
|
|
4708
|
+
}
|
|
4649
4709
|
|
|
4650
4710
|
// src/utils/mask.ts
|
|
4651
4711
|
function parseMask(mask) {
|