@kouji-ui/core 0.9.1 → 0.9.2
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.
|
@@ -1974,6 +1974,21 @@ function longestRunMs(durations, delays) {
|
|
|
1974
1974
|
}
|
|
1975
1975
|
/** The strategy slots of a bundle, in attach order. */
|
|
1976
1976
|
const STRATEGY_SLOTS = ['mount', 'position', 'backdrop', 'scrollLock', 'focusTrap', 'liveAnnouncer', 'trigger'];
|
|
1977
|
+
/**
|
|
1978
|
+
* The controller each attached strategy instance serves. A strategy is
|
|
1979
|
+
* stateful (it holds one overlay's context and its inert / scroll-lock
|
|
1980
|
+
* release handles), so one instance must never run two overlays at once.
|
|
1981
|
+
*/
|
|
1982
|
+
const strategyOwners = new WeakMap();
|
|
1983
|
+
/**
|
|
1984
|
+
* @internal The controller `strategy` is attached to, if any. `KjOverlayPanel`
|
|
1985
|
+
* uses it to drop strategies it inherited from an enclosing overlay (a
|
|
1986
|
+
* select inside a modal dialog must not run the dialog's scrim, inert and
|
|
1987
|
+
* focus trap as its own).
|
|
1988
|
+
*/
|
|
1989
|
+
function overlayStrategyOwner(strategy) {
|
|
1990
|
+
return strategyOwners.get(strategy);
|
|
1991
|
+
}
|
|
1977
1992
|
/**
|
|
1978
1993
|
* Owns the overlay's open/closed state machine plus the rAF/transition
|
|
1979
1994
|
* orchestration. Strategies (mount/position/backdrop/focus-trap/scroll-lock/
|
|
@@ -2125,13 +2140,21 @@ class KjOverlayController {
|
|
|
2125
2140
|
}
|
|
2126
2141
|
for (const slot of STRATEGY_SLOTS) {
|
|
2127
2142
|
const old = prev[slot];
|
|
2128
|
-
if (old && old !== next[slot])
|
|
2143
|
+
if (old && old !== next[slot]) {
|
|
2129
2144
|
old.detach();
|
|
2145
|
+
if (strategyOwners.get(old) === this)
|
|
2146
|
+
strategyOwners.delete(old);
|
|
2147
|
+
}
|
|
2130
2148
|
}
|
|
2131
2149
|
}
|
|
2132
2150
|
this.strategies = next;
|
|
2133
|
-
for (const slot of STRATEGY_SLOTS)
|
|
2134
|
-
next[slot]
|
|
2151
|
+
for (const slot of STRATEGY_SLOTS) {
|
|
2152
|
+
const strategy = next[slot];
|
|
2153
|
+
if (!strategy)
|
|
2154
|
+
continue;
|
|
2155
|
+
strategyOwners.set(strategy, this);
|
|
2156
|
+
strategy.attach(this.context);
|
|
2157
|
+
}
|
|
2135
2158
|
}
|
|
2136
2159
|
open() {
|
|
2137
2160
|
const cur = this._state();
|
|
@@ -2198,7 +2221,12 @@ class KjOverlayController {
|
|
|
2198
2221
|
return;
|
|
2199
2222
|
const s = this.strategies;
|
|
2200
2223
|
for (const slot of [...STRATEGY_SLOTS].reverse()) {
|
|
2201
|
-
s[slot]
|
|
2224
|
+
const strategy = s[slot];
|
|
2225
|
+
if (!strategy)
|
|
2226
|
+
continue;
|
|
2227
|
+
strategy.detach();
|
|
2228
|
+
if (strategyOwners.get(strategy) === this)
|
|
2229
|
+
strategyOwners.delete(strategy);
|
|
2202
2230
|
}
|
|
2203
2231
|
this.strategies = null;
|
|
2204
2232
|
}
|
|
@@ -2911,6 +2939,36 @@ function resolveTokens(self) {
|
|
|
2911
2939
|
role: inject(KJ_OVERLAY_PANEL_ROLE, opts),
|
|
2912
2940
|
};
|
|
2913
2941
|
}
|
|
2942
|
+
/**
|
|
2943
|
+
* `tokens` minus every strategy already attached to a controller other than
|
|
2944
|
+
* `controller`. Strategies are resolved through the injector chain, so an
|
|
2945
|
+
* overlay rendered inside another one (a select, combobox or date picker in
|
|
2946
|
+
* a service-launched dialog) would otherwise find the enclosing overlay's
|
|
2947
|
+
* own instances for every slot it does not provide itself — and run the
|
|
2948
|
+
* dialog's scrim, `inert` and focus trap as if the dropdown were a second
|
|
2949
|
+
* modal, freezing the dialog it belongs to. Those instances belong to the
|
|
2950
|
+
* enclosing overlay; the nested one falls back to "none" for that slot.
|
|
2951
|
+
*/
|
|
2952
|
+
function withoutForeign(tokens, controller) {
|
|
2953
|
+
if (!controller)
|
|
2954
|
+
return tokens;
|
|
2955
|
+
const mine = (s) => {
|
|
2956
|
+
if (!s)
|
|
2957
|
+
return s;
|
|
2958
|
+
const owner = overlayStrategyOwner(s);
|
|
2959
|
+
return owner && owner !== controller ? null : s;
|
|
2960
|
+
};
|
|
2961
|
+
return {
|
|
2962
|
+
...tokens,
|
|
2963
|
+
mount: mine(tokens.mount),
|
|
2964
|
+
position: mine(tokens.position),
|
|
2965
|
+
backdrop: mine(tokens.backdrop),
|
|
2966
|
+
focusTrap: mine(tokens.focusTrap),
|
|
2967
|
+
scrollLock: mine(tokens.scrollLock),
|
|
2968
|
+
liveAnnouncer: mine(tokens.liveAnnouncer),
|
|
2969
|
+
trigger: mine(tokens.trigger),
|
|
2970
|
+
};
|
|
2971
|
+
}
|
|
2914
2972
|
/**
|
|
2915
2973
|
* Marks an element as the overlay's panel — wires it to the controller,
|
|
2916
2974
|
* resolves the panel role/aria-modal, and binds DOM state attributes
|
|
@@ -2925,7 +2983,10 @@ function resolveTokens(self) {
|
|
|
2925
2983
|
* must not inherit the dialog's scroll lock, scrim or `alertdialog` role.
|
|
2926
2984
|
* A panel whose controller is on its host chain (a builder-launched body,
|
|
2927
2985
|
* or the root pattern where one directive provides controller and
|
|
2928
|
-
* strategies for the whole subtree) reads them from that chain
|
|
2986
|
+
* strategies for the whole subtree) reads them from that chain, minus any
|
|
2987
|
+
* strategy instance an enclosing overlay is already running: a select inside
|
|
2988
|
+
* a service-launched dialog finds the dialog's scrim, focus trap and scroll
|
|
2989
|
+
* lock up the chain, and must not adopt them.
|
|
2929
2990
|
*
|
|
2930
2991
|
* A portalled panel whose scope provides a backdrop strategy also gets a
|
|
2931
2992
|
* real `<kj-backdrop>` scrim (created next to the panel, moved into the
|
|
@@ -2980,8 +3041,11 @@ class KjOverlayPanel {
|
|
|
2980
3041
|
return !!t && t.controller !== this.hostController;
|
|
2981
3042
|
}, /* @ts-ignore */
|
|
2982
3043
|
...(ngDevMode ? [{ debugName: "scoped" }] : /* istanbul ignore next */ []));
|
|
2983
|
-
/**
|
|
2984
|
-
|
|
3044
|
+
/**
|
|
3045
|
+
* The tokens that apply to this panel. Inherited ones exclude strategies an
|
|
3046
|
+
* enclosing overlay already runs (see {@link withoutForeign}).
|
|
3047
|
+
*/
|
|
3048
|
+
tokens = computed(() => this.scoped() ? this.own : withoutForeign(this.inherited, this.hostController), /* @ts-ignore */
|
|
2985
3049
|
...(ngDevMode ? [{ debugName: "tokens" }] : /* istanbul ignore next */ []));
|
|
2986
3050
|
role = computed(() => this.tokens().role ?? 'dialog', /* @ts-ignore */
|
|
2987
3051
|
...(ngDevMode ? [{ debugName: "role" }] : /* istanbul ignore next */ []));
|