@memberjunction/ng-ui-components 5.51.0 → 6.1.0-edge.1
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 +7 -0
- package/README.md +51 -2
- package/dist/lib/bottom-sheet/bottom-sheet.component.d.ts +96 -0
- package/dist/lib/bottom-sheet/bottom-sheet.component.d.ts.map +1 -0
- package/dist/lib/bottom-sheet/bottom-sheet.component.js +300 -0
- package/dist/lib/bottom-sheet/bottom-sheet.component.js.map +1 -0
- package/dist/lib/left-nav/left-nav.component.d.ts +142 -3
- package/dist/lib/left-nav/left-nav.component.d.ts.map +1 -1
- package/dist/lib/left-nav/left-nav.component.js +391 -117
- package/dist/lib/left-nav/left-nav.component.js.map +1 -1
- package/dist/lib/slide-panel/slide-panel.component.js +6 -6
- package/dist/lib/slide-panel/slide-panel.component.js.map +1 -1
- package/dist/lib/tabs/tab-chrome-guard.d.ts +14 -0
- package/dist/lib/tabs/tab-chrome-guard.d.ts.map +1 -0
- package/dist/lib/tabs/tab-chrome-guard.js +36 -0
- package/dist/lib/tabs/tab-chrome-guard.js.map +1 -0
- package/dist/lib/tabs/tab-list.directive.d.ts +93 -0
- package/dist/lib/tabs/tab-list.directive.d.ts.map +1 -0
- package/dist/lib/tabs/tab-list.directive.js +202 -0
- package/dist/lib/tabs/tab-list.directive.js.map +1 -0
- package/dist/lib/tabs/tabs.scss +242 -0
- package/dist/lib/workspace-tabs/workspace-card.component.d.ts +63 -0
- package/dist/lib/workspace-tabs/workspace-card.component.d.ts.map +1 -0
- package/dist/lib/workspace-tabs/workspace-card.component.js +256 -0
- package/dist/lib/workspace-tabs/workspace-card.component.js.map +1 -0
- package/dist/lib/workspace-tabs/workspace-tab-store.d.ts +47 -0
- package/dist/lib/workspace-tabs/workspace-tab-store.d.ts.map +1 -0
- package/dist/lib/workspace-tabs/workspace-tab-store.js +112 -0
- package/dist/lib/workspace-tabs/workspace-tab-store.js.map +1 -0
- package/dist/lib/workspace-tabs/workspace-tab-strip.component.d.ts +75 -0
- package/dist/lib/workspace-tabs/workspace-tab-strip.component.d.ts.map +1 -0
- package/dist/lib/workspace-tabs/workspace-tab-strip.component.js +252 -0
- package/dist/lib/workspace-tabs/workspace-tab-strip.component.js.map +1 -0
- package/dist/lib/workspace-tabs/workspace-tabs.types.d.ts +40 -0
- package/dist/lib/workspace-tabs/workspace-tabs.types.d.ts.map +1 -0
- package/dist/lib/workspace-tabs/workspace-tabs.types.js +13 -0
- package/dist/lib/workspace-tabs/workspace-tabs.types.js.map +1 -0
- package/dist/lib/workspace-tabs/workspace-tip.directive.d.ts +34 -0
- package/dist/lib/workspace-tabs/workspace-tip.directive.d.ts.map +1 -0
- package/dist/lib/workspace-tabs/workspace-tip.directive.js +123 -0
- package/dist/lib/workspace-tabs/workspace-tip.directive.js.map +1 -0
- package/dist/public-api.d.ts +8 -0
- package/dist/public-api.d.ts.map +1 -1
- package/dist/public-api.js +8 -0
- package/dist/public-api.js.map +1 -1
- package/package.json +14 -12
|
@@ -0,0 +1,256 @@
|
|
|
1
|
+
import { Component, ChangeDetectionStrategy, Input, Output, EventEmitter } from '@angular/core';
|
|
2
|
+
import { CommonModule } from '@angular/common';
|
|
3
|
+
import { MJWorkspaceTabStripComponent } from './workspace-tab-strip.component';
|
|
4
|
+
import { MJButtonDirective } from '../button/button.directive';
|
|
5
|
+
import * as i0 from "@angular/core";
|
|
6
|
+
const _c0 = [[["", "workspaceHeader", ""]], "*", [["", "workspaceFooterNote", ""]]];
|
|
7
|
+
const _c1 = ["[workspaceHeader]", "*", "[workspaceFooterNote]"];
|
|
8
|
+
function MJWorkspaceCardComponent_Conditional_7_Conditional_2_Template(rf, ctx) { if (rf & 1) {
|
|
9
|
+
i0.ɵɵelement(0, "i", 10);
|
|
10
|
+
i0.ɵɵtext(1);
|
|
11
|
+
} if (rf & 2) {
|
|
12
|
+
const ctx_r1 = i0.ɵɵnextContext(2);
|
|
13
|
+
i0.ɵɵadvance();
|
|
14
|
+
i0.ɵɵtextInterpolate1(" ", ctx_r1.ConfirmBusyLabel, " ");
|
|
15
|
+
} }
|
|
16
|
+
function MJWorkspaceCardComponent_Conditional_7_Conditional_3_Conditional_0_Template(rf, ctx) { if (rf & 1) {
|
|
17
|
+
i0.ɵɵelement(0, "i", 12);
|
|
18
|
+
} if (rf & 2) {
|
|
19
|
+
const ctx_r1 = i0.ɵɵnextContext(3);
|
|
20
|
+
i0.ɵɵclassMap(ctx_r1.ConfirmIcon);
|
|
21
|
+
} }
|
|
22
|
+
function MJWorkspaceCardComponent_Conditional_7_Conditional_3_Template(rf, ctx) { if (rf & 1) {
|
|
23
|
+
i0.ɵɵconditionalCreate(0, MJWorkspaceCardComponent_Conditional_7_Conditional_3_Conditional_0_Template, 1, 2, "i", 11);
|
|
24
|
+
i0.ɵɵtext(1);
|
|
25
|
+
} if (rf & 2) {
|
|
26
|
+
const ctx_r1 = i0.ɵɵnextContext(2);
|
|
27
|
+
i0.ɵɵconditional(ctx_r1.ConfirmIcon ? 0 : -1);
|
|
28
|
+
i0.ɵɵadvance();
|
|
29
|
+
i0.ɵɵtextInterpolate1(" ", ctx_r1.ConfirmLabel, " ");
|
|
30
|
+
} }
|
|
31
|
+
function MJWorkspaceCardComponent_Conditional_7_Conditional_4_Conditional_1_Template(rf, ctx) { if (rf & 1) {
|
|
32
|
+
i0.ɵɵelement(0, "i", 12);
|
|
33
|
+
} if (rf & 2) {
|
|
34
|
+
const ctx_r1 = i0.ɵɵnextContext(3);
|
|
35
|
+
i0.ɵɵclassMap(ctx_r1.DraftIcon);
|
|
36
|
+
} }
|
|
37
|
+
function MJWorkspaceCardComponent_Conditional_7_Conditional_4_Template(rf, ctx) { if (rf & 1) {
|
|
38
|
+
const _r3 = i0.ɵɵgetCurrentView();
|
|
39
|
+
i0.ɵɵelementStart(0, "button", 13);
|
|
40
|
+
i0.ɵɵlistener("click", function MJWorkspaceCardComponent_Conditional_7_Conditional_4_Template_button_click_0_listener() { i0.ɵɵrestoreView(_r3); const ctx_r1 = i0.ɵɵnextContext(2); return i0.ɵɵresetView(ctx_r1.SaveDraft.emit()); });
|
|
41
|
+
i0.ɵɵconditionalCreate(1, MJWorkspaceCardComponent_Conditional_7_Conditional_4_Conditional_1_Template, 1, 2, "i", 11);
|
|
42
|
+
i0.ɵɵtext(2);
|
|
43
|
+
i0.ɵɵelementEnd();
|
|
44
|
+
} if (rf & 2) {
|
|
45
|
+
const ctx_r1 = i0.ɵɵnextContext(2);
|
|
46
|
+
i0.ɵɵproperty("disabled", ctx_r1.DraftDisabled);
|
|
47
|
+
i0.ɵɵadvance();
|
|
48
|
+
i0.ɵɵconditional(ctx_r1.DraftIcon ? 1 : -1);
|
|
49
|
+
i0.ɵɵadvance();
|
|
50
|
+
i0.ɵɵtextInterpolate1(" ", ctx_r1.DraftLabel, " ");
|
|
51
|
+
} }
|
|
52
|
+
function MJWorkspaceCardComponent_Conditional_7_Template(rf, ctx) { if (rf & 1) {
|
|
53
|
+
const _r1 = i0.ɵɵgetCurrentView();
|
|
54
|
+
i0.ɵɵelementStart(0, "footer", 5)(1, "button", 6);
|
|
55
|
+
i0.ɵɵlistener("click", function MJWorkspaceCardComponent_Conditional_7_Template_button_click_1_listener() { i0.ɵɵrestoreView(_r1); const ctx_r1 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r1.Confirm.emit()); });
|
|
56
|
+
i0.ɵɵconditionalCreate(2, MJWorkspaceCardComponent_Conditional_7_Conditional_2_Template, 2, 1)(3, MJWorkspaceCardComponent_Conditional_7_Conditional_3_Template, 2, 2);
|
|
57
|
+
i0.ɵɵelementEnd();
|
|
58
|
+
i0.ɵɵconditionalCreate(4, MJWorkspaceCardComponent_Conditional_7_Conditional_4_Template, 3, 3, "button", 7);
|
|
59
|
+
i0.ɵɵelementStart(5, "button", 8);
|
|
60
|
+
i0.ɵɵlistener("click", function MJWorkspaceCardComponent_Conditional_7_Template_button_click_5_listener() { i0.ɵɵrestoreView(_r1); const ctx_r1 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r1.Discard.emit()); });
|
|
61
|
+
i0.ɵɵtext(6);
|
|
62
|
+
i0.ɵɵelementEnd();
|
|
63
|
+
i0.ɵɵelementStart(7, "span", 9);
|
|
64
|
+
i0.ɵɵprojection(8, 2);
|
|
65
|
+
i0.ɵɵelementEnd()();
|
|
66
|
+
} if (rf & 2) {
|
|
67
|
+
const ctx_r1 = i0.ɵɵnextContext();
|
|
68
|
+
i0.ɵɵadvance();
|
|
69
|
+
i0.ɵɵproperty("disabled", ctx_r1.ConfirmDisabled || ctx_r1.ConfirmBusy)("title", ctx_r1.ConfirmTitle || ctx_r1.ConfirmLabel);
|
|
70
|
+
i0.ɵɵadvance();
|
|
71
|
+
i0.ɵɵconditional(ctx_r1.ConfirmBusy ? 2 : 3);
|
|
72
|
+
i0.ɵɵadvance(2);
|
|
73
|
+
i0.ɵɵconditional(ctx_r1.ShowDraft ? 4 : -1);
|
|
74
|
+
i0.ɵɵadvance(2);
|
|
75
|
+
i0.ɵɵtextInterpolate(ctx_r1.DiscardLabel);
|
|
76
|
+
} }
|
|
77
|
+
/**
|
|
78
|
+
* `<mj-workspace-card>` — the reusable frame every "workspace" screen shares (a screen where the
|
|
79
|
+
* user builds several drafts in parallel, each in a tab). A THIN, slotted wrapper: it owns ONLY the
|
|
80
|
+
* invariant chrome —
|
|
81
|
+
* • the card surface (border + rounded corners + contained scroll),
|
|
82
|
+
* • the tab-strip row (delegated to `<mj-workspace-tab-strip>`, which carries all tab behavior),
|
|
83
|
+
* • an identity band beside the tabs, and
|
|
84
|
+
* • a single scrollable body —
|
|
85
|
+
* and PROJECTS everything that varies:
|
|
86
|
+
* • `[workspaceHeader]` — the per-workshop identity band content (e.g. number/status/currency badges);
|
|
87
|
+
* • the default slot — the workshop's form/body;
|
|
88
|
+
* • `[workspaceFooterNote]` — an optional caption beside the standardized footer verbs.
|
|
89
|
+
*
|
|
90
|
+
* The card deliberately bakes in NO workshop-specific header; those differ per workshop and are
|
|
91
|
+
* projected. Height chain: `:host` fills its parent, the card fills the host, the body is the
|
|
92
|
+
* flex:1 scroller — so the BODY scrolls in place while the strip + identity band stay pinned.
|
|
93
|
+
*
|
|
94
|
+
* SIZING RULE for content inside the card: the card is a query container (`container-type: size`),
|
|
95
|
+
* so content sizes with CONTAINER units (`cqh`/`cqw`/`cqi`) — relative to the card as a pane —
|
|
96
|
+
* never viewport units (`vh`/`vw`), which break the moment the card sits in a split or a smaller
|
|
97
|
+
* window.
|
|
98
|
+
*/
|
|
99
|
+
export class MJWorkspaceCardComponent {
|
|
100
|
+
Tabs = [];
|
|
101
|
+
ActiveId = null;
|
|
102
|
+
NewTabLabel = 'New';
|
|
103
|
+
AriaLabel = 'Workspace';
|
|
104
|
+
/** Passed through to the strip — see `MJWorkspaceTabStripComponent.AllowReorder`. */
|
|
105
|
+
AllowReorder = true;
|
|
106
|
+
// ── Standardized footer (opt-in) ────────────────────────────────────────────
|
|
107
|
+
/** Render the standardized action bar (confirm / save-as-draft / discard) below the body. */
|
|
108
|
+
ShowFooter = false;
|
|
109
|
+
/** The primary verb's label — the ONE thing that changes per workshop ("Create entry" / "Build batch"). */
|
|
110
|
+
ConfirmLabel = 'Confirm';
|
|
111
|
+
/** Optional Font Awesome class for the confirm button (e.g. 'fa-solid fa-check'). */
|
|
112
|
+
ConfirmIcon = null;
|
|
113
|
+
ConfirmDisabled = false;
|
|
114
|
+
/** Tooltip / blocked-reason on the confirm button; falls back to ConfirmLabel. */
|
|
115
|
+
ConfirmTitle = null;
|
|
116
|
+
/** Show the spinner + busy label on the confirm button while an async confirm runs. */
|
|
117
|
+
ConfirmBusy = false;
|
|
118
|
+
ConfirmBusyLabel = 'Working…';
|
|
119
|
+
/** Whether the save-as-draft button appears (some workshops have no draft concept). */
|
|
120
|
+
ShowDraft = true;
|
|
121
|
+
DraftLabel = 'Keep as draft';
|
|
122
|
+
DraftIcon = 'fa-regular fa-floppy-disk';
|
|
123
|
+
DraftDisabled = false;
|
|
124
|
+
DiscardLabel = 'Discard';
|
|
125
|
+
TabSelected = new EventEmitter();
|
|
126
|
+
TabClosed = new EventEmitter();
|
|
127
|
+
NewTabRequested = new EventEmitter();
|
|
128
|
+
TabReordered = new EventEmitter();
|
|
129
|
+
/** The primary verb (create / build / …). */
|
|
130
|
+
Confirm = new EventEmitter();
|
|
131
|
+
SaveDraft = new EventEmitter();
|
|
132
|
+
Discard = new EventEmitter();
|
|
133
|
+
static ɵfac = function MJWorkspaceCardComponent_Factory(__ngFactoryType__) { return new (__ngFactoryType__ || MJWorkspaceCardComponent)(); };
|
|
134
|
+
static ɵcmp = /*@__PURE__*/ i0.ɵɵdefineComponent({ type: MJWorkspaceCardComponent, selectors: [["mj-workspace-card"]], inputs: { Tabs: "Tabs", ActiveId: "ActiveId", NewTabLabel: "NewTabLabel", AriaLabel: "AriaLabel", AllowReorder: "AllowReorder", ShowFooter: "ShowFooter", ConfirmLabel: "ConfirmLabel", ConfirmIcon: "ConfirmIcon", ConfirmDisabled: "ConfirmDisabled", ConfirmTitle: "ConfirmTitle", ConfirmBusy: "ConfirmBusy", ConfirmBusyLabel: "ConfirmBusyLabel", ShowDraft: "ShowDraft", DraftLabel: "DraftLabel", DraftIcon: "DraftIcon", DraftDisabled: "DraftDisabled", DiscardLabel: "DiscardLabel" }, outputs: { TabSelected: "TabSelected", TabClosed: "TabClosed", NewTabRequested: "NewTabRequested", TabReordered: "TabReordered", Confirm: "Confirm", SaveDraft: "SaveDraft", Discard: "Discard" }, ngContentSelectors: _c1, decls: 8, vars: 6, consts: [[1, "ws-card"], [1, "ws-card__head"], [3, "TabSelected", "TabClosed", "NewTabRequested", "TabReordered", "Tabs", "ActiveId", "NewTabLabel", "AllowReorder"], [1, "ws-card__headmeta"], [1, "ws-card__body"], [1, "ws-card__foot"], ["type", "button", "mjButton", "", "variant", "primary", 3, "click", "disabled", "title"], ["type", "button", "mjButton", "", "variant", "outline", 3, "disabled"], ["type", "button", "mjButton", "", "variant", "flat", 3, "click"], [1, "ws-card__footnote"], ["aria-hidden", "true", 1, "fa-solid", "fa-spinner", "fa-spin"], ["aria-hidden", "true", 3, "class"], ["aria-hidden", "true"], ["type", "button", "mjButton", "", "variant", "outline", 3, "click", "disabled"]], template: function MJWorkspaceCardComponent_Template(rf, ctx) { if (rf & 1) {
|
|
135
|
+
i0.ɵɵprojectionDef(_c0);
|
|
136
|
+
i0.ɵɵelementStart(0, "section", 0)(1, "header", 1)(2, "mj-workspace-tab-strip", 2);
|
|
137
|
+
i0.ɵɵlistener("TabSelected", function MJWorkspaceCardComponent_Template_mj_workspace_tab_strip_TabSelected_2_listener($event) { return ctx.TabSelected.emit($event); })("TabClosed", function MJWorkspaceCardComponent_Template_mj_workspace_tab_strip_TabClosed_2_listener($event) { return ctx.TabClosed.emit($event); })("NewTabRequested", function MJWorkspaceCardComponent_Template_mj_workspace_tab_strip_NewTabRequested_2_listener() { return ctx.NewTabRequested.emit(); })("TabReordered", function MJWorkspaceCardComponent_Template_mj_workspace_tab_strip_TabReordered_2_listener($event) { return ctx.TabReordered.emit($event); });
|
|
138
|
+
i0.ɵɵelementEnd();
|
|
139
|
+
i0.ɵɵelementStart(3, "div", 3);
|
|
140
|
+
i0.ɵɵprojection(4);
|
|
141
|
+
i0.ɵɵelementEnd()();
|
|
142
|
+
i0.ɵɵelementStart(5, "div", 4);
|
|
143
|
+
i0.ɵɵprojection(6, 1);
|
|
144
|
+
i0.ɵɵelementEnd();
|
|
145
|
+
i0.ɵɵconditionalCreate(7, MJWorkspaceCardComponent_Conditional_7_Template, 9, 5, "footer", 5);
|
|
146
|
+
i0.ɵɵelementEnd();
|
|
147
|
+
} if (rf & 2) {
|
|
148
|
+
i0.ɵɵattribute("aria-label", ctx.AriaLabel);
|
|
149
|
+
i0.ɵɵadvance(2);
|
|
150
|
+
i0.ɵɵproperty("Tabs", ctx.Tabs)("ActiveId", ctx.ActiveId)("NewTabLabel", ctx.NewTabLabel)("AllowReorder", ctx.AllowReorder);
|
|
151
|
+
i0.ɵɵadvance(5);
|
|
152
|
+
i0.ɵɵconditional(ctx.ShowFooter ? 7 : -1);
|
|
153
|
+
} }, dependencies: [CommonModule, MJWorkspaceTabStripComponent, MJButtonDirective], styles: ["\n\n\n\n\n[_nghost-%COMP%] {\n display: flex;\n flex-direction: column;\n height: 100%;\n min-height: 0;\n}\n\n\n\n.ws-card[_ngcontent-%COMP%] {\n flex: 1;\n min-height: 0;\n display: flex;\n flex-direction: column;\n background: var(--mj-bg-surface);\n border: 1px solid var(--mj-border-default);\n border-radius: 12px;\n overflow: hidden;\n \n\n\n\n\n container-type: size;\n container-name: workspace;\n}\n\n.ws-card__head[_ngcontent-%COMP%] {\n flex: none;\n}\n\n\n\n.ws-card__headmeta[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n gap: 10px;\n flex-wrap: wrap;\n padding: 10px 14px;\n}\n\n\n.ws-card__headmeta[_ngcontent-%COMP%]:empty {\n display: none;\n}\n\n\n\n\n.ws-card__body[_ngcontent-%COMP%] {\n flex: 1;\n min-height: 0;\n overflow-y: auto;\n padding: 0 14px 14px;\n display: flex;\n flex-direction: column;\n gap: 10px;\n}\n\n\n\n\n\n.ws-card__foot[_ngcontent-%COMP%] {\n flex: none;\n display: flex;\n align-items: center;\n gap: 8px;\n flex-wrap: wrap;\n padding: 4px 14px 12px;\n}\n\n\n.ws-card__footnote[_ngcontent-%COMP%] {\n margin-left: auto;\n max-width: 46ch;\n font-size: 12px;\n color: var(--mj-text-muted);\n}\n.ws-card__footnote[_ngcontent-%COMP%]:empty {\n display: none;\n}"], changeDetection: 0 });
|
|
154
|
+
}
|
|
155
|
+
(() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(MJWorkspaceCardComponent, [{
|
|
156
|
+
type: Component,
|
|
157
|
+
args: [{ standalone: true, selector: 'mj-workspace-card', imports: [CommonModule, MJWorkspaceTabStripComponent, MJButtonDirective], changeDetection: ChangeDetectionStrategy.OnPush, template: `
|
|
158
|
+
<section class="ws-card" [attr.aria-label]="AriaLabel">
|
|
159
|
+
<header class="ws-card__head">
|
|
160
|
+
<mj-workspace-tab-strip
|
|
161
|
+
[Tabs]="Tabs"
|
|
162
|
+
[ActiveId]="ActiveId"
|
|
163
|
+
[NewTabLabel]="NewTabLabel"
|
|
164
|
+
[AllowReorder]="AllowReorder"
|
|
165
|
+
(TabSelected)="TabSelected.emit($event)"
|
|
166
|
+
(TabClosed)="TabClosed.emit($event)"
|
|
167
|
+
(NewTabRequested)="NewTabRequested.emit()"
|
|
168
|
+
(TabReordered)="TabReordered.emit($event)">
|
|
169
|
+
</mj-workspace-tab-strip>
|
|
170
|
+
<div class="ws-card__headmeta">
|
|
171
|
+
<ng-content select="[workspaceHeader]"></ng-content>
|
|
172
|
+
</div>
|
|
173
|
+
</header>
|
|
174
|
+
<div class="ws-card__body">
|
|
175
|
+
<ng-content></ng-content>
|
|
176
|
+
</div>
|
|
177
|
+
<!-- Standardized action bar: the SAME three verbs on every workspace — a primary
|
|
178
|
+
confirm/submit whose LABEL is the only thing that varies per use case, a neutral
|
|
179
|
+
save-as-draft, and a neutral discard — so no workshop hand-rolls its own footer. Pinned
|
|
180
|
+
below the scrolling body. Opt-in via [ShowFooter]; the [workspaceFooterNote] slot carries
|
|
181
|
+
any per-workshop caption beside the buttons. Confirm sits LEFT of the neutral verbs per
|
|
182
|
+
the MJ dialog-button convention. -->
|
|
183
|
+
@if (ShowFooter) {
|
|
184
|
+
<footer class="ws-card__foot">
|
|
185
|
+
<button type="button" mjButton variant="primary"
|
|
186
|
+
[disabled]="ConfirmDisabled || ConfirmBusy"
|
|
187
|
+
[title]="ConfirmTitle || ConfirmLabel"
|
|
188
|
+
(click)="Confirm.emit()">
|
|
189
|
+
@if (ConfirmBusy) {
|
|
190
|
+
<i class="fa-solid fa-spinner fa-spin" aria-hidden="true"></i> {{ ConfirmBusyLabel }}
|
|
191
|
+
} @else {
|
|
192
|
+
@if (ConfirmIcon) { <i [class]="ConfirmIcon" aria-hidden="true"></i> } {{ ConfirmLabel }}
|
|
193
|
+
}
|
|
194
|
+
</button>
|
|
195
|
+
@if (ShowDraft) {
|
|
196
|
+
<button type="button" mjButton variant="outline" [disabled]="DraftDisabled" (click)="SaveDraft.emit()">
|
|
197
|
+
@if (DraftIcon) { <i [class]="DraftIcon" aria-hidden="true"></i> } {{ DraftLabel }}
|
|
198
|
+
</button>
|
|
199
|
+
}
|
|
200
|
+
<button type="button" mjButton variant="flat" (click)="Discard.emit()">{{ DiscardLabel }}</button>
|
|
201
|
+
<span class="ws-card__footnote"><ng-content select="[workspaceFooterNote]"></ng-content></span>
|
|
202
|
+
</footer>
|
|
203
|
+
}
|
|
204
|
+
</section>
|
|
205
|
+
`, styles: ["/* The reusable workspace card frame \u2014 design tokens only. The height chain (host fills parent \u2192 card\n fills host \u2192 body is the flex:1 scroller) is load-bearing: it's what lets the BODY scroll in place\n while the tab strip + identity band stay pinned. */\n\n:host {\n display: flex;\n flex-direction: column;\n height: 100%;\n min-height: 0;\n}\n\n/* One object against the page: --mj-bg-surface, rounded, with the body's scroll contained inside. */\n.ws-card {\n flex: 1;\n min-height: 0;\n display: flex;\n flex-direction: column;\n background: var(--mj-bg-surface);\n border: 1px solid var(--mj-border-default);\n border-radius: 12px;\n overflow: hidden;\n /* SIZING CONTAINER (design rule, Marcelo 2026-07-21): the card is a query container, so content\n inside sizes with CONTAINER units (cqh/cqw/cqi) \u2014 relative to THIS card (a window/pane), never the\n viewport (vh/vw). vh would size to the whole screen regardless of the pane the card sits in. The\n card's own size comes from the flex chain (parent-driven), so size containment is safe here. */\n container-type: size;\n container-name: workspace;\n}\n\n.ws-card__head {\n flex: none;\n}\n\n/* Identity band below the tab row (never in line with the tabs) \u2014 the slot the workshop projects into. */\n.ws-card__headmeta {\n display: flex;\n align-items: center;\n gap: 10px;\n flex-wrap: wrap;\n padding: 10px 14px;\n}\n/* Collapse the band entirely when the workshop projects nothing into it (no empty padded strip). */\n.ws-card__headmeta:empty {\n display: none;\n}\n\n/* The single scrollable body. Standard gutters + a stacked-column default (the compact-workspace\n layout); a workshop's projected content flows here. */\n.ws-card__body {\n flex: 1;\n min-height: 0;\n overflow-y: auto;\n padding: 0 14px 14px;\n display: flex;\n flex-direction: column;\n gap: 10px;\n}\n\n/* The standardized action bar \u2014 pinned below the scrolling body, so the verbs stay reachable without\n scrolling to the end of a long form. NO top border (Marcelo 2026-07-21): the buttons should read as\n part of the sheet, not a boxed-off strip \u2014 the card's own frame is the only border we want. */\n.ws-card__foot {\n flex: none;\n display: flex;\n align-items: center;\n gap: 8px;\n flex-wrap: wrap;\n padding: 4px 14px 12px;\n}\n/* Per-workshop caption beside the verbs (projected). Collapses when nothing is slotted. */\n.ws-card__footnote {\n margin-left: auto;\n max-width: 46ch;\n font-size: 12px;\n color: var(--mj-text-muted);\n}\n.ws-card__footnote:empty {\n display: none;\n}\n"] }]
|
|
206
|
+
}], null, { Tabs: [{
|
|
207
|
+
type: Input
|
|
208
|
+
}], ActiveId: [{
|
|
209
|
+
type: Input
|
|
210
|
+
}], NewTabLabel: [{
|
|
211
|
+
type: Input
|
|
212
|
+
}], AriaLabel: [{
|
|
213
|
+
type: Input
|
|
214
|
+
}], AllowReorder: [{
|
|
215
|
+
type: Input
|
|
216
|
+
}], ShowFooter: [{
|
|
217
|
+
type: Input
|
|
218
|
+
}], ConfirmLabel: [{
|
|
219
|
+
type: Input
|
|
220
|
+
}], ConfirmIcon: [{
|
|
221
|
+
type: Input
|
|
222
|
+
}], ConfirmDisabled: [{
|
|
223
|
+
type: Input
|
|
224
|
+
}], ConfirmTitle: [{
|
|
225
|
+
type: Input
|
|
226
|
+
}], ConfirmBusy: [{
|
|
227
|
+
type: Input
|
|
228
|
+
}], ConfirmBusyLabel: [{
|
|
229
|
+
type: Input
|
|
230
|
+
}], ShowDraft: [{
|
|
231
|
+
type: Input
|
|
232
|
+
}], DraftLabel: [{
|
|
233
|
+
type: Input
|
|
234
|
+
}], DraftIcon: [{
|
|
235
|
+
type: Input
|
|
236
|
+
}], DraftDisabled: [{
|
|
237
|
+
type: Input
|
|
238
|
+
}], DiscardLabel: [{
|
|
239
|
+
type: Input
|
|
240
|
+
}], TabSelected: [{
|
|
241
|
+
type: Output
|
|
242
|
+
}], TabClosed: [{
|
|
243
|
+
type: Output
|
|
244
|
+
}], NewTabRequested: [{
|
|
245
|
+
type: Output
|
|
246
|
+
}], TabReordered: [{
|
|
247
|
+
type: Output
|
|
248
|
+
}], Confirm: [{
|
|
249
|
+
type: Output
|
|
250
|
+
}], SaveDraft: [{
|
|
251
|
+
type: Output
|
|
252
|
+
}], Discard: [{
|
|
253
|
+
type: Output
|
|
254
|
+
}] }); })();
|
|
255
|
+
(() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassDebugInfo(MJWorkspaceCardComponent, { className: "MJWorkspaceCardComponent", filePath: "lib/workspace-tabs/workspace-card.component.ts", lineNumber: 85 }); })();
|
|
256
|
+
//# sourceMappingURL=workspace-card.component.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"workspace-card.component.js","sourceRoot":"","sources":["../../../src/lib/workspace-tabs/workspace-card.component.ts"],"names":[],"mappings":"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","sourcesContent":["import { Component, ChangeDetectionStrategy, Input, Output, EventEmitter } from '@angular/core';\nimport { CommonModule } from '@angular/common';\nimport { MJWorkspaceTab } from './workspace-tabs.types';\nimport { MJWorkspaceTabStripComponent, MJTabReorder } from './workspace-tab-strip.component';\nimport { MJButtonDirective } from '../button/button.directive';\n\n/**\n * `<mj-workspace-card>` — the reusable frame every \"workspace\" screen shares (a screen where the\n * user builds several drafts in parallel, each in a tab). A THIN, slotted wrapper: it owns ONLY the\n * invariant chrome —\n * • the card surface (border + rounded corners + contained scroll),\n * • the tab-strip row (delegated to `<mj-workspace-tab-strip>`, which carries all tab behavior),\n * • an identity band beside the tabs, and\n * • a single scrollable body —\n * and PROJECTS everything that varies:\n * • `[workspaceHeader]` — the per-workshop identity band content (e.g. number/status/currency badges);\n * • the default slot — the workshop's form/body;\n * • `[workspaceFooterNote]` — an optional caption beside the standardized footer verbs.\n *\n * The card deliberately bakes in NO workshop-specific header; those differ per workshop and are\n * projected. Height chain: `:host` fills its parent, the card fills the host, the body is the\n * flex:1 scroller — so the BODY scrolls in place while the strip + identity band stay pinned.\n *\n * SIZING RULE for content inside the card: the card is a query container (`container-type: size`),\n * so content sizes with CONTAINER units (`cqh`/`cqw`/`cqi`) — relative to the card as a pane —\n * never viewport units (`vh`/`vw`), which break the moment the card sits in a split or a smaller\n * window.\n */\n@Component({\n standalone: true,\n selector: 'mj-workspace-card',\n imports: [CommonModule, MJWorkspaceTabStripComponent, MJButtonDirective],\n changeDetection: ChangeDetectionStrategy.OnPush,\n template: `\n <section class=\"ws-card\" [attr.aria-label]=\"AriaLabel\">\n <header class=\"ws-card__head\">\n <mj-workspace-tab-strip\n [Tabs]=\"Tabs\"\n [ActiveId]=\"ActiveId\"\n [NewTabLabel]=\"NewTabLabel\"\n [AllowReorder]=\"AllowReorder\"\n (TabSelected)=\"TabSelected.emit($event)\"\n (TabClosed)=\"TabClosed.emit($event)\"\n (NewTabRequested)=\"NewTabRequested.emit()\"\n (TabReordered)=\"TabReordered.emit($event)\">\n </mj-workspace-tab-strip>\n <div class=\"ws-card__headmeta\">\n <ng-content select=\"[workspaceHeader]\"></ng-content>\n </div>\n </header>\n <div class=\"ws-card__body\">\n <ng-content></ng-content>\n </div>\n <!-- Standardized action bar: the SAME three verbs on every workspace — a primary\n confirm/submit whose LABEL is the only thing that varies per use case, a neutral\n save-as-draft, and a neutral discard — so no workshop hand-rolls its own footer. Pinned\n below the scrolling body. Opt-in via [ShowFooter]; the [workspaceFooterNote] slot carries\n any per-workshop caption beside the buttons. Confirm sits LEFT of the neutral verbs per\n the MJ dialog-button convention. -->\n @if (ShowFooter) {\n <footer class=\"ws-card__foot\">\n <button type=\"button\" mjButton variant=\"primary\"\n [disabled]=\"ConfirmDisabled || ConfirmBusy\"\n [title]=\"ConfirmTitle || ConfirmLabel\"\n (click)=\"Confirm.emit()\">\n @if (ConfirmBusy) {\n <i class=\"fa-solid fa-spinner fa-spin\" aria-hidden=\"true\"></i> {{ ConfirmBusyLabel }}\n } @else {\n @if (ConfirmIcon) { <i [class]=\"ConfirmIcon\" aria-hidden=\"true\"></i> } {{ ConfirmLabel }}\n }\n </button>\n @if (ShowDraft) {\n <button type=\"button\" mjButton variant=\"outline\" [disabled]=\"DraftDisabled\" (click)=\"SaveDraft.emit()\">\n @if (DraftIcon) { <i [class]=\"DraftIcon\" aria-hidden=\"true\"></i> } {{ DraftLabel }}\n </button>\n }\n <button type=\"button\" mjButton variant=\"flat\" (click)=\"Discard.emit()\">{{ DiscardLabel }}</button>\n <span class=\"ws-card__footnote\"><ng-content select=\"[workspaceFooterNote]\"></ng-content></span>\n </footer>\n }\n </section>\n `,\n styleUrls: ['./workspace-card.component.css'],\n})\nexport class MJWorkspaceCardComponent {\n @Input() Tabs: MJWorkspaceTab[] = [];\n @Input() ActiveId: string | null = null;\n @Input() NewTabLabel = 'New';\n @Input() AriaLabel = 'Workspace';\n /** Passed through to the strip — see `MJWorkspaceTabStripComponent.AllowReorder`. */\n @Input() AllowReorder = true;\n\n // ── Standardized footer (opt-in) ────────────────────────────────────────────\n /** Render the standardized action bar (confirm / save-as-draft / discard) below the body. */\n @Input() ShowFooter = false;\n /** The primary verb's label — the ONE thing that changes per workshop (\"Create entry\" / \"Build batch\"). */\n @Input() ConfirmLabel = 'Confirm';\n /** Optional Font Awesome class for the confirm button (e.g. 'fa-solid fa-check'). */\n @Input() ConfirmIcon: string | null = null;\n @Input() ConfirmDisabled = false;\n /** Tooltip / blocked-reason on the confirm button; falls back to ConfirmLabel. */\n @Input() ConfirmTitle: string | null = null;\n /** Show the spinner + busy label on the confirm button while an async confirm runs. */\n @Input() ConfirmBusy = false;\n @Input() ConfirmBusyLabel = 'Working…';\n /** Whether the save-as-draft button appears (some workshops have no draft concept). */\n @Input() ShowDraft = true;\n @Input() DraftLabel = 'Keep as draft';\n @Input() DraftIcon: string | null = 'fa-regular fa-floppy-disk';\n @Input() DraftDisabled = false;\n @Input() DiscardLabel = 'Discard';\n\n @Output() TabSelected = new EventEmitter<string>();\n @Output() TabClosed = new EventEmitter<string>();\n @Output() NewTabRequested = new EventEmitter<void>();\n @Output() TabReordered = new EventEmitter<MJTabReorder>();\n /** The primary verb (create / build / …). */\n @Output() Confirm = new EventEmitter<void>();\n @Output() SaveDraft = new EventEmitter<void>();\n @Output() Discard = new EventEmitter<void>();\n}\n"]}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { MJWorkspaceTab, MJWorkspaceTabState } from './workspace-tabs.types';
|
|
2
|
+
/**
|
|
3
|
+
* The pure, framework-free state machine behind `<mj-workspace-tab-strip>`.
|
|
4
|
+
*
|
|
5
|
+
* Deliberately extracted from the component so the tab semantics are exhaustively unit-testable in
|
|
6
|
+
* isolation (see `__tests__/workspace-tab-store.test.ts`); the Angular shell around it stays thin.
|
|
7
|
+
* No Angular, no DB, no app types — synchronous and total.
|
|
8
|
+
*
|
|
9
|
+
* v1 semantics: session-scoped only. Nothing here touches persistence.
|
|
10
|
+
*/
|
|
11
|
+
export declare class MJWorkspaceTabStore<TState = unknown> {
|
|
12
|
+
private tabs;
|
|
13
|
+
private activeId;
|
|
14
|
+
/** Current tabs, in strip order. Returns a copy — callers must not mutate internal state. */
|
|
15
|
+
get Tabs(): MJWorkspaceTab<TState>[];
|
|
16
|
+
get ActiveId(): string | null;
|
|
17
|
+
get ActiveTab(): MJWorkspaceTab<TState> | null;
|
|
18
|
+
get Count(): number;
|
|
19
|
+
/** True when any tab holds unsaved edits — the "you'll lose work" signal for the host. */
|
|
20
|
+
get HasDirtyTabs(): boolean;
|
|
21
|
+
/**
|
|
22
|
+
* Add a tab and make it active. An existing tab with the same Id is NOT duplicated — it is
|
|
23
|
+
* activated instead (re-opening the same draft focuses it, which is what a user means).
|
|
24
|
+
*/
|
|
25
|
+
Open(tab: MJWorkspaceTab<TState>): MJWorkspaceTab<TState>;
|
|
26
|
+
Activate(id: string): boolean;
|
|
27
|
+
/**
|
|
28
|
+
* Close a tab. When the closed tab was active, activation falls to its NEIGHBOUR — the tab to its
|
|
29
|
+
* right, else the one to its left, else null. (Closing the tab you're looking at should land you
|
|
30
|
+
* somewhere sensible, not on nothing.)
|
|
31
|
+
*/
|
|
32
|
+
Close(id: string): boolean;
|
|
33
|
+
/** Replace a tab's host-owned payload. Marks the tab dirty unless told otherwise. */
|
|
34
|
+
UpdateState(id: string, state: TState, markDirty?: boolean): boolean;
|
|
35
|
+
/** Move a tab through its lifecycle. Clears the rejection reason on any non-rejected state. */
|
|
36
|
+
SetStatus(id: string, status: MJWorkspaceTabState, rejectionReason?: string | null): boolean;
|
|
37
|
+
/** Mark a tab saved/clean without touching its payload. */
|
|
38
|
+
MarkClean(id: string): boolean;
|
|
39
|
+
/**
|
|
40
|
+
* Move a tab from one position to another (drag-reorder). Active tab + payloads are untouched — only
|
|
41
|
+
* strip ORDER changes. Out-of-range or no-op indices return false and change nothing.
|
|
42
|
+
*/
|
|
43
|
+
Reorder(fromIndex: number, toIndex: number): boolean;
|
|
44
|
+
/** Drop every tab (session end / explicit discard-all). */
|
|
45
|
+
Clear(): void;
|
|
46
|
+
}
|
|
47
|
+
//# sourceMappingURL=workspace-tab-store.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"workspace-tab-store.d.ts","sourceRoot":"","sources":["../../../src/lib/workspace-tabs/workspace-tab-store.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,mBAAmB,EAAE,MAAM,wBAAwB,CAAC;AAE7E;;;;;;;;GAQG;AACH,qBAAa,mBAAmB,CAAC,MAAM,GAAG,OAAO;IAC/C,OAAO,CAAC,IAAI,CAAgC;IAC5C,OAAO,CAAC,QAAQ,CAAuB;IAEvC,6FAA6F;IAC7F,IAAW,IAAI,IAAI,cAAc,CAAC,MAAM,CAAC,EAAE,CAE1C;IAED,IAAW,QAAQ,IAAI,MAAM,GAAG,IAAI,CAEnC;IAED,IAAW,SAAS,IAAI,cAAc,CAAC,MAAM,CAAC,GAAG,IAAI,CAEpD;IAED,IAAW,KAAK,IAAI,MAAM,CAEzB;IAED,0FAA0F;IAC1F,IAAW,YAAY,IAAI,OAAO,CAEjC;IAED;;;OAGG;IACI,IAAI,CAAC,GAAG,EAAE,cAAc,CAAC,MAAM,CAAC,GAAG,cAAc,CAAC,MAAM,CAAC;IAWzD,QAAQ,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO;IAMpC;;;;OAIG;IACI,KAAK,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO;IAcjC,qFAAqF;IAC9E,WAAW,CAAC,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,SAAS,UAAO,GAAG,OAAO;IAQxE,+FAA+F;IACxF,SAAS,CAAC,EAAE,EAAE,MAAM,EAAE,MAAM,EAAE,mBAAmB,EAAE,eAAe,GAAE,MAAM,GAAG,IAAW,GAAG,OAAO;IAQzG,2DAA2D;IACpD,SAAS,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO;IAOrC;;;OAGG;IACI,OAAO,CAAC,SAAS,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO;IAQ3D,2DAA2D;IACpD,KAAK,IAAI,IAAI;CAIrB"}
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The pure, framework-free state machine behind `<mj-workspace-tab-strip>`.
|
|
3
|
+
*
|
|
4
|
+
* Deliberately extracted from the component so the tab semantics are exhaustively unit-testable in
|
|
5
|
+
* isolation (see `__tests__/workspace-tab-store.test.ts`); the Angular shell around it stays thin.
|
|
6
|
+
* No Angular, no DB, no app types — synchronous and total.
|
|
7
|
+
*
|
|
8
|
+
* v1 semantics: session-scoped only. Nothing here touches persistence.
|
|
9
|
+
*/
|
|
10
|
+
export class MJWorkspaceTabStore {
|
|
11
|
+
tabs = [];
|
|
12
|
+
activeId = null;
|
|
13
|
+
/** Current tabs, in strip order. Returns a copy — callers must not mutate internal state. */
|
|
14
|
+
get Tabs() {
|
|
15
|
+
return [...this.tabs];
|
|
16
|
+
}
|
|
17
|
+
get ActiveId() {
|
|
18
|
+
return this.activeId;
|
|
19
|
+
}
|
|
20
|
+
get ActiveTab() {
|
|
21
|
+
return this.tabs.find((t) => t.Id === this.activeId) ?? null;
|
|
22
|
+
}
|
|
23
|
+
get Count() {
|
|
24
|
+
return this.tabs.length;
|
|
25
|
+
}
|
|
26
|
+
/** True when any tab holds unsaved edits — the "you'll lose work" signal for the host. */
|
|
27
|
+
get HasDirtyTabs() {
|
|
28
|
+
return this.tabs.some((t) => t.Dirty === true);
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Add a tab and make it active. An existing tab with the same Id is NOT duplicated — it is
|
|
32
|
+
* activated instead (re-opening the same draft focuses it, which is what a user means).
|
|
33
|
+
*/
|
|
34
|
+
Open(tab) {
|
|
35
|
+
const existing = this.tabs.find((t) => t.Id === tab.Id);
|
|
36
|
+
if (existing) {
|
|
37
|
+
this.activeId = existing.Id;
|
|
38
|
+
return existing;
|
|
39
|
+
}
|
|
40
|
+
this.tabs.push(tab);
|
|
41
|
+
this.activeId = tab.Id;
|
|
42
|
+
return tab;
|
|
43
|
+
}
|
|
44
|
+
Activate(id) {
|
|
45
|
+
if (!this.tabs.some((t) => t.Id === id))
|
|
46
|
+
return false;
|
|
47
|
+
this.activeId = id;
|
|
48
|
+
return true;
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Close a tab. When the closed tab was active, activation falls to its NEIGHBOUR — the tab to its
|
|
52
|
+
* right, else the one to its left, else null. (Closing the tab you're looking at should land you
|
|
53
|
+
* somewhere sensible, not on nothing.)
|
|
54
|
+
*/
|
|
55
|
+
Close(id) {
|
|
56
|
+
const index = this.tabs.findIndex((t) => t.Id === id);
|
|
57
|
+
if (index === -1)
|
|
58
|
+
return false;
|
|
59
|
+
const wasActive = this.activeId === id;
|
|
60
|
+
this.tabs.splice(index, 1);
|
|
61
|
+
if (wasActive) {
|
|
62
|
+
const neighbour = this.tabs[index] ?? this.tabs[index - 1] ?? null;
|
|
63
|
+
this.activeId = neighbour ? neighbour.Id : null;
|
|
64
|
+
}
|
|
65
|
+
return true;
|
|
66
|
+
}
|
|
67
|
+
/** Replace a tab's host-owned payload. Marks the tab dirty unless told otherwise. */
|
|
68
|
+
UpdateState(id, state, markDirty = true) {
|
|
69
|
+
const tab = this.tabs.find((t) => t.Id === id);
|
|
70
|
+
if (!tab)
|
|
71
|
+
return false;
|
|
72
|
+
tab.State = state;
|
|
73
|
+
if (markDirty)
|
|
74
|
+
tab.Dirty = true;
|
|
75
|
+
return true;
|
|
76
|
+
}
|
|
77
|
+
/** Move a tab through its lifecycle. Clears the rejection reason on any non-rejected state. */
|
|
78
|
+
SetStatus(id, status, rejectionReason = null) {
|
|
79
|
+
const tab = this.tabs.find((t) => t.Id === id);
|
|
80
|
+
if (!tab)
|
|
81
|
+
return false;
|
|
82
|
+
tab.Status = status;
|
|
83
|
+
tab.RejectionReason = status === 'rejected' ? rejectionReason : null;
|
|
84
|
+
return true;
|
|
85
|
+
}
|
|
86
|
+
/** Mark a tab saved/clean without touching its payload. */
|
|
87
|
+
MarkClean(id) {
|
|
88
|
+
const tab = this.tabs.find((t) => t.Id === id);
|
|
89
|
+
if (!tab)
|
|
90
|
+
return false;
|
|
91
|
+
tab.Dirty = false;
|
|
92
|
+
return true;
|
|
93
|
+
}
|
|
94
|
+
/**
|
|
95
|
+
* Move a tab from one position to another (drag-reorder). Active tab + payloads are untouched — only
|
|
96
|
+
* strip ORDER changes. Out-of-range or no-op indices return false and change nothing.
|
|
97
|
+
*/
|
|
98
|
+
Reorder(fromIndex, toIndex) {
|
|
99
|
+
const n = this.tabs.length;
|
|
100
|
+
if (fromIndex === toIndex || fromIndex < 0 || fromIndex >= n || toIndex < 0 || toIndex >= n)
|
|
101
|
+
return false;
|
|
102
|
+
const [moved] = this.tabs.splice(fromIndex, 1);
|
|
103
|
+
this.tabs.splice(toIndex, 0, moved);
|
|
104
|
+
return true;
|
|
105
|
+
}
|
|
106
|
+
/** Drop every tab (session end / explicit discard-all). */
|
|
107
|
+
Clear() {
|
|
108
|
+
this.tabs = [];
|
|
109
|
+
this.activeId = null;
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
//# sourceMappingURL=workspace-tab-store.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"workspace-tab-store.js","sourceRoot":"","sources":["../../../src/lib/workspace-tabs/workspace-tab-store.ts"],"names":[],"mappings":"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","sourcesContent":["import { MJWorkspaceTab, MJWorkspaceTabState } from './workspace-tabs.types';\n\n/**\n * The pure, framework-free state machine behind `<mj-workspace-tab-strip>`.\n *\n * Deliberately extracted from the component so the tab semantics are exhaustively unit-testable in\n * isolation (see `__tests__/workspace-tab-store.test.ts`); the Angular shell around it stays thin.\n * No Angular, no DB, no app types — synchronous and total.\n *\n * v1 semantics: session-scoped only. Nothing here touches persistence.\n */\nexport class MJWorkspaceTabStore<TState = unknown> {\n private tabs: MJWorkspaceTab<TState>[] = [];\n private activeId: string | null = null;\n\n /** Current tabs, in strip order. Returns a copy — callers must not mutate internal state. */\n public get Tabs(): MJWorkspaceTab<TState>[] {\n return [...this.tabs];\n }\n\n public get ActiveId(): string | null {\n return this.activeId;\n }\n\n public get ActiveTab(): MJWorkspaceTab<TState> | null {\n return this.tabs.find((t) => t.Id === this.activeId) ?? null;\n }\n\n public get Count(): number {\n return this.tabs.length;\n }\n\n /** True when any tab holds unsaved edits — the \"you'll lose work\" signal for the host. */\n public get HasDirtyTabs(): boolean {\n return this.tabs.some((t) => t.Dirty === true);\n }\n\n /**\n * Add a tab and make it active. An existing tab with the same Id is NOT duplicated — it is\n * activated instead (re-opening the same draft focuses it, which is what a user means).\n */\n public Open(tab: MJWorkspaceTab<TState>): MJWorkspaceTab<TState> {\n const existing = this.tabs.find((t) => t.Id === tab.Id);\n if (existing) {\n this.activeId = existing.Id;\n return existing;\n }\n this.tabs.push(tab);\n this.activeId = tab.Id;\n return tab;\n }\n\n public Activate(id: string): boolean {\n if (!this.tabs.some((t) => t.Id === id)) return false;\n this.activeId = id;\n return true;\n }\n\n /**\n * Close a tab. When the closed tab was active, activation falls to its NEIGHBOUR — the tab to its\n * right, else the one to its left, else null. (Closing the tab you're looking at should land you\n * somewhere sensible, not on nothing.)\n */\n public Close(id: string): boolean {\n const index = this.tabs.findIndex((t) => t.Id === id);\n if (index === -1) return false;\n\n const wasActive = this.activeId === id;\n this.tabs.splice(index, 1);\n\n if (wasActive) {\n const neighbour = this.tabs[index] ?? this.tabs[index - 1] ?? null;\n this.activeId = neighbour ? neighbour.Id : null;\n }\n return true;\n }\n\n /** Replace a tab's host-owned payload. Marks the tab dirty unless told otherwise. */\n public UpdateState(id: string, state: TState, markDirty = true): boolean {\n const tab = this.tabs.find((t) => t.Id === id);\n if (!tab) return false;\n tab.State = state;\n if (markDirty) tab.Dirty = true;\n return true;\n }\n\n /** Move a tab through its lifecycle. Clears the rejection reason on any non-rejected state. */\n public SetStatus(id: string, status: MJWorkspaceTabState, rejectionReason: string | null = null): boolean {\n const tab = this.tabs.find((t) => t.Id === id);\n if (!tab) return false;\n tab.Status = status;\n tab.RejectionReason = status === 'rejected' ? rejectionReason : null;\n return true;\n }\n\n /** Mark a tab saved/clean without touching its payload. */\n public MarkClean(id: string): boolean {\n const tab = this.tabs.find((t) => t.Id === id);\n if (!tab) return false;\n tab.Dirty = false;\n return true;\n }\n\n /**\n * Move a tab from one position to another (drag-reorder). Active tab + payloads are untouched — only\n * strip ORDER changes. Out-of-range or no-op indices return false and change nothing.\n */\n public Reorder(fromIndex: number, toIndex: number): boolean {\n const n = this.tabs.length;\n if (fromIndex === toIndex || fromIndex < 0 || fromIndex >= n || toIndex < 0 || toIndex >= n) return false;\n const [moved] = this.tabs.splice(fromIndex, 1);\n this.tabs.splice(toIndex, 0, moved);\n return true;\n }\n\n /** Drop every tab (session end / explicit discard-all). */\n public Clear(): void {\n this.tabs = [];\n this.activeId = null;\n }\n}\n"]}
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import { AfterViewInit, EventEmitter } from '@angular/core';
|
|
2
|
+
import { CdkDragDrop } from '@angular/cdk/drag-drop';
|
|
3
|
+
import { MJWorkspaceTab } from './workspace-tabs.types';
|
|
4
|
+
import { MJTabListRequest } from '../tabs/tab-list.directive';
|
|
5
|
+
import * as i0 from "@angular/core";
|
|
6
|
+
/** A drag-reorder request from the strip: move the tab at `previousIndex` to `currentIndex`. */
|
|
7
|
+
export interface MJTabReorder {
|
|
8
|
+
previousIndex: number;
|
|
9
|
+
currentIndex: number;
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* `<mj-workspace-tab-strip>` — browser-style draft tabs, presentation over `MJWorkspaceTabStore`.
|
|
13
|
+
*
|
|
14
|
+
* Dumb by design: it renders the tabs it is handed and emits intent. The store holds the state
|
|
15
|
+
* machine; the host owns what a tab's payload means. That split keeps it app-agnostic.
|
|
16
|
+
*
|
|
17
|
+
* **Browser-tab behavior lives HERE** so every consumer inherits it:
|
|
18
|
+
* - pinned new-tab button; the tab LIST scrolls horizontally *behind* it on overflow;
|
|
19
|
+
* - tabs size to their text up to `--mj-tab-max`, then ellipsize; the inline unsaved-dot pushes the label;
|
|
20
|
+
* - the tab body keeps the default arrow cursor (like browser tabs);
|
|
21
|
+
* - the full label shows via the reusable `mjTip` tooltip when a tab is truncated (delayed, non-interactive);
|
|
22
|
+
* - drag-reorder via CDK, emitted as intent for the host to apply to its store.
|
|
23
|
+
*
|
|
24
|
+
* **Appearance and keyboard behaviour are NOT owned here.** The look comes from the global
|
|
25
|
+
* `.mj-tabs*` chrome in `tabs.scss`, and the ARIA tabs keyboard contract from `mjTabList` — both
|
|
26
|
+
* shared with `mj-tabstrip`, so the two strips cannot drift apart visually or behaviourally even
|
|
27
|
+
* though they keep different state models.
|
|
28
|
+
*/
|
|
29
|
+
export declare class MJWorkspaceTabStripComponent implements AfterViewInit {
|
|
30
|
+
private readonly elementRef;
|
|
31
|
+
ngAfterViewInit(): void;
|
|
32
|
+
Tabs: MJWorkspaceTab[];
|
|
33
|
+
ActiveId: string | null;
|
|
34
|
+
ShowNewTab: boolean;
|
|
35
|
+
NewTabLabel: string;
|
|
36
|
+
/**
|
|
37
|
+
* Whether tabs can be drag-reordered at all. The escape hatch for hosts where even long-press
|
|
38
|
+
* dragging is wrong (a kiosk surface, a read-only review screen) — reorder simply isn't offered
|
|
39
|
+
* and every touch gesture belongs to scrolling.
|
|
40
|
+
*/
|
|
41
|
+
AllowReorder: boolean;
|
|
42
|
+
/**
|
|
43
|
+
* Per-pointer-type drag threshold, resolving the touch conflict between REORDER and SCROLL: an
|
|
44
|
+
* overflowing tab list scrolls by horizontal swipe, and a swipe starts on a tab — with an
|
|
45
|
+
* immediate drag threshold, CDK claims that touch and the list becomes unscrollable on phones.
|
|
46
|
+
* A hold-to-drag delay is the platform idiom for exactly this (iOS home screen, mobile browser
|
|
47
|
+
* tabs): a moving finger within the delay window is a SCROLL and CDK lets it through natively; a
|
|
48
|
+
* still finger past it is a DRAG. Mouse stays immediate — pointer users scroll with the wheel or
|
|
49
|
+
* scrollbar, so there is no gesture to disambiguate and a delay would just feel laggy.
|
|
50
|
+
*/
|
|
51
|
+
readonly DragStartDelay: {
|
|
52
|
+
touch: number;
|
|
53
|
+
mouse: number;
|
|
54
|
+
};
|
|
55
|
+
TabSelected: EventEmitter<string>;
|
|
56
|
+
TabClosed: EventEmitter<string>;
|
|
57
|
+
NewTabRequested: EventEmitter<void>;
|
|
58
|
+
TabReordered: EventEmitter<MJTabReorder>;
|
|
59
|
+
/** CDK drop — emit the reorder intent; the host applies it to the store (the strip stays dumb). */
|
|
60
|
+
onDrop(event: CdkDragDrop<MJWorkspaceTab[]>): void;
|
|
61
|
+
/**
|
|
62
|
+
* The tab's accessible name. The dirty dot and the rejected colour/icon are the only signals for
|
|
63
|
+
* those states, and neither survives being read aloud — so they are spelled out here instead.
|
|
64
|
+
*/
|
|
65
|
+
tabAccessibleName(tab: MJWorkspaceTab): string;
|
|
66
|
+
/** Keyboard arrow/Home/End/Enter from `mjTabList` — map the position back onto our id. */
|
|
67
|
+
onActivateRequested(request: MJTabListRequest): void;
|
|
68
|
+
/** Delete/Backspace on a focused tab — the APG close gesture for a closeable tab. */
|
|
69
|
+
onCloseRequested(request: MJTabListRequest): void;
|
|
70
|
+
/** The close button sits inside the tab, so its click would otherwise also select the tab. */
|
|
71
|
+
onCloseClick(event: MouseEvent, id: string): void;
|
|
72
|
+
static ɵfac: i0.ɵɵFactoryDeclaration<MJWorkspaceTabStripComponent, never>;
|
|
73
|
+
static ɵcmp: i0.ɵɵComponentDeclaration<MJWorkspaceTabStripComponent, "mj-workspace-tab-strip", never, { "Tabs": { "alias": "Tabs"; "required": false; }; "ActiveId": { "alias": "ActiveId"; "required": false; }; "ShowNewTab": { "alias": "ShowNewTab"; "required": false; }; "NewTabLabel": { "alias": "NewTabLabel"; "required": false; }; "AllowReorder": { "alias": "AllowReorder"; "required": false; }; }, { "TabSelected": "TabSelected"; "TabClosed": "TabClosed"; "NewTabRequested": "NewTabRequested"; "TabReordered": "TabReordered"; }, never, never, true, never>;
|
|
74
|
+
}
|
|
75
|
+
//# sourceMappingURL=workspace-tab-strip.component.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"workspace-tab-strip.component.d.ts","sourceRoot":"","sources":["../../../src/lib/workspace-tabs/workspace-tab-strip.component.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAkD,YAAY,EAAyB,MAAM,eAAe,CAAC;AAEnI,OAAO,EAAwB,WAAW,EAAE,MAAM,wBAAwB,CAAC;AAC3E,OAAO,EAAE,cAAc,EAAE,MAAM,wBAAwB,CAAC;AAExD,OAAO,EAAsB,gBAAgB,EAAE,MAAM,4BAA4B,CAAC;;AAGlF,gGAAgG;AAChG,MAAM,WAAW,YAAY;IAC3B,aAAa,EAAE,MAAM,CAAC;IACtB,YAAY,EAAE,MAAM,CAAC;CACtB;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,qBAoEa,4BAA6B,YAAW,aAAa;IAChE,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAsB;IAEjD,eAAe,IAAI,IAAI;IAId,IAAI,EAAE,cAAc,EAAE,CAAM;IAC5B,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAQ;IAC/B,UAAU,UAAQ;IAClB,WAAW,SAAS;IAE7B;;;;OAIG;IACM,YAAY,UAAQ;IAE7B;;;;;;;;OAQG;IACH,SAAgB,cAAc;;;MAA4B;IAEhD,WAAW,uBAA8B;IACzC,SAAS,uBAA8B;IACvC,eAAe,qBAA4B;IAC3C,YAAY,6BAAoC;IAE1D,mGAAmG;IAC5F,MAAM,CAAC,KAAK,EAAE,WAAW,CAAC,cAAc,EAAE,CAAC,GAAG,IAAI;IAMzD;;;OAGG;IACI,iBAAiB,CAAC,GAAG,EAAE,cAAc,GAAG,MAAM;IAOrD,0FAA0F;IACnF,mBAAmB,CAAC,OAAO,EAAE,gBAAgB,GAAG,IAAI;IAO3D,qFAAqF;IAC9E,gBAAgB,CAAC,OAAO,EAAE,gBAAgB,GAAG,IAAI;IAOxD,8FAA8F;IACvF,YAAY,CAAC,KAAK,EAAE,UAAU,EAAE,EAAE,EAAE,MAAM,GAAG,IAAI;yCAtE7C,4BAA4B;2CAA5B,4BAA4B;CA0ExC"}
|