@blorkfield/blork-tabs 0.4.0 → 0.5.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/dist/index.cjs +76 -0
- package/dist/index.d.cts +62 -1
- package/dist/index.d.ts +62 -1
- package/dist/index.js +75 -0
- package/dist/styles.css +82 -0
- package/package.json +1 -1
- package/src/TagButton.ts +145 -0
- package/src/index.ts +2 -0
package/dist/index.cjs
CHANGED
|
@@ -32,6 +32,7 @@ __export(index_exports, {
|
|
|
32
32
|
createPanelElement: () => createPanelElement,
|
|
33
33
|
createPanelState: () => createPanelState,
|
|
34
34
|
createPresetAnchor: () => createPresetAnchor,
|
|
35
|
+
createTagButton: () => createTagButton,
|
|
35
36
|
detachFromGroup: () => detachFromGroup,
|
|
36
37
|
findSnapTarget: () => findSnapTarget,
|
|
37
38
|
getConnectedGroup: () => getConnectedGroup,
|
|
@@ -1633,6 +1634,80 @@ var TabManager = class {
|
|
|
1633
1634
|
this.debugPanelElements.clear();
|
|
1634
1635
|
}
|
|
1635
1636
|
};
|
|
1637
|
+
|
|
1638
|
+
// src/TagButton.ts
|
|
1639
|
+
function createTagButton(label, config = {}) {
|
|
1640
|
+
const { defaultActive = false, inputs = [], onChange } = config;
|
|
1641
|
+
const hasInputs = inputs.length > 0;
|
|
1642
|
+
const el = document.createElement(hasInputs ? "div" : "button");
|
|
1643
|
+
el.className = "blork-tabs-tag-btn";
|
|
1644
|
+
if (hasInputs) {
|
|
1645
|
+
el.setAttribute("role", "button");
|
|
1646
|
+
el.setAttribute("tabindex", "0");
|
|
1647
|
+
}
|
|
1648
|
+
el.appendChild(document.createTextNode(label));
|
|
1649
|
+
const inputElements = [];
|
|
1650
|
+
if (hasInputs) {
|
|
1651
|
+
const inputsContainer = document.createElement("span");
|
|
1652
|
+
inputsContainer.className = "blork-tabs-tag-inputs";
|
|
1653
|
+
for (const inputConfig of inputs) {
|
|
1654
|
+
if (inputConfig.label) {
|
|
1655
|
+
const labelSpan = document.createElement("span");
|
|
1656
|
+
labelSpan.textContent = inputConfig.label;
|
|
1657
|
+
inputsContainer.appendChild(labelSpan);
|
|
1658
|
+
}
|
|
1659
|
+
if (inputConfig.type === "select") {
|
|
1660
|
+
const select = document.createElement("select");
|
|
1661
|
+
select.className = "blork-tabs-tag-select";
|
|
1662
|
+
for (const opt of inputConfig.options ?? []) {
|
|
1663
|
+
const option = document.createElement("option");
|
|
1664
|
+
option.value = opt.value;
|
|
1665
|
+
option.textContent = opt.label;
|
|
1666
|
+
select.appendChild(option);
|
|
1667
|
+
}
|
|
1668
|
+
select.addEventListener("click", (e) => e.stopPropagation());
|
|
1669
|
+
select.addEventListener("mousedown", (e) => e.stopPropagation());
|
|
1670
|
+
inputsContainer.appendChild(select);
|
|
1671
|
+
inputElements.push(select);
|
|
1672
|
+
} else {
|
|
1673
|
+
const input = document.createElement("input");
|
|
1674
|
+
input.type = "number";
|
|
1675
|
+
input.className = "blork-tabs-tag-input";
|
|
1676
|
+
if (inputConfig.defaultValue !== void 0) input.value = String(inputConfig.defaultValue);
|
|
1677
|
+
if (inputConfig.step !== void 0) input.step = String(inputConfig.step);
|
|
1678
|
+
if (inputConfig.min !== void 0) input.min = String(inputConfig.min);
|
|
1679
|
+
if (inputConfig.max !== void 0) input.max = String(inputConfig.max);
|
|
1680
|
+
input.addEventListener("click", (e) => e.stopPropagation());
|
|
1681
|
+
inputsContainer.appendChild(input);
|
|
1682
|
+
inputElements.push(input);
|
|
1683
|
+
}
|
|
1684
|
+
}
|
|
1685
|
+
el.appendChild(inputsContainer);
|
|
1686
|
+
}
|
|
1687
|
+
let active = defaultActive;
|
|
1688
|
+
if (active) el.classList.add("active");
|
|
1689
|
+
const setActive = (value) => {
|
|
1690
|
+
active = value;
|
|
1691
|
+
el.classList.toggle("active", active);
|
|
1692
|
+
onChange?.(active);
|
|
1693
|
+
};
|
|
1694
|
+
el.addEventListener("click", () => setActive(!active));
|
|
1695
|
+
el.addEventListener("keydown", (e) => {
|
|
1696
|
+
const ke = e;
|
|
1697
|
+
if (ke.key === "Enter" || ke.key === " ") {
|
|
1698
|
+
ke.preventDefault();
|
|
1699
|
+
setActive(!active);
|
|
1700
|
+
}
|
|
1701
|
+
});
|
|
1702
|
+
return {
|
|
1703
|
+
element: el,
|
|
1704
|
+
isActive: () => active,
|
|
1705
|
+
setActive,
|
|
1706
|
+
toggle: () => setActive(!active),
|
|
1707
|
+
getValue: (index) => inputElements[index]?.value ?? "",
|
|
1708
|
+
getInput: (index) => inputElements[index]
|
|
1709
|
+
};
|
|
1710
|
+
}
|
|
1636
1711
|
// Annotate the CommonJS export names for ESM import in node:
|
|
1637
1712
|
0 && (module.exports = {
|
|
1638
1713
|
AnchorManager,
|
|
@@ -1647,6 +1722,7 @@ var TabManager = class {
|
|
|
1647
1722
|
createPanelElement,
|
|
1648
1723
|
createPanelState,
|
|
1649
1724
|
createPresetAnchor,
|
|
1725
|
+
createTagButton,
|
|
1650
1726
|
detachFromGroup,
|
|
1651
1727
|
findSnapTarget,
|
|
1652
1728
|
getConnectedGroup,
|
package/dist/index.d.cts
CHANGED
|
@@ -823,6 +823,67 @@ interface HoverEnlargeConfig {
|
|
|
823
823
|
*/
|
|
824
824
|
declare function setupHoverEnlarge(config: HoverEnlargeConfig): void;
|
|
825
825
|
|
|
826
|
+
/**
|
|
827
|
+
* Tag button: a toggleable pill-shaped button that optionally reveals
|
|
828
|
+
* inline inputs (numbers, selects) when active.
|
|
829
|
+
*
|
|
830
|
+
* @example
|
|
831
|
+
* ```typescript
|
|
832
|
+
* // Simple toggle
|
|
833
|
+
* const staticBtn = createTagButton('static');
|
|
834
|
+
* container.appendChild(staticBtn.element);
|
|
835
|
+
*
|
|
836
|
+
* // Toggle with parameterized inputs
|
|
837
|
+
* const govBtn = createTagButton('gravity_override', {
|
|
838
|
+
* inputs: [
|
|
839
|
+
* { label: 'x', defaultValue: 0, step: 0.1 },
|
|
840
|
+
* { label: 'y', defaultValue: -1, step: 0.1 },
|
|
841
|
+
* ],
|
|
842
|
+
* });
|
|
843
|
+
* container.appendChild(govBtn.element);
|
|
844
|
+
*
|
|
845
|
+
* // Read state when needed
|
|
846
|
+
* if (govBtn.isActive()) {
|
|
847
|
+
* const gx = parseFloat(govBtn.getValue(0));
|
|
848
|
+
* const gy = parseFloat(govBtn.getValue(1));
|
|
849
|
+
* }
|
|
850
|
+
* ```
|
|
851
|
+
*/
|
|
852
|
+
interface TagButtonNumberInputConfig {
|
|
853
|
+
type?: 'number';
|
|
854
|
+
/** Short label rendered before the input (e.g. 'x', 'y') */
|
|
855
|
+
label?: string;
|
|
856
|
+
defaultValue?: number;
|
|
857
|
+
step?: number;
|
|
858
|
+
min?: number;
|
|
859
|
+
max?: number;
|
|
860
|
+
}
|
|
861
|
+
interface TagButtonSelectInputConfig {
|
|
862
|
+
type: 'select';
|
|
863
|
+
label?: string;
|
|
864
|
+
options?: Array<{
|
|
865
|
+
value: string;
|
|
866
|
+
label: string;
|
|
867
|
+
}>;
|
|
868
|
+
}
|
|
869
|
+
type TagButtonInputConfig = TagButtonNumberInputConfig | TagButtonSelectInputConfig;
|
|
870
|
+
interface TagButtonConfig {
|
|
871
|
+
defaultActive?: boolean;
|
|
872
|
+
inputs?: TagButtonInputConfig[];
|
|
873
|
+
onChange?: (active: boolean) => void;
|
|
874
|
+
}
|
|
875
|
+
interface TagButton {
|
|
876
|
+
element: HTMLElement;
|
|
877
|
+
isActive(): boolean;
|
|
878
|
+
setActive(active: boolean): void;
|
|
879
|
+
toggle(): void;
|
|
880
|
+
/** Get the string value of the input at position index */
|
|
881
|
+
getValue(index: number): string;
|
|
882
|
+
/** Direct access to the underlying input or select element */
|
|
883
|
+
getInput(index: number): HTMLInputElement | HTMLSelectElement | undefined;
|
|
884
|
+
}
|
|
885
|
+
declare function createTagButton(label: string, config?: TagButtonConfig): TagButton;
|
|
886
|
+
|
|
826
887
|
/**
|
|
827
888
|
* @blorkfield/blork-tabs - Panel
|
|
828
889
|
* Individual panel component with collapse/expand functionality
|
|
@@ -955,4 +1016,4 @@ declare function getMovingGroupRespectingPins(grabbedPanel: PanelState, panels:
|
|
|
955
1016
|
*/
|
|
956
1017
|
declare function unsnap(leftPanel: PanelState, rightPanel: PanelState): void;
|
|
957
1018
|
|
|
958
|
-
export { type AnchorConfig, AnchorManager, type AnchorPreset, type AnchorSnapEvent, type AnchorSnapResult, type AnchorState, type AutoHideCallbacks, AutoHideManager, type Bounds, type CSSClasses, type DebugLog, type DebugLogConfig, type DebugLogLevel, type DebugPanel, type DebugPanelConfig, type DragEndEvent, DragManager, type DragMode, type DragMoveEvent, type DragStartEvent, type DragState, type EventListener, type PanelAddedEvent, type PanelCollapseEvent, type PanelConfig, type PanelDetachedEvent, type PanelHideEvent, type PanelPinEvent, type PanelRemovedEvent, type PanelShowEvent, type PanelSnapEvent, type PanelState, type Position, type ResolvedTabManagerConfig, SnapPreview, type SnapSide, type SnapTarget, TabManager, type TabManagerConfig, type TabManagerEvents, areInSameChain, createDebugLog, createDebugPanelContent, createDebugPanelInterface, createPanelElement, createPanelState, createPresetAnchor, detachFromGroup, findSnapTarget, getConnectedGroup, getDefaultAnchorConfigs, getDefaultZIndex, getDragZIndex, getLeftmostPanel, getMovingGroupRespectingPins, getPanelDimensions, getPanelPosition, getRightmostPanel, hidePanel, setPanelPosition, setPanelZIndex, setupHoverEnlarge, showPanel, snapPanels, snapPanelsToTarget, toggleCollapse, togglePin, unsnap, updateSnappedPositions };
|
|
1019
|
+
export { type AnchorConfig, AnchorManager, type AnchorPreset, type AnchorSnapEvent, type AnchorSnapResult, type AnchorState, type AutoHideCallbacks, AutoHideManager, type Bounds, type CSSClasses, type DebugLog, type DebugLogConfig, type DebugLogLevel, type DebugPanel, type DebugPanelConfig, type DragEndEvent, DragManager, type DragMode, type DragMoveEvent, type DragStartEvent, type DragState, type EventListener, type PanelAddedEvent, type PanelCollapseEvent, type PanelConfig, type PanelDetachedEvent, type PanelHideEvent, type PanelPinEvent, type PanelRemovedEvent, type PanelShowEvent, type PanelSnapEvent, type PanelState, type Position, type ResolvedTabManagerConfig, SnapPreview, type SnapSide, type SnapTarget, TabManager, type TabManagerConfig, type TabManagerEvents, type TagButton, type TagButtonConfig, type TagButtonInputConfig, type TagButtonNumberInputConfig, type TagButtonSelectInputConfig, areInSameChain, createDebugLog, createDebugPanelContent, createDebugPanelInterface, createPanelElement, createPanelState, createPresetAnchor, createTagButton, detachFromGroup, findSnapTarget, getConnectedGroup, getDefaultAnchorConfigs, getDefaultZIndex, getDragZIndex, getLeftmostPanel, getMovingGroupRespectingPins, getPanelDimensions, getPanelPosition, getRightmostPanel, hidePanel, setPanelPosition, setPanelZIndex, setupHoverEnlarge, showPanel, snapPanels, snapPanelsToTarget, toggleCollapse, togglePin, unsnap, updateSnappedPositions };
|
package/dist/index.d.ts
CHANGED
|
@@ -823,6 +823,67 @@ interface HoverEnlargeConfig {
|
|
|
823
823
|
*/
|
|
824
824
|
declare function setupHoverEnlarge(config: HoverEnlargeConfig): void;
|
|
825
825
|
|
|
826
|
+
/**
|
|
827
|
+
* Tag button: a toggleable pill-shaped button that optionally reveals
|
|
828
|
+
* inline inputs (numbers, selects) when active.
|
|
829
|
+
*
|
|
830
|
+
* @example
|
|
831
|
+
* ```typescript
|
|
832
|
+
* // Simple toggle
|
|
833
|
+
* const staticBtn = createTagButton('static');
|
|
834
|
+
* container.appendChild(staticBtn.element);
|
|
835
|
+
*
|
|
836
|
+
* // Toggle with parameterized inputs
|
|
837
|
+
* const govBtn = createTagButton('gravity_override', {
|
|
838
|
+
* inputs: [
|
|
839
|
+
* { label: 'x', defaultValue: 0, step: 0.1 },
|
|
840
|
+
* { label: 'y', defaultValue: -1, step: 0.1 },
|
|
841
|
+
* ],
|
|
842
|
+
* });
|
|
843
|
+
* container.appendChild(govBtn.element);
|
|
844
|
+
*
|
|
845
|
+
* // Read state when needed
|
|
846
|
+
* if (govBtn.isActive()) {
|
|
847
|
+
* const gx = parseFloat(govBtn.getValue(0));
|
|
848
|
+
* const gy = parseFloat(govBtn.getValue(1));
|
|
849
|
+
* }
|
|
850
|
+
* ```
|
|
851
|
+
*/
|
|
852
|
+
interface TagButtonNumberInputConfig {
|
|
853
|
+
type?: 'number';
|
|
854
|
+
/** Short label rendered before the input (e.g. 'x', 'y') */
|
|
855
|
+
label?: string;
|
|
856
|
+
defaultValue?: number;
|
|
857
|
+
step?: number;
|
|
858
|
+
min?: number;
|
|
859
|
+
max?: number;
|
|
860
|
+
}
|
|
861
|
+
interface TagButtonSelectInputConfig {
|
|
862
|
+
type: 'select';
|
|
863
|
+
label?: string;
|
|
864
|
+
options?: Array<{
|
|
865
|
+
value: string;
|
|
866
|
+
label: string;
|
|
867
|
+
}>;
|
|
868
|
+
}
|
|
869
|
+
type TagButtonInputConfig = TagButtonNumberInputConfig | TagButtonSelectInputConfig;
|
|
870
|
+
interface TagButtonConfig {
|
|
871
|
+
defaultActive?: boolean;
|
|
872
|
+
inputs?: TagButtonInputConfig[];
|
|
873
|
+
onChange?: (active: boolean) => void;
|
|
874
|
+
}
|
|
875
|
+
interface TagButton {
|
|
876
|
+
element: HTMLElement;
|
|
877
|
+
isActive(): boolean;
|
|
878
|
+
setActive(active: boolean): void;
|
|
879
|
+
toggle(): void;
|
|
880
|
+
/** Get the string value of the input at position index */
|
|
881
|
+
getValue(index: number): string;
|
|
882
|
+
/** Direct access to the underlying input or select element */
|
|
883
|
+
getInput(index: number): HTMLInputElement | HTMLSelectElement | undefined;
|
|
884
|
+
}
|
|
885
|
+
declare function createTagButton(label: string, config?: TagButtonConfig): TagButton;
|
|
886
|
+
|
|
826
887
|
/**
|
|
827
888
|
* @blorkfield/blork-tabs - Panel
|
|
828
889
|
* Individual panel component with collapse/expand functionality
|
|
@@ -955,4 +1016,4 @@ declare function getMovingGroupRespectingPins(grabbedPanel: PanelState, panels:
|
|
|
955
1016
|
*/
|
|
956
1017
|
declare function unsnap(leftPanel: PanelState, rightPanel: PanelState): void;
|
|
957
1018
|
|
|
958
|
-
export { type AnchorConfig, AnchorManager, type AnchorPreset, type AnchorSnapEvent, type AnchorSnapResult, type AnchorState, type AutoHideCallbacks, AutoHideManager, type Bounds, type CSSClasses, type DebugLog, type DebugLogConfig, type DebugLogLevel, type DebugPanel, type DebugPanelConfig, type DragEndEvent, DragManager, type DragMode, type DragMoveEvent, type DragStartEvent, type DragState, type EventListener, type PanelAddedEvent, type PanelCollapseEvent, type PanelConfig, type PanelDetachedEvent, type PanelHideEvent, type PanelPinEvent, type PanelRemovedEvent, type PanelShowEvent, type PanelSnapEvent, type PanelState, type Position, type ResolvedTabManagerConfig, SnapPreview, type SnapSide, type SnapTarget, TabManager, type TabManagerConfig, type TabManagerEvents, areInSameChain, createDebugLog, createDebugPanelContent, createDebugPanelInterface, createPanelElement, createPanelState, createPresetAnchor, detachFromGroup, findSnapTarget, getConnectedGroup, getDefaultAnchorConfigs, getDefaultZIndex, getDragZIndex, getLeftmostPanel, getMovingGroupRespectingPins, getPanelDimensions, getPanelPosition, getRightmostPanel, hidePanel, setPanelPosition, setPanelZIndex, setupHoverEnlarge, showPanel, snapPanels, snapPanelsToTarget, toggleCollapse, togglePin, unsnap, updateSnappedPositions };
|
|
1019
|
+
export { type AnchorConfig, AnchorManager, type AnchorPreset, type AnchorSnapEvent, type AnchorSnapResult, type AnchorState, type AutoHideCallbacks, AutoHideManager, type Bounds, type CSSClasses, type DebugLog, type DebugLogConfig, type DebugLogLevel, type DebugPanel, type DebugPanelConfig, type DragEndEvent, DragManager, type DragMode, type DragMoveEvent, type DragStartEvent, type DragState, type EventListener, type PanelAddedEvent, type PanelCollapseEvent, type PanelConfig, type PanelDetachedEvent, type PanelHideEvent, type PanelPinEvent, type PanelRemovedEvent, type PanelShowEvent, type PanelSnapEvent, type PanelState, type Position, type ResolvedTabManagerConfig, SnapPreview, type SnapSide, type SnapTarget, TabManager, type TabManagerConfig, type TabManagerEvents, type TagButton, type TagButtonConfig, type TagButtonInputConfig, type TagButtonNumberInputConfig, type TagButtonSelectInputConfig, areInSameChain, createDebugLog, createDebugPanelContent, createDebugPanelInterface, createPanelElement, createPanelState, createPresetAnchor, createTagButton, detachFromGroup, findSnapTarget, getConnectedGroup, getDefaultAnchorConfigs, getDefaultZIndex, getDragZIndex, getLeftmostPanel, getMovingGroupRespectingPins, getPanelDimensions, getPanelPosition, getRightmostPanel, hidePanel, setPanelPosition, setPanelZIndex, setupHoverEnlarge, showPanel, snapPanels, snapPanelsToTarget, toggleCollapse, togglePin, unsnap, updateSnappedPositions };
|
package/dist/index.js
CHANGED
|
@@ -1574,6 +1574,80 @@ var TabManager = class {
|
|
|
1574
1574
|
this.debugPanelElements.clear();
|
|
1575
1575
|
}
|
|
1576
1576
|
};
|
|
1577
|
+
|
|
1578
|
+
// src/TagButton.ts
|
|
1579
|
+
function createTagButton(label, config = {}) {
|
|
1580
|
+
const { defaultActive = false, inputs = [], onChange } = config;
|
|
1581
|
+
const hasInputs = inputs.length > 0;
|
|
1582
|
+
const el = document.createElement(hasInputs ? "div" : "button");
|
|
1583
|
+
el.className = "blork-tabs-tag-btn";
|
|
1584
|
+
if (hasInputs) {
|
|
1585
|
+
el.setAttribute("role", "button");
|
|
1586
|
+
el.setAttribute("tabindex", "0");
|
|
1587
|
+
}
|
|
1588
|
+
el.appendChild(document.createTextNode(label));
|
|
1589
|
+
const inputElements = [];
|
|
1590
|
+
if (hasInputs) {
|
|
1591
|
+
const inputsContainer = document.createElement("span");
|
|
1592
|
+
inputsContainer.className = "blork-tabs-tag-inputs";
|
|
1593
|
+
for (const inputConfig of inputs) {
|
|
1594
|
+
if (inputConfig.label) {
|
|
1595
|
+
const labelSpan = document.createElement("span");
|
|
1596
|
+
labelSpan.textContent = inputConfig.label;
|
|
1597
|
+
inputsContainer.appendChild(labelSpan);
|
|
1598
|
+
}
|
|
1599
|
+
if (inputConfig.type === "select") {
|
|
1600
|
+
const select = document.createElement("select");
|
|
1601
|
+
select.className = "blork-tabs-tag-select";
|
|
1602
|
+
for (const opt of inputConfig.options ?? []) {
|
|
1603
|
+
const option = document.createElement("option");
|
|
1604
|
+
option.value = opt.value;
|
|
1605
|
+
option.textContent = opt.label;
|
|
1606
|
+
select.appendChild(option);
|
|
1607
|
+
}
|
|
1608
|
+
select.addEventListener("click", (e) => e.stopPropagation());
|
|
1609
|
+
select.addEventListener("mousedown", (e) => e.stopPropagation());
|
|
1610
|
+
inputsContainer.appendChild(select);
|
|
1611
|
+
inputElements.push(select);
|
|
1612
|
+
} else {
|
|
1613
|
+
const input = document.createElement("input");
|
|
1614
|
+
input.type = "number";
|
|
1615
|
+
input.className = "blork-tabs-tag-input";
|
|
1616
|
+
if (inputConfig.defaultValue !== void 0) input.value = String(inputConfig.defaultValue);
|
|
1617
|
+
if (inputConfig.step !== void 0) input.step = String(inputConfig.step);
|
|
1618
|
+
if (inputConfig.min !== void 0) input.min = String(inputConfig.min);
|
|
1619
|
+
if (inputConfig.max !== void 0) input.max = String(inputConfig.max);
|
|
1620
|
+
input.addEventListener("click", (e) => e.stopPropagation());
|
|
1621
|
+
inputsContainer.appendChild(input);
|
|
1622
|
+
inputElements.push(input);
|
|
1623
|
+
}
|
|
1624
|
+
}
|
|
1625
|
+
el.appendChild(inputsContainer);
|
|
1626
|
+
}
|
|
1627
|
+
let active = defaultActive;
|
|
1628
|
+
if (active) el.classList.add("active");
|
|
1629
|
+
const setActive = (value) => {
|
|
1630
|
+
active = value;
|
|
1631
|
+
el.classList.toggle("active", active);
|
|
1632
|
+
onChange?.(active);
|
|
1633
|
+
};
|
|
1634
|
+
el.addEventListener("click", () => setActive(!active));
|
|
1635
|
+
el.addEventListener("keydown", (e) => {
|
|
1636
|
+
const ke = e;
|
|
1637
|
+
if (ke.key === "Enter" || ke.key === " ") {
|
|
1638
|
+
ke.preventDefault();
|
|
1639
|
+
setActive(!active);
|
|
1640
|
+
}
|
|
1641
|
+
});
|
|
1642
|
+
return {
|
|
1643
|
+
element: el,
|
|
1644
|
+
isActive: () => active,
|
|
1645
|
+
setActive,
|
|
1646
|
+
toggle: () => setActive(!active),
|
|
1647
|
+
getValue: (index) => inputElements[index]?.value ?? "",
|
|
1648
|
+
getInput: (index) => inputElements[index]
|
|
1649
|
+
};
|
|
1650
|
+
}
|
|
1577
1651
|
export {
|
|
1578
1652
|
AnchorManager,
|
|
1579
1653
|
AutoHideManager,
|
|
@@ -1587,6 +1661,7 @@ export {
|
|
|
1587
1661
|
createPanelElement,
|
|
1588
1662
|
createPanelState,
|
|
1589
1663
|
createPresetAnchor,
|
|
1664
|
+
createTagButton,
|
|
1590
1665
|
detachFromGroup,
|
|
1591
1666
|
findSnapTarget,
|
|
1592
1667
|
getConnectedGroup,
|
package/dist/styles.css
CHANGED
|
@@ -220,6 +220,88 @@
|
|
|
220
220
|
transition: none !important;
|
|
221
221
|
}
|
|
222
222
|
|
|
223
|
+
/* Tag Button */
|
|
224
|
+
.blork-tabs-tag-btn {
|
|
225
|
+
padding: 4px 10px;
|
|
226
|
+
background: var(--blork-tabs-panel-bg, #1a1a2e);
|
|
227
|
+
border: 1px solid #3a3a5a;
|
|
228
|
+
border-radius: 4px;
|
|
229
|
+
color: #aaa;
|
|
230
|
+
cursor: pointer;
|
|
231
|
+
font-size: 11px;
|
|
232
|
+
font-family: monospace;
|
|
233
|
+
transition: all 0.15s;
|
|
234
|
+
user-select: none;
|
|
235
|
+
display: inline-flex;
|
|
236
|
+
align-items: center;
|
|
237
|
+
gap: 6px;
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
.blork-tabs-tag-btn:hover {
|
|
241
|
+
border-color: #5a5a7a;
|
|
242
|
+
color: #ccc;
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
.blork-tabs-tag-btn.active {
|
|
246
|
+
border-color: var(--blork-tabs-accent, #4a90d9);
|
|
247
|
+
color: var(--blork-tabs-accent, #4a90d9);
|
|
248
|
+
box-shadow: 0 0 8px rgba(74, 144, 217, 0.5), 0 0 2px rgba(74, 144, 217, 0.8);
|
|
249
|
+
text-shadow: 0 0 8px rgba(74, 144, 217, 0.9);
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
/* Tag inputs (hidden until button is active) */
|
|
253
|
+
.blork-tabs-tag-inputs {
|
|
254
|
+
display: none;
|
|
255
|
+
align-items: center;
|
|
256
|
+
gap: 3px;
|
|
257
|
+
font-size: 10px;
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
.blork-tabs-tag-btn.active .blork-tabs-tag-inputs {
|
|
261
|
+
display: inline-flex;
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
.blork-tabs-tag-input {
|
|
265
|
+
width: 36px;
|
|
266
|
+
background: transparent;
|
|
267
|
+
border: none;
|
|
268
|
+
border-bottom: 1px solid #555;
|
|
269
|
+
color: inherit;
|
|
270
|
+
font-size: 10px;
|
|
271
|
+
font-family: monospace;
|
|
272
|
+
text-align: center;
|
|
273
|
+
padding: 0 2px;
|
|
274
|
+
outline: none;
|
|
275
|
+
-moz-appearance: textfield;
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
.blork-tabs-tag-input::-webkit-outer-spin-button,
|
|
279
|
+
.blork-tabs-tag-input::-webkit-inner-spin-button {
|
|
280
|
+
-webkit-appearance: none;
|
|
281
|
+
margin: 0;
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
.blork-tabs-tag-btn.active .blork-tabs-tag-input {
|
|
285
|
+
border-bottom-color: rgba(74, 144, 217, 0.6);
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
.blork-tabs-tag-select {
|
|
289
|
+
background: var(--blork-tabs-panel-bg, #1a1a2e);
|
|
290
|
+
border: none;
|
|
291
|
+
border-bottom: 1px solid #555;
|
|
292
|
+
color: #ccc;
|
|
293
|
+
font-size: 10px;
|
|
294
|
+
font-family: monospace;
|
|
295
|
+
outline: none;
|
|
296
|
+
cursor: pointer;
|
|
297
|
+
max-width: 80px;
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
.blork-tabs-tag-btn.active .blork-tabs-tag-select {
|
|
301
|
+
border-bottom-color: rgba(74, 144, 217, 0.6);
|
|
302
|
+
color: var(--blork-tabs-accent, #4a90d9);
|
|
303
|
+
}
|
|
304
|
+
|
|
223
305
|
/* Debug Panel Log */
|
|
224
306
|
.blork-tabs-debug-log {
|
|
225
307
|
min-height: 100px;
|
package/package.json
CHANGED
package/src/TagButton.ts
ADDED
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tag button: a toggleable pill-shaped button that optionally reveals
|
|
3
|
+
* inline inputs (numbers, selects) when active.
|
|
4
|
+
*
|
|
5
|
+
* @example
|
|
6
|
+
* ```typescript
|
|
7
|
+
* // Simple toggle
|
|
8
|
+
* const staticBtn = createTagButton('static');
|
|
9
|
+
* container.appendChild(staticBtn.element);
|
|
10
|
+
*
|
|
11
|
+
* // Toggle with parameterized inputs
|
|
12
|
+
* const govBtn = createTagButton('gravity_override', {
|
|
13
|
+
* inputs: [
|
|
14
|
+
* { label: 'x', defaultValue: 0, step: 0.1 },
|
|
15
|
+
* { label: 'y', defaultValue: -1, step: 0.1 },
|
|
16
|
+
* ],
|
|
17
|
+
* });
|
|
18
|
+
* container.appendChild(govBtn.element);
|
|
19
|
+
*
|
|
20
|
+
* // Read state when needed
|
|
21
|
+
* if (govBtn.isActive()) {
|
|
22
|
+
* const gx = parseFloat(govBtn.getValue(0));
|
|
23
|
+
* const gy = parseFloat(govBtn.getValue(1));
|
|
24
|
+
* }
|
|
25
|
+
* ```
|
|
26
|
+
*/
|
|
27
|
+
|
|
28
|
+
export interface TagButtonNumberInputConfig {
|
|
29
|
+
type?: 'number';
|
|
30
|
+
/** Short label rendered before the input (e.g. 'x', 'y') */
|
|
31
|
+
label?: string;
|
|
32
|
+
defaultValue?: number;
|
|
33
|
+
step?: number;
|
|
34
|
+
min?: number;
|
|
35
|
+
max?: number;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export interface TagButtonSelectInputConfig {
|
|
39
|
+
type: 'select';
|
|
40
|
+
label?: string;
|
|
41
|
+
options?: Array<{ value: string; label: string }>;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export type TagButtonInputConfig = TagButtonNumberInputConfig | TagButtonSelectInputConfig;
|
|
45
|
+
|
|
46
|
+
export interface TagButtonConfig {
|
|
47
|
+
defaultActive?: boolean;
|
|
48
|
+
inputs?: TagButtonInputConfig[];
|
|
49
|
+
onChange?: (active: boolean) => void;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
export interface TagButton {
|
|
53
|
+
element: HTMLElement;
|
|
54
|
+
isActive(): boolean;
|
|
55
|
+
setActive(active: boolean): void;
|
|
56
|
+
toggle(): void;
|
|
57
|
+
/** Get the string value of the input at position index */
|
|
58
|
+
getValue(index: number): string;
|
|
59
|
+
/** Direct access to the underlying input or select element */
|
|
60
|
+
getInput(index: number): HTMLInputElement | HTMLSelectElement | undefined;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
export function createTagButton(label: string, config: TagButtonConfig = {}): TagButton {
|
|
64
|
+
const { defaultActive = false, inputs = [], onChange } = config;
|
|
65
|
+
|
|
66
|
+
const hasInputs = inputs.length > 0;
|
|
67
|
+
const el = document.createElement(hasInputs ? 'div' : 'button') as HTMLElement;
|
|
68
|
+
el.className = 'blork-tabs-tag-btn';
|
|
69
|
+
if (hasInputs) {
|
|
70
|
+
el.setAttribute('role', 'button');
|
|
71
|
+
el.setAttribute('tabindex', '0');
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
el.appendChild(document.createTextNode(label));
|
|
75
|
+
|
|
76
|
+
const inputElements: Array<HTMLInputElement | HTMLSelectElement> = [];
|
|
77
|
+
|
|
78
|
+
if (hasInputs) {
|
|
79
|
+
const inputsContainer = document.createElement('span');
|
|
80
|
+
inputsContainer.className = 'blork-tabs-tag-inputs';
|
|
81
|
+
|
|
82
|
+
for (const inputConfig of inputs) {
|
|
83
|
+
if (inputConfig.label) {
|
|
84
|
+
const labelSpan = document.createElement('span');
|
|
85
|
+
labelSpan.textContent = inputConfig.label;
|
|
86
|
+
inputsContainer.appendChild(labelSpan);
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
if (inputConfig.type === 'select') {
|
|
90
|
+
const select = document.createElement('select');
|
|
91
|
+
select.className = 'blork-tabs-tag-select';
|
|
92
|
+
for (const opt of (inputConfig.options ?? [])) {
|
|
93
|
+
const option = document.createElement('option');
|
|
94
|
+
option.value = opt.value;
|
|
95
|
+
option.textContent = opt.label;
|
|
96
|
+
select.appendChild(option);
|
|
97
|
+
}
|
|
98
|
+
select.addEventListener('click', e => e.stopPropagation());
|
|
99
|
+
select.addEventListener('mousedown', e => e.stopPropagation());
|
|
100
|
+
inputsContainer.appendChild(select);
|
|
101
|
+
inputElements.push(select);
|
|
102
|
+
} else {
|
|
103
|
+
const input = document.createElement('input');
|
|
104
|
+
input.type = 'number';
|
|
105
|
+
input.className = 'blork-tabs-tag-input';
|
|
106
|
+
if (inputConfig.defaultValue !== undefined) input.value = String(inputConfig.defaultValue);
|
|
107
|
+
if (inputConfig.step !== undefined) input.step = String(inputConfig.step);
|
|
108
|
+
if (inputConfig.min !== undefined) input.min = String(inputConfig.min);
|
|
109
|
+
if (inputConfig.max !== undefined) input.max = String(inputConfig.max);
|
|
110
|
+
input.addEventListener('click', e => e.stopPropagation());
|
|
111
|
+
inputsContainer.appendChild(input);
|
|
112
|
+
inputElements.push(input);
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
el.appendChild(inputsContainer);
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
let active = defaultActive;
|
|
120
|
+
if (active) el.classList.add('active');
|
|
121
|
+
|
|
122
|
+
const setActive = (value: boolean) => {
|
|
123
|
+
active = value;
|
|
124
|
+
el.classList.toggle('active', active);
|
|
125
|
+
onChange?.(active);
|
|
126
|
+
};
|
|
127
|
+
|
|
128
|
+
el.addEventListener('click', () => setActive(!active));
|
|
129
|
+
el.addEventListener('keydown', (e: Event) => {
|
|
130
|
+
const ke = e as KeyboardEvent;
|
|
131
|
+
if (ke.key === 'Enter' || ke.key === ' ') {
|
|
132
|
+
ke.preventDefault();
|
|
133
|
+
setActive(!active);
|
|
134
|
+
}
|
|
135
|
+
});
|
|
136
|
+
|
|
137
|
+
return {
|
|
138
|
+
element: el,
|
|
139
|
+
isActive: () => active,
|
|
140
|
+
setActive,
|
|
141
|
+
toggle: () => setActive(!active),
|
|
142
|
+
getValue: (index: number) => inputElements[index]?.value ?? '',
|
|
143
|
+
getInput: (index: number) => inputElements[index],
|
|
144
|
+
};
|
|
145
|
+
}
|
package/src/index.ts
CHANGED
|
@@ -43,6 +43,8 @@ export { DragManager } from './DragManager';
|
|
|
43
43
|
export { SnapPreview } from './SnapPreview';
|
|
44
44
|
export { AutoHideManager } from './AutoHideManager';
|
|
45
45
|
export { createDebugPanelContent, createDebugPanelInterface, createDebugLog, setupHoverEnlarge } from './DebugPanel';
|
|
46
|
+
export { createTagButton } from './TagButton';
|
|
47
|
+
export type { TagButtonConfig, TagButtonInputConfig, TagButtonNumberInputConfig, TagButtonSelectInputConfig, TagButton } from './TagButton';
|
|
46
48
|
export type { AutoHideCallbacks } from './AutoHideManager';
|
|
47
49
|
export {
|
|
48
50
|
createPanelElement,
|