@operato/layout 10.11.0 → 10.15.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/CHANGELOG.md +17 -0
- package/coverage/lcov-report/base.css +224 -0
- package/coverage/lcov-report/block-navigation.js +87 -0
- package/coverage/lcov-report/favicon.png +0 -0
- package/coverage/lcov-report/index.html +401 -0
- package/coverage/lcov-report/prettify.css +1 -0
- package/coverage/lcov-report/prettify.js +2 -0
- package/coverage/lcov-report/sort-arrow-sprite.png +0 -0
- package/coverage/lcov-report/sorter.js +210 -0
- package/coverage/lcov.info +14447 -0
- package/dist/src/actions/layout.d.ts +2 -9
- package/dist/src/actions/layout.js +21 -5
- package/dist/src/actions/layout.js.map +1 -1
- package/dist/test/popup-result.test.d.ts +1 -0
- package/dist/test/popup-result.test.js +162 -0
- package/dist/test/popup-result.test.js.map +1 -0
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +6 -6
- package/test/popup-result.test.ts +202 -0
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { TemplateResult } from 'lit';
|
|
2
|
-
import {
|
|
2
|
+
import { PopupOptions } from '@operato/popup';
|
|
3
3
|
export type Viewpart = {
|
|
4
4
|
hovering?: 'center' | 'edge' | 'next';
|
|
5
5
|
show?: boolean;
|
|
@@ -67,11 +67,4 @@ export declare const updateLayout: (wide?: boolean) => void;
|
|
|
67
67
|
export declare const openOverlay: (name: string, options?: any, silent?: boolean) => void;
|
|
68
68
|
export declare const closeOverlay: (name: string) => void;
|
|
69
69
|
export declare const toggleOverlay: (name: string, options: any) => void;
|
|
70
|
-
|
|
71
|
-
* open popup in operato application environment
|
|
72
|
-
*
|
|
73
|
-
* @param {*} template html template to be rendered inside the popup
|
|
74
|
-
* @param {PopupOptions} options
|
|
75
|
-
* @returns popup handle object. This object is used to close the popup.
|
|
76
|
-
*/
|
|
77
|
-
export declare const openPopup: (template: TemplateResult, options?: PopupOptions) => PopupHandle;
|
|
70
|
+
export declare const openPopup: <R = unknown>(template: TemplateResult, options?: PopupOptions) => import("@operato/popup").PopupHandle<R>;
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import { setOpenPopupImplementation } from '@operato/popup';
|
|
1
|
+
import { openPopup as openPopupWithResult, setOpenPopupImplementation } from '@operato/popup';
|
|
2
|
+
import { claimHistoryBack } from '@operato/popup/implementation.js';
|
|
2
3
|
import { store } from '@operato/shell';
|
|
3
4
|
export const appendViewpart = ({ name, viewpart, position }) => {
|
|
4
5
|
store.dispatch({
|
|
@@ -123,9 +124,9 @@ var popupSequence = 0;
|
|
|
123
124
|
*
|
|
124
125
|
* @param {*} template html template to be rendered inside the popup
|
|
125
126
|
* @param {PopupOptions} options
|
|
126
|
-
* @returns
|
|
127
|
+
* @returns the implementation handle. Callers get the full handle (with `result`) from `openPopup`.
|
|
127
128
|
*/
|
|
128
|
-
|
|
129
|
+
const openPopupInLayout = (template, options = {}) => {
|
|
129
130
|
var name = `$popup-${popupSequence++}`;
|
|
130
131
|
appendViewpart({
|
|
131
132
|
name,
|
|
@@ -142,13 +143,22 @@ export const openPopup = (template, options = {}) => {
|
|
|
142
143
|
position: VIEWPART_POSITION.HEADERBAR
|
|
143
144
|
});
|
|
144
145
|
openOverlay(name, options, true);
|
|
146
|
+
/* Typed, not cast: a cast would let a close() that forgets to answer compile. */
|
|
145
147
|
var popup = {
|
|
146
148
|
name,
|
|
147
149
|
close: () => {
|
|
148
150
|
/* 현재 overlay state를 확인해서, 자신인 경우에 history.back() 한다. */
|
|
149
151
|
var state = history.state;
|
|
150
152
|
var overlay = (state || {}).overlay;
|
|
151
|
-
|
|
153
|
+
/*
|
|
154
|
+
* Not on top, or a back for this popup is already on its way: close nothing, and say so, so the
|
|
155
|
+
* value of this request is not kept as the result.
|
|
156
|
+
*/
|
|
157
|
+
if (!overlay || overlay.name != name || !claimHistoryBack(name)) {
|
|
158
|
+
return false;
|
|
159
|
+
}
|
|
160
|
+
history.back();
|
|
161
|
+
return true;
|
|
152
162
|
},
|
|
153
163
|
closed: false
|
|
154
164
|
};
|
|
@@ -161,5 +171,11 @@ export const openPopup = (template, options = {}) => {
|
|
|
161
171
|
});
|
|
162
172
|
return popup;
|
|
163
173
|
};
|
|
164
|
-
setOpenPopupImplementation(
|
|
174
|
+
setOpenPopupImplementation(openPopupInLayout);
|
|
175
|
+
/*
|
|
176
|
+
* The popup package's openPopup adds `result` and `close(result)` on top of whichever implementation is
|
|
177
|
+
* registered, so the handle is built in one place. Re-exported here because most callers import
|
|
178
|
+
* openPopup from @operato/layout.
|
|
179
|
+
*/
|
|
180
|
+
export const openPopup = openPopupWithResult;
|
|
165
181
|
//# sourceMappingURL=layout.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"layout.js","sourceRoot":"","sources":["../../../src/actions/layout.ts"],"names":[],"mappings":"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","sourcesContent":["import { TemplateResult } from 'lit'\n\nimport { PopupHandle, PopupOptions, setOpenPopupImplementation } from '@operato/popup'\nimport { store } from '@operato/shell'\n\nexport type Viewpart = {\n hovering?: 'center' | 'edge' | 'next'\n show?: boolean\n size?: string\n title?: string\n help?: string\n level?: VIEWPART_LEVEL\n closable?: boolean\n search?: {\n value?: string\n handler?: (closure: any, value: string) => void\n placeholder?: string\n }\n filter?: {\n handler?: (closure: any) => void\n }\n backdrop?: boolean\n temporary?: boolean /* auto remove */\n resizable?: boolean\n movable?: boolean\n template?: TemplateResult\n position?: VIEWPART_POSITION\n templateProperties?: any\n zIndex?: number\n}\n\nexport type AppendViewpartAction = {\n name: string\n viewpart: Viewpart\n position: VIEWPART_POSITION\n}\n\nexport const appendViewpart = ({ name, viewpart, position }: AppendViewpartAction) => {\n store.dispatch({\n type: APPEND_VIEWPART,\n name,\n viewpart: {\n ...viewpart,\n show: viewpart.hovering && viewpart.show ? false : viewpart.show\n },\n position\n })\n\n if (viewpart.hovering && viewpart.show) {\n openOverlay(name)\n }\n}\n\nexport const removeViewpart = (name: string) => {\n store.dispatch({\n type: REMOVE_VIEWPART,\n name\n })\n}\n\nexport const updateViewpart = (name: string, override: any) => {\n store.dispatch({\n type: UPDATE_VIEWPART,\n name,\n override\n })\n}\n\nexport const APPEND_VIEWPART = 'APPEND_VIEWPART'\nexport const REMOVE_VIEWPART = 'REMOVE_VIEWPART'\nexport const UPDATE_VIEWPART = 'UPDATE_VIEWPART'\n\nexport enum VIEWPART_POSITION {\n HEADERBAR = 'headerbar',\n NAVBAR = 'navbar',\n ASIDEBAR = 'asidebar',\n FOOTERBAR = 'footerbar',\n PAGE_HEADERBAR = 'page-headerbar',\n PAGE_FOOTERBAR = 'page-footerbar'\n}\n\n/**\n * 각 뷰파트 스택의 순서를 정하는 속성이다.\n * 뷰파트의 level 속성에 정의되도록 한다.\n * 맨 위/아래/좌측/우측에 보여져야하는 뷰파트는 TOPMOST 속성을 갖도록 하고,\n * 쌓인 순서대로 보여지는 뷰파트는 NORMAL 속성을 갖도록 한다.\n * default 속성은 NORMAL이다.\n */\nexport enum VIEWPART_LEVEL {\n TOPMOST = 'TOPMOST',\n NORMAL = 'NORMAL'\n}\n\nexport enum TOOL_POSITION {\n FRONT_END = 'FRONT_END',\n FRONT = 'FRONT',\n CENTER = 'CENTER',\n REAR = 'REAR',\n REAR_END = 'REAR_END'\n}\n\nexport const UPDATE_VIEWPORT_WIDTH = 'UPDATE_VIEWPORT_WIDTH'\n\nexport const updateLayout = (wide?: boolean) => {\n store.dispatch({\n type: UPDATE_VIEWPORT_WIDTH,\n width: wide ? 'WIDE' : 'NARROW'\n })\n}\n\n/* overlay navigation */\nvar overlaySequence = 0\n\nexport const openOverlay = (name: string, options?: any, silent?: boolean) => {\n var beforeState = history.state\n var beforeOverlay = beforeState ? beforeState.overlay : undefined\n var beforeSequence = !beforeOverlay || beforeOverlay.sequence === undefined ? overlaySequence : beforeOverlay.sequence\n var afterSequence = (overlaySequence = beforeSequence + 1)\n\n /* store의 layout의 내용을 변경한다. */\n if (!silent && options) {\n store.dispatch({\n type: UPDATE_VIEWPART,\n name,\n override: options\n })\n }\n\n /*\n * 현재 history.state를 확인하고, overlay의 이름이 같은\n * history에 추가하고 open 동작을 실행한다.\n */\n var afterState = Object.assign({}, beforeState || {}, {\n overlay: { name, sequence: afterSequence, escapable: options?.escapable !== false }\n })\n\n history.pushState(afterState, '', location.href)\n\n window.dispatchEvent(\n new CustomEvent('popstate', {\n detail: { state: afterState }\n })\n )\n}\n\nexport const closeOverlay = (name: string) => {\n /*\n * 실제로 overlay를 close하는 작업은 window.onpopstate 핸들러에서 한다.\n */\n history.back()\n}\n\nexport const toggleOverlay = (name: string, options: any) => {\n var { name: beforeOverlayName } = (history.state && history.state.overlay) || {}\n\n if (beforeOverlayName == name) {\n closeOverlay(name)\n } else {\n openOverlay(name, options)\n }\n}\n\n/*\n * popup handling\n *\n * popup은 overlay의 특별한 형태이다.\n * popup은 open될 때, viewpart를 append 하며, close 될 때 viewpart를 remove 한다.\n * - name: '$popup-${popupSequence}'\n * - position: VIEWPART_POSITION_HEADERBAR\n * - hovering: 'center' | 'next' | 'edge'\n */\nvar popupSequence = 0\n\n/**\n * open popup in operato application environment\n *\n * @param {*} template html template to be rendered inside the popup\n * @param {PopupOptions} options\n * @returns popup handle object. This object is used to close the popup.\n */\nexport const openPopup = (template: TemplateResult, options: PopupOptions = {}): PopupHandle => {\n var name = `$popup-${popupSequence++}`\n\n appendViewpart({\n name,\n viewpart: {\n hovering: 'center',\n closable: true,\n movable: true,\n ...options,\n backdrop: true,\n show: false,\n temporary: true /* auto remove */,\n template\n },\n position: VIEWPART_POSITION.HEADERBAR\n })\n\n openOverlay(name, options, true)\n\n var popup = {\n name,\n close: () => {\n /* 현재 overlay state를 확인해서, 자신인 경우에 history.back() 한다. */\n var state = history.state\n var overlay = (state || {}).overlay\n\n overlay && overlay.name == name && history.back()\n },\n closed: false\n } as PopupHandle\n\n document.addEventListener('overlay-closed', function listener(e) {\n if (name == (e as CustomEvent).detail) {\n popup.closed = true\n popup.onclosed && popup.onclosed()\n document.removeEventListener('overlay-closed', listener)\n }\n })\n\n return popup\n}\n\nsetOpenPopupImplementation(openPopup)\n"]}
|
|
1
|
+
{"version":3,"file":"layout.js","sourceRoot":"","sources":["../../../src/actions/layout.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,SAAS,IAAI,mBAAmB,EAAgB,0BAA0B,EAAE,MAAM,gBAAgB,CAAA;AAC3G,OAAO,EAAE,gBAAgB,EAA6B,MAAM,kCAAkC,CAAA;AAC9F,OAAO,EAAE,KAAK,EAAE,MAAM,gBAAgB,CAAA;AAkCtC,MAAM,CAAC,MAAM,cAAc,GAAG,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAE,QAAQ,EAAwB,EAAE,EAAE;IACnF,KAAK,CAAC,QAAQ,CAAC;QACb,IAAI,EAAE,eAAe;QACrB,IAAI;QACJ,QAAQ,EAAE;YACR,GAAG,QAAQ;YACX,IAAI,EAAE,QAAQ,CAAC,QAAQ,IAAI,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,QAAQ,CAAC,IAAI;SACjE;QACD,QAAQ;KACT,CAAC,CAAA;IAEF,IAAI,QAAQ,CAAC,QAAQ,IAAI,QAAQ,CAAC,IAAI,EAAE,CAAC;QACvC,WAAW,CAAC,IAAI,CAAC,CAAA;IACnB,CAAC;AACH,CAAC,CAAA;AAED,MAAM,CAAC,MAAM,cAAc,GAAG,CAAC,IAAY,EAAE,EAAE;IAC7C,KAAK,CAAC,QAAQ,CAAC;QACb,IAAI,EAAE,eAAe;QACrB,IAAI;KACL,CAAC,CAAA;AACJ,CAAC,CAAA;AAED,MAAM,CAAC,MAAM,cAAc,GAAG,CAAC,IAAY,EAAE,QAAa,EAAE,EAAE;IAC5D,KAAK,CAAC,QAAQ,CAAC;QACb,IAAI,EAAE,eAAe;QACrB,IAAI;QACJ,QAAQ;KACT,CAAC,CAAA;AACJ,CAAC,CAAA;AAED,MAAM,CAAC,MAAM,eAAe,GAAG,iBAAiB,CAAA;AAChD,MAAM,CAAC,MAAM,eAAe,GAAG,iBAAiB,CAAA;AAChD,MAAM,CAAC,MAAM,eAAe,GAAG,iBAAiB,CAAA;AAEhD,MAAM,CAAN,IAAY,iBAOX;AAPD,WAAY,iBAAiB;IAC3B,4CAAuB,CAAA;IACvB,sCAAiB,CAAA;IACjB,0CAAqB,CAAA;IACrB,4CAAuB,CAAA;IACvB,sDAAiC,CAAA;IACjC,sDAAiC,CAAA;AACnC,CAAC,EAPW,iBAAiB,KAAjB,iBAAiB,QAO5B;AAED;;;;;;GAMG;AACH,MAAM,CAAN,IAAY,cAGX;AAHD,WAAY,cAAc;IACxB,qCAAmB,CAAA;IACnB,mCAAiB,CAAA;AACnB,CAAC,EAHW,cAAc,KAAd,cAAc,QAGzB;AAED,MAAM,CAAN,IAAY,aAMX;AAND,WAAY,aAAa;IACvB,wCAAuB,CAAA;IACvB,gCAAe,CAAA;IACf,kCAAiB,CAAA;IACjB,8BAAa,CAAA;IACb,sCAAqB,CAAA;AACvB,CAAC,EANW,aAAa,KAAb,aAAa,QAMxB;AAED,MAAM,CAAC,MAAM,qBAAqB,GAAG,uBAAuB,CAAA;AAE5D,MAAM,CAAC,MAAM,YAAY,GAAG,CAAC,IAAc,EAAE,EAAE;IAC7C,KAAK,CAAC,QAAQ,CAAC;QACb,IAAI,EAAE,qBAAqB;QAC3B,KAAK,EAAE,IAAI,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,QAAQ;KAChC,CAAC,CAAA;AACJ,CAAC,CAAA;AAED,wBAAwB;AACxB,IAAI,eAAe,GAAG,CAAC,CAAA;AAEvB,MAAM,CAAC,MAAM,WAAW,GAAG,CAAC,IAAY,EAAE,OAAa,EAAE,MAAgB,EAAE,EAAE;IAC3E,IAAI,WAAW,GAAG,OAAO,CAAC,KAAK,CAAA;IAC/B,IAAI,aAAa,GAAG,WAAW,CAAC,CAAC,CAAC,WAAW,CAAC,OAAO,CAAC,CAAC,CAAC,SAAS,CAAA;IACjE,IAAI,cAAc,GAAG,CAAC,aAAa,IAAI,aAAa,CAAC,QAAQ,KAAK,SAAS,CAAC,CAAC,CAAC,eAAe,CAAC,CAAC,CAAC,aAAa,CAAC,QAAQ,CAAA;IACtH,IAAI,aAAa,GAAG,CAAC,eAAe,GAAG,cAAc,GAAG,CAAC,CAAC,CAAA;IAE1D,8BAA8B;IAC9B,IAAI,CAAC,MAAM,IAAI,OAAO,EAAE,CAAC;QACvB,KAAK,CAAC,QAAQ,CAAC;YACb,IAAI,EAAE,eAAe;YACrB,IAAI;YACJ,QAAQ,EAAE,OAAO;SAClB,CAAC,CAAA;IACJ,CAAC;IAED;;;OAGG;IACH,IAAI,UAAU,GAAG,MAAM,CAAC,MAAM,CAAC,EAAE,EAAE,WAAW,IAAI,EAAE,EAAE;QACpD,OAAO,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,aAAa,EAAE,SAAS,EAAE,CAAA,OAAO,aAAP,OAAO,uBAAP,OAAO,CAAE,SAAS,MAAK,KAAK,EAAE;KACpF,CAAC,CAAA;IAEF,OAAO,CAAC,SAAS,CAAC,UAAU,EAAE,EAAE,EAAE,QAAQ,CAAC,IAAI,CAAC,CAAA;IAEhD,MAAM,CAAC,aAAa,CAClB,IAAI,WAAW,CAAC,UAAU,EAAE;QAC1B,MAAM,EAAE,EAAE,KAAK,EAAE,UAAU,EAAE;KAC9B,CAAC,CACH,CAAA;AACH,CAAC,CAAA;AAED,MAAM,CAAC,MAAM,YAAY,GAAG,CAAC,IAAY,EAAE,EAAE;IAC3C;;OAEG;IACH,OAAO,CAAC,IAAI,EAAE,CAAA;AAChB,CAAC,CAAA;AAED,MAAM,CAAC,MAAM,aAAa,GAAG,CAAC,IAAY,EAAE,OAAY,EAAE,EAAE;IAC1D,IAAI,EAAE,IAAI,EAAE,iBAAiB,EAAE,GAAG,CAAC,OAAO,CAAC,KAAK,IAAI,OAAO,CAAC,KAAK,CAAC,OAAO,CAAC,IAAI,EAAE,CAAA;IAEhF,IAAI,iBAAiB,IAAI,IAAI,EAAE,CAAC;QAC9B,YAAY,CAAC,IAAI,CAAC,CAAA;IACpB,CAAC;SAAM,CAAC;QACN,WAAW,CAAC,IAAI,EAAE,OAAO,CAAC,CAAA;IAC5B,CAAC;AACH,CAAC,CAAA;AAED;;;;;;;;GAQG;AACH,IAAI,aAAa,GAAG,CAAC,CAAA;AAErB;;;;;;GAMG;AACH,MAAM,iBAAiB,GAAG,CAAC,QAAwB,EAAE,UAAwB,EAAE,EAA6B,EAAE;IAC5G,IAAI,IAAI,GAAG,UAAU,aAAa,EAAE,EAAE,CAAA;IAEtC,cAAc,CAAC;QACb,IAAI;QACJ,QAAQ,EAAE;YACR,QAAQ,EAAE,QAAQ;YAClB,QAAQ,EAAE,IAAI;YACd,OAAO,EAAE,IAAI;YACb,GAAG,OAAO;YACV,QAAQ,EAAE,IAAI;YACd,IAAI,EAAE,KAAK;YACX,SAAS,EAAE,IAAI,CAAC,iBAAiB;YACjC,QAAQ;SACT;QACD,QAAQ,EAAE,iBAAiB,CAAC,SAAS;KACtC,CAAC,CAAA;IAEF,WAAW,CAAC,IAAI,EAAE,OAAO,EAAE,IAAI,CAAC,CAAA;IAEhC,iFAAiF;IACjF,IAAI,KAAK,GAA8B;QACrC,IAAI;QACJ,KAAK,EAAE,GAAG,EAAE;YACV,wDAAwD;YACxD,IAAI,KAAK,GAAG,OAAO,CAAC,KAAK,CAAA;YACzB,IAAI,OAAO,GAAG,CAAC,KAAK,IAAI,EAAE,CAAC,CAAC,OAAO,CAAA;YAEnC;;;eAGG;YACH,IAAI,CAAC,OAAO,IAAI,OAAO,CAAC,IAAI,IAAI,IAAI,IAAI,CAAC,gBAAgB,CAAC,IAAI,CAAC,EAAE,CAAC;gBAChE,OAAO,KAAK,CAAA;YACd,CAAC;YAED,OAAO,CAAC,IAAI,EAAE,CAAA;YACd,OAAO,IAAI,CAAA;QACb,CAAC;QACD,MAAM,EAAE,KAAK;KACd,CAAA;IAED,QAAQ,CAAC,gBAAgB,CAAC,gBAAgB,EAAE,SAAS,QAAQ,CAAC,CAAC;QAC7D,IAAI,IAAI,IAAK,CAAiB,CAAC,MAAM,EAAE,CAAC;YACtC,KAAK,CAAC,MAAM,GAAG,IAAI,CAAA;YACnB,KAAK,CAAC,QAAQ,IAAI,KAAK,CAAC,QAAQ,EAAE,CAAA;YAClC,QAAQ,CAAC,mBAAmB,CAAC,gBAAgB,EAAE,QAAQ,CAAC,CAAA;QAC1D,CAAC;IACH,CAAC,CAAC,CAAA;IAEF,OAAO,KAAK,CAAA;AACd,CAAC,CAAA;AAED,0BAA0B,CAAC,iBAAiB,CAAC,CAAA;AAE7C;;;;GAIG;AACH,MAAM,CAAC,MAAM,SAAS,GAAG,mBAAmB,CAAA","sourcesContent":["import { TemplateResult } from 'lit'\n\nimport { openPopup as openPopupWithResult, PopupOptions, setOpenPopupImplementation } from '@operato/popup'\nimport { claimHistoryBack, PopupImplementationHandle } from '@operato/popup/implementation.js'\nimport { store } from '@operato/shell'\n\nexport type Viewpart = {\n hovering?: 'center' | 'edge' | 'next'\n show?: boolean\n size?: string\n title?: string\n help?: string\n level?: VIEWPART_LEVEL\n closable?: boolean\n search?: {\n value?: string\n handler?: (closure: any, value: string) => void\n placeholder?: string\n }\n filter?: {\n handler?: (closure: any) => void\n }\n backdrop?: boolean\n temporary?: boolean /* auto remove */\n resizable?: boolean\n movable?: boolean\n template?: TemplateResult\n position?: VIEWPART_POSITION\n templateProperties?: any\n zIndex?: number\n}\n\nexport type AppendViewpartAction = {\n name: string\n viewpart: Viewpart\n position: VIEWPART_POSITION\n}\n\nexport const appendViewpart = ({ name, viewpart, position }: AppendViewpartAction) => {\n store.dispatch({\n type: APPEND_VIEWPART,\n name,\n viewpart: {\n ...viewpart,\n show: viewpart.hovering && viewpart.show ? false : viewpart.show\n },\n position\n })\n\n if (viewpart.hovering && viewpart.show) {\n openOverlay(name)\n }\n}\n\nexport const removeViewpart = (name: string) => {\n store.dispatch({\n type: REMOVE_VIEWPART,\n name\n })\n}\n\nexport const updateViewpart = (name: string, override: any) => {\n store.dispatch({\n type: UPDATE_VIEWPART,\n name,\n override\n })\n}\n\nexport const APPEND_VIEWPART = 'APPEND_VIEWPART'\nexport const REMOVE_VIEWPART = 'REMOVE_VIEWPART'\nexport const UPDATE_VIEWPART = 'UPDATE_VIEWPART'\n\nexport enum VIEWPART_POSITION {\n HEADERBAR = 'headerbar',\n NAVBAR = 'navbar',\n ASIDEBAR = 'asidebar',\n FOOTERBAR = 'footerbar',\n PAGE_HEADERBAR = 'page-headerbar',\n PAGE_FOOTERBAR = 'page-footerbar'\n}\n\n/**\n * 각 뷰파트 스택의 순서를 정하는 속성이다.\n * 뷰파트의 level 속성에 정의되도록 한다.\n * 맨 위/아래/좌측/우측에 보여져야하는 뷰파트는 TOPMOST 속성을 갖도록 하고,\n * 쌓인 순서대로 보여지는 뷰파트는 NORMAL 속성을 갖도록 한다.\n * default 속성은 NORMAL이다.\n */\nexport enum VIEWPART_LEVEL {\n TOPMOST = 'TOPMOST',\n NORMAL = 'NORMAL'\n}\n\nexport enum TOOL_POSITION {\n FRONT_END = 'FRONT_END',\n FRONT = 'FRONT',\n CENTER = 'CENTER',\n REAR = 'REAR',\n REAR_END = 'REAR_END'\n}\n\nexport const UPDATE_VIEWPORT_WIDTH = 'UPDATE_VIEWPORT_WIDTH'\n\nexport const updateLayout = (wide?: boolean) => {\n store.dispatch({\n type: UPDATE_VIEWPORT_WIDTH,\n width: wide ? 'WIDE' : 'NARROW'\n })\n}\n\n/* overlay navigation */\nvar overlaySequence = 0\n\nexport const openOverlay = (name: string, options?: any, silent?: boolean) => {\n var beforeState = history.state\n var beforeOverlay = beforeState ? beforeState.overlay : undefined\n var beforeSequence = !beforeOverlay || beforeOverlay.sequence === undefined ? overlaySequence : beforeOverlay.sequence\n var afterSequence = (overlaySequence = beforeSequence + 1)\n\n /* store의 layout의 내용을 변경한다. */\n if (!silent && options) {\n store.dispatch({\n type: UPDATE_VIEWPART,\n name,\n override: options\n })\n }\n\n /*\n * 현재 history.state를 확인하고, overlay의 이름이 같은\n * history에 추가하고 open 동작을 실행한다.\n */\n var afterState = Object.assign({}, beforeState || {}, {\n overlay: { name, sequence: afterSequence, escapable: options?.escapable !== false }\n })\n\n history.pushState(afterState, '', location.href)\n\n window.dispatchEvent(\n new CustomEvent('popstate', {\n detail: { state: afterState }\n })\n )\n}\n\nexport const closeOverlay = (name: string) => {\n /*\n * 실제로 overlay를 close하는 작업은 window.onpopstate 핸들러에서 한다.\n */\n history.back()\n}\n\nexport const toggleOverlay = (name: string, options: any) => {\n var { name: beforeOverlayName } = (history.state && history.state.overlay) || {}\n\n if (beforeOverlayName == name) {\n closeOverlay(name)\n } else {\n openOverlay(name, options)\n }\n}\n\n/*\n * popup handling\n *\n * popup은 overlay의 특별한 형태이다.\n * popup은 open될 때, viewpart를 append 하며, close 될 때 viewpart를 remove 한다.\n * - name: '$popup-${popupSequence}'\n * - position: VIEWPART_POSITION_HEADERBAR\n * - hovering: 'center' | 'next' | 'edge'\n */\nvar popupSequence = 0\n\n/**\n * open popup in operato application environment\n *\n * @param {*} template html template to be rendered inside the popup\n * @param {PopupOptions} options\n * @returns the implementation handle. Callers get the full handle (with `result`) from `openPopup`.\n */\nconst openPopupInLayout = (template: TemplateResult, options: PopupOptions = {}): PopupImplementationHandle => {\n var name = `$popup-${popupSequence++}`\n\n appendViewpart({\n name,\n viewpart: {\n hovering: 'center',\n closable: true,\n movable: true,\n ...options,\n backdrop: true,\n show: false,\n temporary: true /* auto remove */,\n template\n },\n position: VIEWPART_POSITION.HEADERBAR\n })\n\n openOverlay(name, options, true)\n\n /* Typed, not cast: a cast would let a close() that forgets to answer compile. */\n var popup: PopupImplementationHandle = {\n name,\n close: () => {\n /* 현재 overlay state를 확인해서, 자신인 경우에 history.back() 한다. */\n var state = history.state\n var overlay = (state || {}).overlay\n\n /*\n * Not on top, or a back for this popup is already on its way: close nothing, and say so, so the\n * value of this request is not kept as the result.\n */\n if (!overlay || overlay.name != name || !claimHistoryBack(name)) {\n return false\n }\n\n history.back()\n return true\n },\n closed: false\n }\n\n document.addEventListener('overlay-closed', function listener(e) {\n if (name == (e as CustomEvent).detail) {\n popup.closed = true\n popup.onclosed && popup.onclosed()\n document.removeEventListener('overlay-closed', listener)\n }\n })\n\n return popup\n}\n\nsetOpenPopupImplementation(openPopupInLayout)\n\n/*\n * The popup package's openPopup adds `result` and `close(result)` on top of whichever implementation is\n * registered, so the handle is built in one place. Re-exported here because most callers import\n * openPopup from @operato/layout.\n */\nexport const openPopup = openPopupWithResult\n"]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import '../src/index.js';
|
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
import { expect, fixture } from '@open-wc/testing';
|
|
2
|
+
import { html } from 'lit';
|
|
3
|
+
import { store } from '@operato/shell';
|
|
4
|
+
import { closePopup } from '@operato/popup';
|
|
5
|
+
import '../src/index.js';
|
|
6
|
+
import { openPopup } from '../src/index.js';
|
|
7
|
+
/**
|
|
8
|
+
* The popup result settles exactly once, with the right value, whichever way the popup closes -
|
|
9
|
+
* for the layout implementation, the one applications run.
|
|
10
|
+
*
|
|
11
|
+
* Here every close goes through history: the overlay pushes a state when it opens, and closing is
|
|
12
|
+
* history.back(), which hides the temporary viewpart, which removes the overlay, which announces
|
|
13
|
+
* `overlay-closed`. The same table runs for the default implementation in @operato/popup/test.
|
|
14
|
+
*/
|
|
15
|
+
const TIMEOUT = 'TIMEOUT';
|
|
16
|
+
function within(promise, ms = 3000) {
|
|
17
|
+
return Promise.race([promise, new Promise(resolve => setTimeout(() => resolve(TIMEOUT), ms))]);
|
|
18
|
+
}
|
|
19
|
+
const settleTurn = (ms = 100) => new Promise(resolve => setTimeout(resolve, ms));
|
|
20
|
+
/*
|
|
21
|
+
* Stand in for the application shell's router: it calls the registered navigation callbacks on
|
|
22
|
+
* popstate. The layout's overlay stack lives in one of those callbacks.
|
|
23
|
+
*/
|
|
24
|
+
window.addEventListener('popstate', e => {
|
|
25
|
+
var _a;
|
|
26
|
+
const callbacks = (((_a = store.getState().route) === null || _a === void 0 ? void 0 : _a.callbacks) || []);
|
|
27
|
+
callbacks.forEach(callback => callback(window.location, e));
|
|
28
|
+
});
|
|
29
|
+
async function waitForOverlay(name) {
|
|
30
|
+
var _a;
|
|
31
|
+
for (let i = 0; i < 50; i++) {
|
|
32
|
+
const bar = document.querySelector('ox-header-bar');
|
|
33
|
+
const overlays = Array.from((_a = bar === null || bar === void 0 ? void 0 : bar.renderRoot.querySelectorAll('ox-floating-overlay')) !== null && _a !== void 0 ? _a : []);
|
|
34
|
+
const overlay = overlays.find(o => o.name === name);
|
|
35
|
+
if (overlay) {
|
|
36
|
+
await overlay.updateComplete;
|
|
37
|
+
return overlay;
|
|
38
|
+
}
|
|
39
|
+
await settleTurn(20);
|
|
40
|
+
}
|
|
41
|
+
throw new Error(`overlay ${name} did not appear`);
|
|
42
|
+
}
|
|
43
|
+
let markerSequence = 0;
|
|
44
|
+
async function open(options = {}) {
|
|
45
|
+
/* Closing must go back exactly one step - to this marker, not past it. */
|
|
46
|
+
const marker = `before-popup-${markerSequence++}`;
|
|
47
|
+
history.pushState({ marker }, '', location.href);
|
|
48
|
+
const handle = openPopup(html `<div class="content">content</div>`, options);
|
|
49
|
+
let onclosedCalls = 0;
|
|
50
|
+
handle.onclosed = () => onclosedCalls++;
|
|
51
|
+
const overlay = await waitForOverlay(handle.name);
|
|
52
|
+
return {
|
|
53
|
+
handle,
|
|
54
|
+
overlay,
|
|
55
|
+
content: overlay.querySelector('.content'),
|
|
56
|
+
onclosedCalls: () => onclosedCalls,
|
|
57
|
+
backToMarker: () => { var _a, _b; return ((_a = history.state) === null || _a === void 0 ? void 0 : _a.marker) === marker && !((_b = history.state) === null || _b === void 0 ? void 0 : _b.overlay); }
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
describe('openPopup result - layout implementation', () => {
|
|
61
|
+
beforeEach(async () => {
|
|
62
|
+
/*
|
|
63
|
+
* A floor of history entries, so a close that goes back one step too far lands on this page
|
|
64
|
+
* instead of leaving the test runner. Such an extra step still fails the test through
|
|
65
|
+
* `history.length`-independent checks below: the next popup would not appear.
|
|
66
|
+
*/
|
|
67
|
+
for (let i = 0; i < 5; i++)
|
|
68
|
+
history.pushState({}, '', location.href);
|
|
69
|
+
await fixture(html `<ox-header-bar></ox-header-bar>`);
|
|
70
|
+
});
|
|
71
|
+
it('handle.close(value) settles with that value', async () => {
|
|
72
|
+
const { handle, onclosedCalls, backToMarker } = await open();
|
|
73
|
+
handle.close('saved');
|
|
74
|
+
expect(await within(handle.result)).to.equal('saved');
|
|
75
|
+
await settleTurn();
|
|
76
|
+
expect(handle.closed).to.equal(true);
|
|
77
|
+
expect(onclosedCalls()).to.equal(1);
|
|
78
|
+
expect(backToMarker(), 'history went back exactly one step').to.equal(true);
|
|
79
|
+
});
|
|
80
|
+
it('closePopup(element, value) from the content settles with that value', async () => {
|
|
81
|
+
const { handle, content, onclosedCalls, backToMarker } = await open();
|
|
82
|
+
closePopup(content, 'picked');
|
|
83
|
+
expect(await within(handle.result)).to.equal('picked');
|
|
84
|
+
await settleTurn();
|
|
85
|
+
expect(handle.closed).to.equal(true);
|
|
86
|
+
expect(onclosedCalls()).to.equal(1);
|
|
87
|
+
expect(backToMarker(), 'history went back exactly one step').to.equal(true);
|
|
88
|
+
});
|
|
89
|
+
it('history back settles undefined', async () => {
|
|
90
|
+
const { handle, onclosedCalls, backToMarker } = await open();
|
|
91
|
+
history.back();
|
|
92
|
+
expect(await within(handle.result)).to.equal(undefined);
|
|
93
|
+
await settleTurn();
|
|
94
|
+
expect(handle.closed).to.equal(true);
|
|
95
|
+
expect(onclosedCalls()).to.equal(1);
|
|
96
|
+
expect(backToMarker(), 'history went back exactly one step').to.equal(true);
|
|
97
|
+
});
|
|
98
|
+
it('ESC settles undefined', async () => {
|
|
99
|
+
const { handle, onclosedCalls, backToMarker } = await open({ escapable: true });
|
|
100
|
+
document.dispatchEvent(new KeyboardEvent('keydown', { key: 'Escape', bubbles: true }));
|
|
101
|
+
expect(await within(handle.result)).to.equal(undefined);
|
|
102
|
+
await settleTurn();
|
|
103
|
+
expect(onclosedCalls()).to.equal(1);
|
|
104
|
+
expect(backToMarker(), 'history went back exactly one step').to.equal(true);
|
|
105
|
+
});
|
|
106
|
+
it('backdrop click settles undefined', async () => {
|
|
107
|
+
const { handle, overlay, onclosedCalls, backToMarker } = await open({ escapable: true });
|
|
108
|
+
const backdrop = overlay.renderRoot.querySelector('#backdrop');
|
|
109
|
+
expect(backdrop, 'backdrop element').to.exist;
|
|
110
|
+
backdrop.click();
|
|
111
|
+
expect(await within(handle.result)).to.equal(undefined);
|
|
112
|
+
await settleTurn();
|
|
113
|
+
expect(onclosedCalls()).to.equal(1);
|
|
114
|
+
expect(backToMarker(), 'history went back exactly one step').to.equal(true);
|
|
115
|
+
});
|
|
116
|
+
it('the X button settles undefined', async () => {
|
|
117
|
+
const { handle, overlay, onclosedCalls, backToMarker } = await open({ title: 'x' });
|
|
118
|
+
const closer = overlay.renderRoot.querySelector('[closable][close]');
|
|
119
|
+
expect(closer, 'close button').to.exist;
|
|
120
|
+
closer.click();
|
|
121
|
+
expect(await within(handle.result)).to.equal(undefined);
|
|
122
|
+
await settleTurn();
|
|
123
|
+
expect(onclosedCalls()).to.equal(1);
|
|
124
|
+
expect(backToMarker(), 'history went back exactly one step').to.equal(true);
|
|
125
|
+
});
|
|
126
|
+
it('the first close request wins: closePopup then handle.close', async () => {
|
|
127
|
+
const { handle, content, onclosedCalls, backToMarker } = await open();
|
|
128
|
+
closePopup(content, 'first');
|
|
129
|
+
handle.close('second');
|
|
130
|
+
expect(await within(handle.result)).to.equal('first');
|
|
131
|
+
await settleTurn();
|
|
132
|
+
expect(onclosedCalls()).to.equal(1);
|
|
133
|
+
expect(backToMarker(), 'history went back exactly one step').to.equal(true);
|
|
134
|
+
});
|
|
135
|
+
it('a close request that closed nothing leaves no value behind', async () => {
|
|
136
|
+
/*
|
|
137
|
+
* A lower popup cannot close while another sits on top of it. Its close('ignored') does nothing -
|
|
138
|
+
* and must not come back later as the result when the person dismisses that popup.
|
|
139
|
+
*/
|
|
140
|
+
const lower = await open();
|
|
141
|
+
const upper = await open();
|
|
142
|
+
lower.handle.close('ignored');
|
|
143
|
+
await settleTurn();
|
|
144
|
+
expect(lower.handle.closed, 'the lower popup stays open').to.equal(false);
|
|
145
|
+
upper.handle.close();
|
|
146
|
+
expect(await within(upper.handle.result)).to.equal(undefined);
|
|
147
|
+
await settleTurn();
|
|
148
|
+
const closer = lower.overlay.renderRoot.querySelector('[closable][close]');
|
|
149
|
+
closer.click();
|
|
150
|
+
expect(await within(lower.handle.result)).to.equal(undefined);
|
|
151
|
+
});
|
|
152
|
+
it('the first close request wins: handle.close then closePopup', async () => {
|
|
153
|
+
const { handle, content, onclosedCalls, backToMarker } = await open();
|
|
154
|
+
handle.close('first');
|
|
155
|
+
closePopup(content, 'second');
|
|
156
|
+
expect(await within(handle.result)).to.equal('first');
|
|
157
|
+
await settleTurn();
|
|
158
|
+
expect(onclosedCalls()).to.equal(1);
|
|
159
|
+
expect(backToMarker(), 'history went back exactly one step').to.equal(true);
|
|
160
|
+
});
|
|
161
|
+
});
|
|
162
|
+
//# sourceMappingURL=popup-result.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"popup-result.test.js","sourceRoot":"","sources":["../../test/popup-result.test.ts"],"names":[],"mappings":"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","sourcesContent":["import { expect, fixture } from '@open-wc/testing'\nimport { html } from 'lit'\n\nimport { store } from '@operato/shell'\nimport { closePopup } from '@operato/popup'\nimport type { OxFloatingOverlay } from '@operato/popup/ox-floating-overlay.js'\n\nimport '../src/index.js'\nimport { openPopup } from '../src/index.js'\n\n/**\n * The popup result settles exactly once, with the right value, whichever way the popup closes -\n * for the layout implementation, the one applications run.\n *\n * Here every close goes through history: the overlay pushes a state when it opens, and closing is\n * history.back(), which hides the temporary viewpart, which removes the overlay, which announces\n * `overlay-closed`. The same table runs for the default implementation in @operato/popup/test.\n */\n\nconst TIMEOUT = 'TIMEOUT'\n\nfunction within<T>(promise: Promise<T>, ms = 3000): Promise<T | typeof TIMEOUT> {\n return Promise.race([promise, new Promise<typeof TIMEOUT>(resolve => setTimeout(() => resolve(TIMEOUT), ms))])\n}\n\nconst settleTurn = (ms = 100) => new Promise(resolve => setTimeout(resolve, ms))\n\n/*\n * Stand in for the application shell's router: it calls the registered navigation callbacks on\n * popstate. The layout's overlay stack lives in one of those callbacks.\n */\nwindow.addEventListener('popstate', e => {\n const callbacks = ((store.getState() as any).route?.callbacks || []) as Array<(l: Location, e: Event) => void>\n callbacks.forEach(callback => callback(window.location, e))\n})\n\nasync function waitForOverlay(name: string): Promise<OxFloatingOverlay> {\n for (let i = 0; i < 50; i++) {\n const bar = document.querySelector('ox-header-bar') as (HTMLElement & { renderRoot: ParentNode }) | null\n const overlays = Array.from(bar?.renderRoot.querySelectorAll('ox-floating-overlay') ?? []) as OxFloatingOverlay[]\n const overlay = overlays.find(o => o.name === name)\n if (overlay) {\n await overlay.updateComplete\n return overlay\n }\n await settleTurn(20)\n }\n throw new Error(`overlay ${name} did not appear`)\n}\n\nlet markerSequence = 0\n\nasync function open(options = {}) {\n /* Closing must go back exactly one step - to this marker, not past it. */\n const marker = `before-popup-${markerSequence++}`\n history.pushState({ marker }, '', location.href)\n\n const handle = openPopup<string>(html`<div class=\"content\">content</div>`, options)\n let onclosedCalls = 0\n handle.onclosed = () => onclosedCalls++\n\n const overlay = await waitForOverlay(handle.name)\n\n return {\n handle,\n overlay,\n content: overlay.querySelector('.content') as HTMLElement,\n onclosedCalls: () => onclosedCalls,\n backToMarker: () => history.state?.marker === marker && !history.state?.overlay\n }\n}\n\ndescribe('openPopup result - layout implementation', () => {\n beforeEach(async () => {\n /*\n * A floor of history entries, so a close that goes back one step too far lands on this page\n * instead of leaving the test runner. Such an extra step still fails the test through\n * `history.length`-independent checks below: the next popup would not appear.\n */\n for (let i = 0; i < 5; i++) history.pushState({}, '', location.href)\n await fixture(html`<ox-header-bar></ox-header-bar>`)\n })\n\n it('handle.close(value) settles with that value', async () => {\n const { handle, onclosedCalls, backToMarker } = await open()\n\n handle.close('saved')\n\n expect(await within(handle.result)).to.equal('saved')\n await settleTurn()\n expect(handle.closed).to.equal(true)\n expect(onclosedCalls()).to.equal(1)\n expect(backToMarker(), 'history went back exactly one step').to.equal(true)\n })\n\n it('closePopup(element, value) from the content settles with that value', async () => {\n const { handle, content, onclosedCalls, backToMarker } = await open()\n\n closePopup(content, 'picked')\n\n expect(await within(handle.result)).to.equal('picked')\n await settleTurn()\n expect(handle.closed).to.equal(true)\n expect(onclosedCalls()).to.equal(1)\n expect(backToMarker(), 'history went back exactly one step').to.equal(true)\n })\n\n it('history back settles undefined', async () => {\n const { handle, onclosedCalls, backToMarker } = await open()\n\n history.back()\n\n expect(await within(handle.result)).to.equal(undefined)\n await settleTurn()\n expect(handle.closed).to.equal(true)\n expect(onclosedCalls()).to.equal(1)\n expect(backToMarker(), 'history went back exactly one step').to.equal(true)\n })\n\n it('ESC settles undefined', async () => {\n const { handle, onclosedCalls, backToMarker } = await open({ escapable: true })\n\n document.dispatchEvent(new KeyboardEvent('keydown', { key: 'Escape', bubbles: true }))\n\n expect(await within(handle.result)).to.equal(undefined)\n await settleTurn()\n expect(onclosedCalls()).to.equal(1)\n expect(backToMarker(), 'history went back exactly one step').to.equal(true)\n })\n\n it('backdrop click settles undefined', async () => {\n const { handle, overlay, onclosedCalls, backToMarker } = await open({ escapable: true })\n\n const backdrop = overlay.renderRoot.querySelector('#backdrop') as HTMLElement\n expect(backdrop, 'backdrop element').to.exist\n backdrop.click()\n\n expect(await within(handle.result)).to.equal(undefined)\n await settleTurn()\n expect(onclosedCalls()).to.equal(1)\n expect(backToMarker(), 'history went back exactly one step').to.equal(true)\n })\n\n it('the X button settles undefined', async () => {\n const { handle, overlay, onclosedCalls, backToMarker } = await open({ title: 'x' })\n\n const closer = overlay.renderRoot.querySelector('[closable][close]') as HTMLElement\n expect(closer, 'close button').to.exist\n closer.click()\n\n expect(await within(handle.result)).to.equal(undefined)\n await settleTurn()\n expect(onclosedCalls()).to.equal(1)\n expect(backToMarker(), 'history went back exactly one step').to.equal(true)\n })\n\n it('the first close request wins: closePopup then handle.close', async () => {\n const { handle, content, onclosedCalls, backToMarker } = await open()\n\n closePopup(content, 'first')\n handle.close('second')\n\n expect(await within(handle.result)).to.equal('first')\n await settleTurn()\n expect(onclosedCalls()).to.equal(1)\n expect(backToMarker(), 'history went back exactly one step').to.equal(true)\n })\n\n it('a close request that closed nothing leaves no value behind', async () => {\n /*\n * A lower popup cannot close while another sits on top of it. Its close('ignored') does nothing -\n * and must not come back later as the result when the person dismisses that popup.\n */\n const lower = await open()\n const upper = await open()\n\n lower.handle.close('ignored')\n await settleTurn()\n expect(lower.handle.closed, 'the lower popup stays open').to.equal(false)\n\n upper.handle.close()\n expect(await within(upper.handle.result)).to.equal(undefined)\n await settleTurn()\n\n const closer = lower.overlay.renderRoot.querySelector('[closable][close]') as HTMLElement\n closer.click()\n\n expect(await within(lower.handle.result)).to.equal(undefined)\n })\n\n it('the first close request wins: handle.close then closePopup', async () => {\n const { handle, content, onclosedCalls, backToMarker } = await open()\n\n handle.close('first')\n closePopup(content, 'second')\n\n expect(await within(handle.result)).to.equal('first')\n await settleTurn()\n expect(onclosedCalls()).to.equal(1)\n expect(backToMarker(), 'history went back exactly one step').to.equal(true)\n })\n})\n"]}
|