@operato/app 10.29.47 → 10.29.49
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/dist/src/selector/ox-selector-resource-object.js +13 -4
- package/dist/src/selector/ox-selector-resource-object.js.map +1 -1
- package/dist/test/selector-search-goes-to-the-list.test.d.ts +1 -0
- package/dist/test/selector-search-goes-to-the-list.test.js +31 -0
- package/dist/test/selector-search-goes-to-the-list.test.js.map +1 -0
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +12 -12
- package/test/selector-search-goes-to-the-list.test.ts +34 -0
package/CHANGELOG.md
CHANGED
|
@@ -3,6 +3,23 @@
|
|
|
3
3
|
All notable changes to this project will be documented in this file.
|
|
4
4
|
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
|
5
5
|
|
|
6
|
+
### [10.29.49](https://github.com/hatiolab/operato/compare/v10.29.48...v10.29.49) (2026-10-23)
|
|
7
|
+
|
|
8
|
+
**Note:** Version bump only for package @operato/app
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
### [10.29.48](https://github.com/hatiolab/operato/compare/v10.29.47...v10.29.48) (2026-10-22)
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
### :bug: Bug Fix
|
|
18
|
+
|
|
19
|
+
* **app:** the reference picker sends its search term to the list ([d26a2c1](https://github.com/hatiolab/operato/commit/d26a2c1e430bd7217c1e309e1d0ec547c61441a9))
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
|
|
6
23
|
### [10.29.47](https://github.com/hatiolab/operato/compare/v10.29.46...v10.29.47) (2026-10-21)
|
|
7
24
|
|
|
8
25
|
**Note:** Version bump only for package @operato/app
|
|
@@ -47,7 +47,13 @@ let OxSelectorResourceObject = class OxSelectorResourceObject extends LitElement
|
|
|
47
47
|
* 이 상태를 갱신한다.
|
|
48
48
|
*/
|
|
49
49
|
this.hasSelection = false;
|
|
50
|
-
|
|
50
|
+
/*
|
|
51
|
+
* The title bar's search term goes to the list as its `search`, and the list matches it on the fields it declares
|
|
52
|
+
* searchable. It used to become one filter per column marked `filter: 'search'`, and a picker whose columns marked
|
|
53
|
+
* none - most of them - searched nothing while the box took the typing. The argument is sent only with a term, so a
|
|
54
|
+
* list query that takes no `search` still opens.
|
|
55
|
+
*/
|
|
56
|
+
this.fetchHandler = async ({ filters = [], page, limit, sortings = [], inherited, search }) => {
|
|
51
57
|
var _a, _b;
|
|
52
58
|
if ((_a = this.basicArgs) === null || _a === void 0 ? void 0 : _a.filters) {
|
|
53
59
|
filters = adjustFilters(filters, this.basicArgs.filters);
|
|
@@ -57,10 +63,11 @@ let OxSelectorResourceObject = class OxSelectorResourceObject extends LitElement
|
|
|
57
63
|
sortings = [...this.basicArgs.sorters];
|
|
58
64
|
}
|
|
59
65
|
const pagination = { page, limit };
|
|
66
|
+
const term = search === null || search === void 0 ? void 0 : search.trim();
|
|
60
67
|
const response = await client.query({
|
|
61
68
|
query: gql `
|
|
62
|
-
query($filters: [Filter!], $pagination: Pagination, $sortings: [Sorting!], $inherited: InheritedValueType) {
|
|
63
|
-
fetch: ${this.queryName} (filters: $filters, pagination: $pagination, sortings: $sortings, inherited: $inherited) {
|
|
69
|
+
query($filters: [Filter!], $pagination: Pagination, $sortings: [Sorting!], $inherited: InheritedValueType${term ? ', $search: String' : ''}) {
|
|
70
|
+
fetch: ${this.queryName} (filters: $filters, pagination: $pagination, sortings: $sortings, inherited: $inherited${term ? ', search: $search' : ''}) {
|
|
64
71
|
${this.getQueryFields()}
|
|
65
72
|
}
|
|
66
73
|
}
|
|
@@ -69,7 +76,8 @@ let OxSelectorResourceObject = class OxSelectorResourceObject extends LitElement
|
|
|
69
76
|
filters,
|
|
70
77
|
pagination,
|
|
71
78
|
sortings,
|
|
72
|
-
inherited: inherited || this.inherited
|
|
79
|
+
inherited: inherited || this.inherited,
|
|
80
|
+
...(term ? { search: term } : {})
|
|
73
81
|
},
|
|
74
82
|
context: gqlContext()
|
|
75
83
|
});
|
|
@@ -131,6 +139,7 @@ let OxSelectorResourceObject = class OxSelectorResourceObject extends LitElement
|
|
|
131
139
|
있던 것이고, 남겨 두면 다음 사람이 같은 곳에 빠진다.
|
|
132
140
|
-->
|
|
133
141
|
<ox-grist
|
|
142
|
+
search-mode="term"
|
|
134
143
|
.mode=${isMobileDevice() ? 'LIST' : 'GRID'}
|
|
135
144
|
.config=${this.config}
|
|
136
145
|
.fetchHandler=${this.fetchHandler.bind(this)}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ox-selector-resource-object.js","sourceRoot":"","sources":["../../../src/selector/ox-selector-resource-object.ts"],"names":[],"mappings":"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","sourcesContent":["import '@material/web/icon/icon.js'\nimport '@operato/data-grist'\n\nimport gql from 'graphql-tag'\nimport { css, html, LitElement, PropertyValues } from 'lit'\nimport { customElement, property, query, state } from 'lit/decorators.js'\n\nimport {\n DataGrist,\n FetchHandler,\n GristData,\n GristEventHandler,\n GristRecord,\n ZERO_DATA,\n InheritedValueType\n} from '@operato/data-grist'\nimport { client, gqlContext } from '@operato/graphql'\nimport { i18next } from '@operato/i18n'\nimport { closePopup } from '@operato/popup'\nimport { adjustFilters, isMobileDevice } from '@operato/utils'\nimport { ButtonContainerStyles, CommonHeaderStyles } from '@operato/styles'\n\n@customElement('ox-selector-resource-object')\nexport class OxSelectorResourceObject extends LitElement {\n static styles = [\n CommonHeaderStyles,\n css`\n :host {\n display: flex;\n flex-direction: column;\n\n background-color: var(--md-sys-color-surface);\n\n width: var(--overlay-center-normal-width, 50%);\n height: var(--overlay-center-normal-height, 50%);\n }\n\n ox-grist {\n flex: 1;\n }\n\n .header {\n grid-template-areas: 'filters actions';\n }\n\n [filler] {\n margin-left: auto;\n }\n\n /*\n * 꺼진 단추가 왜 꺼졌는지 말하는 한 줄. **단추 바로 앞에 선다** — 멀리 두면 그 단추의\n * 이야기로 읽히지 않는다.\n *\n * ── 색만 조금 세운다 ──────────────────────────────────────────────────\n * on-surface-variant 로 두었더니 너무 조용했다. 꺼진 단추 옆의 흐린 글자는 그 단추의\n * 일부처럼 보여서, 확인이 왜 꺼졌는지 찾는 눈에 걸리지 않았다.\n *\n * 크기와 배치는 그대로 두고 **잉크만** 바꾼다. 붉은색은 오류이고, 이것은 오류가 아니라\n * 남은 일이다.\n *\n * primary 를 먼저 써 봤는데 이 제품에서는 **흑연**이다(#262e38) — 틀 자체가 무채색이라\n * 강조가 되지 않았다. 강조 잉크는 --ui-accent 이고, 그 색은 **작은 글자를 담을 수 있도록\n * 톤을 낮춰 둔** 것이다.\n *\n * [확인함] 2026-09-05 · --ui-accent #0c7a4c · 팝업 면 #f0f2f4 · 대비 4.79\n * 작은 글자 기준 4.5 를 넘는다\n *\n * accent 가 없는 앱에서는 primary 로 떨어진다.\n */\n [hint] {\n color: var(--ui-accent, var(--md-sys-color-primary));\n font-size: var(--md-sys-typescale-label-medium-size, 0.75rem);\n margin-right: var(--spacing-small, 4px);\n }\n `\n ]\n\n /**\n * 지금 들어 있는 값. **여럿 고르기에서는 배열이다.**\n *\n * 목록을 받아올 때 이 값과 같은 줄을 골라 둔다(fetchHandler). 그래서 팝업을 열면 지금 값이\n * 이미 고른 상태로 보인다.\n */\n @property({ type: Object }) value?: string | string[]\n\n /**\n * 여럿을 고를 수 있나. **부르는 쪽이 켠다.**\n *\n * ── 왜 필요한가 (2026-09-05) ────────────────────────────────────────────\n * 마스터 화면의 관계 칸 다섯이 여러 값을 든다 — 상위 등급(baseIds) · 요구 검사\n * (testSpecificationIds) · 공정의 차례(steps). 이 선택기가 하나만 내주어서 그 칸들이\n * **자유 텍스트로 남아 있었다.**\n *\n * 자유 텍스트는 조용히 틀린다. GUNSAN 을 GUNSAM 이라고 쳐도 저장되고, 그 뒤로 그 참조는\n * 어디에서도 안 걸린다 — 화면에는 값이 있는데.\n *\n * ── 켜면 무엇이 달라지나 ────────────────────────────────────────────────\n * 고르기 줄을 눌러 켜고 끈다(select-row-toggle). 겹쳐 고를 수 있다\n * 두 번 누르기 확정하지 않는다 — 한 줄만 넘기게 되므로\n * confirmCallback 고른 것들의 배열을 넘긴다\n *\n * 켠 쪽이 그 모양을 아니까 콜백을 둘로 나누지 않는다.\n */\n @property({ type: Boolean }) multiple: boolean = false\n @property({ type: Object }) config: any\n @property({ type: Object }) data: GristData = ZERO_DATA\n @property({ type: String }) queryName!: string\n @property({ type: Object }) basicArgs: any\n @property({ type: String }) inherited?: InheritedValueType = InheritedValueType.Include\n @property({ type: Object }) confirmCallback?: (record?: Partial<GristRecord>) => void\n @property({ type: Array }) selectedRecords: GristRecord[] = []\n\n @property({ type: Array }) searchFields: any\n @property({ type: Array }) columns: {\n type: string\n name: string\n header: string\n subFields?: string[]\n hidden: boolean\n queryName: string\n width: number\n }[] = []\n @property({ type: Object }) pagination?: {\n limit: number\n pages: number[]\n }\n @property({ type: Object }) list: any\n @property({ type: String }) valueField: string | ((item: any) => any) = 'id'\n\n /** 설정을 한 번 세웠나 — 열·질의가 바뀔 때만 다시 세운다. */\n private _configured = false\n\n /**\n * 지금 고른 것이 있나 — **바닥 단추가 이 값을 본다.**\n *\n * `selected` 게터는 그리스트를 그때그때 읽으므로 반응형이 아니다. 그래서 단추의 disabled 를\n * 그것으로 묶으면 골라도 다시 그려지지 않는다. 그리스트가 내는 select-record-change 를 듣고\n * 이 상태를 갱신한다.\n */\n @state() private hasSelection = false\n\n @query('ox-grist') grist!: DataGrist\n\n render() {\n return html`\n <!--\n ── .data 를 넘기지 않는다 ────────────────────────────────────────────────\n\n 목록은 아래 fetchHandler 가 채운다. 그런데 .data 로 this.data 를 함께 넘기고\n 있었고, 그 값은 늘 ZERO_DATA(빈 것)였다 — 이 부품에 data 를 주는 부르는 쪽이 없다.\n\n 그리스트는 data 가 설정되면 **reset() 한다**(data-grist 의 updated). 그래서 이 부품이\n 다시 그려지는 순간 빈 값이 다시 내려가 **목록이 통째로 사라진다.**\n\n 여태 안 드러난 이유는 이 부품이 첫 렌더 뒤로 다시 그려질 일이 없었기 때문이다. 바닥\n 단추를 고른 상태에 묶으면서(hasSelection) 비로소 다시 그려지게 되었고, 그때 줄을\n 누르면 목록이 비었다.\n\n [확인함] 2026-09-05 · 라디오 클릭 → reset() 1회(DataGrist.updated) → 20건 → 0건\n\n 고른 상태를 되돌리는 것이 아니라 **이 바인딩을 걷는다.** 넘길 값이 없으면서 넘기고\n 있던 것이고, 남겨 두면 다음 사람이 같은 곳에 빠진다.\n -->\n <ox-grist\n .mode=${isMobileDevice() ? 'LIST' : 'GRID'}\n .config=${this.config}\n .fetchHandler=${this.fetchHandler.bind(this)}\n .selectedRecords=${this.selectedRecords}\n @select-record-change=${() => {\n this.hasSelection = (this.grist?.selected || []).length > 0\n }}\n >\n <div class=\"header\" slot=\"headroom\">\n <div class=\"filters\">\n <!--\n **검색은 이 영역이 그리지 않는다.**\n 찾기는 화면의 제목줄이 든다(페이지의 표준이 그렇다 — 제목 왼쪽, 찾기 오른쪽 끝).\n 여기서도 그리면 검색창이 둘이 된다. 실제로 한 번 그렇게 됐다.\n\n 이 영역은 **찾기가 아닌 필터**의 자리다. 그것이 하나도 없으면 아래 hideHeadroom 이\n 영역째 감춘다 — 열 것이 없는 문을 두지 않는다.\n -->\n <ox-filters-form autofocus without-search></ox-filters-form>\n </div>\n </div>\n </ox-grist>\n\n <!--\n 바닥 — **세 동작의 무게가 다르다.**\n\n 지정 안 함 이 칸에 아무것도 넣지 않는다. 왼쪽 끝에 따로 선다 — 확정과 나란히 두면 잘못 누른다\n 닫기 아무것도 하지 않고 나간다. 글자만\n 확인 이 팝업이 하러 온 일. 하나만 채운다\n\n 아이콘을 떼었다. 「비우기」에는 체크박스 글리프가 붙어 있었는데, 그것은 **입력칸의\n 글리프**여서 동작이 아니라 「끄고 켜는 것」으로 읽혔다. 닫기의 ⊗ 와 확인의 ✓ 도 걷는다 —\n 낱말이 이미 그 일을 말하고, 곁 단추에 아이콘이 붙으면 무게가 주 동작과 비슷해진다.\n -->\n <div class=\"footer\">\n <!--\n 「비우기」였다. **무엇을** 비우는지 말하지 않는다 — 팝업 안에 목록도 검색창도 있어서\n 「목록을 비우나」로도 읽힌다. 이 단추가 하는 일은 그 칸을 빈 값으로 두고 닫는 것이다.\n\n button.empty 를 그대로 쓰지 않는 이유: 그 문자열은 여러 개를 고르는 팝업들도 함께 쓴다\n (결재자 · 수신자 편집기). 거기서는 「고른 것 전부 지우기」이고, 그것은 「지정 안 함」과\n 다른 말이다. 한 문자열이 두 뜻을 겸하고 있었다.\n -->\n <button @click=${this.onEmpty.bind(this)} clear>${i18next.t('button.unset')}</button>\n\n <div filler></div>\n\n <!--\n 힌트는 **단추 두 개 앞**에 선다. 닫기와 확인은 한 쌍이라, 그 사이에 글자가 끼면\n 쌍이 갈라져 보인다.\n -->\n ${!this.hasSelection\n ? html`<span hint\n >${i18next.t(this.multiple ? 'text.pick-from-the-list' : 'text.pick-one-from-the-list')}</span\n >`\n : ''}\n <button @click=${this.onCancel.bind(this)}>${i18next.t('button.cancel')}</button>\n <!--\n ── 고른 것이 없으면 **확인을 끄고, 왜 껐는지 말한다** ─────────────────\n\n 꺼진 단추만 두면 막다른 길이다. 누른 사람은 「왜 안 되나」를 알 길이 없고, 특히 이\n 팝업은 목록이 여러 쪽이라 자기가 무엇을 안 했는지 스스로 떠올리기 어렵다.\n\n 그래서 한 줄을 함께 둔다. **고르면 사라진다** — 할 일이 남아 있는 동안만 보인다.\n\n ── 그 앞에는 더 나빴다 ────────────────────────────────────────────────\n 고른 것이 없을 때 확인이 **값을 지웠다**. 확인이 「지정 안 함」과 같은 일을 했다\n (onConfirm 주석 참조).\n -->\n <button\n @click=${this.onConfirm.bind(this)}\n ?disabled=${!this.hasSelection}\n title=${!this.hasSelection ? i18next.t('text.pick-one-from-the-list') : ''}\n done\n >\n ${i18next.t('button.confirm')}\n </button>\n </div>\n `\n }\n\n searchText(value: string) {\n this.grist.searchText = value\n }\n\n toggleFilter() {\n this.grist.toggleHeadroom()\n }\n\n onEmpty() {\n this.confirmCallback && this.confirmCallback()\n closePopup(this)\n }\n\n onCancel() {\n closePopup(this)\n }\n\n onConfirm() {\n const selected = this.selected\n\n /*\n * ── 고른 것이 없으면 **아무 일도 하지 않는다** ────────────────────────────\n *\n * 전에는 `confirmCallback(this.selected)` 를 그냥 불렀다. 고른 것이 없으면 `selected` 가\n * undefined 이고, 그것은 **아래 onEmpty 가 넘기는 것과 똑같다** — 즉 확인이 「지정 안 함」과\n * 같은 일을 했다. 값을 지우는 단추가 둘이었고, 그중 하나가 주 동작(채운 단추)이었다.\n *\n * 이것이 조용한 데이터 손실로 이어졌다. 고른 표시는 목록을 받아올 때 **그 쪽에서 값을 찾으면**\n * 켜지므로(아래 fetchHandler), 값이 2쪽에 있으면 1쪽만 받은 상태에서는 빈 채다.\n *\n * [확인함] 2026-09-05 · 제조법 · 만드는 품목 · 총 106건\n * value=MT10097283 은 1쪽에 없다 → selectedRecords = []\n * 팝업을 열고 아무것도 만지지 않고 확인 → 칸이 빈 값이 되었다\n *\n * 확인의 뜻은 「이 고른 것으로 한다」다. 고른 것이 없으면 확인할 것이 없으므로 닫기와 같다.\n * **값을 지우는 길은 「지정 안 함」 하나다.**\n */\n if (!selected || (Array.isArray(selected) && selected.length === 0)) {\n closePopup(this)\n return\n }\n\n this.confirmCallback && this.confirmCallback(selected)\n closePopup(this)\n }\n\n fetchHandler: FetchHandler = async ({ filters = [], page, limit, sortings = [], inherited }) => {\n if (this.basicArgs?.filters) {\n filters = adjustFilters(filters, this.basicArgs.filters)\n }\n // basicArgs.sorters가 있으면 기본 정렬로 병합\n if (this.basicArgs?.sorters && sortings.length === 0) {\n sortings = [...this.basicArgs.sorters]\n }\n const pagination = { page, limit }\n\n const response = await client.query({\n query: gql`\n query($filters: [Filter!], $pagination: Pagination, $sortings: [Sorting!], $inherited: InheritedValueType) {\n fetch: ${\n this.queryName\n } (filters: $filters, pagination: $pagination, sortings: $sortings, inherited: $inherited) {\n ${this.getQueryFields()}\n }\n }\n `,\n variables: {\n filters,\n pagination,\n sortings,\n inherited: inherited || this.inherited\n },\n context: gqlContext()\n })\n\n if (!response.errors) {\n const records = response.data.fetch.items.map((item: any) => {\n let rowValue\n\n if (this.valueField && typeof this.valueField === 'function') {\n rowValue = this.valueField(item)\n } else if (this.valueField) {\n rowValue = item[this.valueField]\n } else {\n rowValue = item.id\n }\n\n /*\n * 지금 값과 같은 줄을 골라 둔다. 여럿 고르기에서는 값이 배열이므로 그 안에 있는지 본다.\n *\n * 값이 이 쪽에 없으면 골라지지 않는다 — 그것이 이 부품의 한계다. 부르는 쪽이 지금 값을\n * 글자로 함께 보여 주는 이유가 그것이다(ops-master 의 참조 팝업).\n */\n const wanted = Array.isArray(this.value) ? this.value : this.value ? [this.value] : []\n\n if (wanted.length && wanted.includes(rowValue)) {\n this.selectedRecords = this.multiple ? [...this.selectedRecords, item] : [item]\n item['__selected__'] = true\n /* 받아온 쪽에서 값을 찾았다 — 바닥 단추도 그것을 알아야 한다. */\n this.hasSelection = true\n }\n\n return item\n })\n const total = response.data.fetch.total\n\n return {\n records,\n total,\n limit,\n page\n }\n }\n }\n\n /**\n * 표의 설정을 **첫 렌더 전에** 세운다.\n *\n * ── 왜 firstUpdated 가 아닌가 ──────────────────────────────────────────────\n * 여기 있던 것을 `firstUpdated` 에서 만들고 있었다. 그러면 `<ox-grist>` 와 그 안의 필터 폼이\n * **먼저 붙고** 설정이 나중에 온다. 필터 폼은 붙는 순간의 설정을 한 번 읽어 두므로, 그 스냅숏이\n * 병합 전 것이었다 — 열 일곱 개 중 넷만 든 설정이라 필터가 하나도 없었고, 그래서 필터 영역이\n * 빈 채로 32px 만 서 있었다.\n *\n * `willUpdate` 는 렌더 전에 돈다. 폼이 붙을 때 이미 완성된 설정을 읽는다.\n */\n willUpdate(changed: PropertyValues<this>) {\n /* 열·질의가 바뀌면 다시 세운다. 그 밖의 변화에는 손대지 않는다. */\n /* multiple 이 바뀌면 거터와 고르기 규칙이 달라지므로 설정을 다시 세운다. */\n if (\n this._configured &&\n !changed.has('columns') &&\n !changed.has('queryName') &&\n !changed.has('list') &&\n !changed.has('multiple')\n ) {\n return\n }\n\n this._configured = true\n\n const config = {\n list: this.list,\n columns: [\n {\n type: 'gutter',\n gutterName: 'sequence'\n },\n {\n type: 'gutter',\n gutterName: 'row-selector',\n multiple: this.multiple\n }\n ],\n rows: {\n selectable: {\n multiple: this.multiple\n },\n handlers: {\n /*\n * 하나만 고를 때는 누르면 그 줄로 바뀐다. 여럿일 때는 눌러 켜고 끈다 — 앞의 것을\n * 풀면서 고르면 여러 개를 담을 수 없다.\n */\n click: this.multiple ? 'select-row-toggle' : 'select-row',\n /*\n * 두 번 눌러 확정하는 것은 하나만 고를 때다. 여럿일 때 그렇게 두면 **그 한 줄만**\n * 넘어가고 앞서 고른 것이 사라진다.\n */\n ...(this.multiple\n ? {}\n : {\n dblclick: ((columns, data, column, record, rowIndex, field) => {\n this.onConfirm()\n }) as GristEventHandler\n })\n },\n appendable: false\n },\n pagination: this.pagination\n }\n\n const columns = this.columns || [\n {\n type: 'string',\n name: 'id',\n header: i18next.t('field.id'),\n hidden: true\n },\n {\n type: 'string',\n name: 'name',\n header: i18next.t('field.name'),\n record: {\n align: 'left'\n },\n sortable: true,\n filter: 'search',\n width: 160\n },\n {\n type: 'string',\n name: 'description',\n header: i18next.t('field.description'),\n record: {\n align: 'left'\n },\n sortable: true,\n filter: 'search',\n /*\n * 남는 폭을 이 열이 채운다.\n *\n * 열 폭은 grid-template-columns 에 그대로 들어가므로 문자열도 된다(data-grist 의\n * setGridTemplateColumns). 고정 300px 이었을 때는 오른쪽에 200px 가까운 죽은 자리와\n * 세로선 하나가 남았다 — 목록이 상자 한쪽에 몰려 보인다.\n */\n width: 'minmax(240px, 1fr)'\n }\n ]\n\n /*\n * **밖에서 준 설정을 덮어쓰지 않는다.**\n *\n * 여기서 `this.config` 를 통째로 갈아 끼우고 있었다. 그래서 부르는 쪽이 `config` 를 주어도\n * 사라졌다 — 빈 목록에 무슨 말을 할지(`emptyNote`)를 밖에서 정할 길이 없었다.\n *\n * 빈 목록은 **두 가지 다른 사실**을 말해야 하는 자리다. 「이 표에 아직 아무것도 없다」와\n * 「검색에 걸린 것이 없다」는 다르고, 하나로 합치면 결함이 「고를 게 없다」로 보인다. 실제로\n * 그런 일이 있었다(배열 모양이 달라 목록이 비었는데 아무도 결함인 줄 몰랐다).\n */\n const given = this.config || {}\n\n this.config = {\n ...config,\n ...given,\n columns: [\n ...config.columns,\n ...columns.map(selectField => {\n return {\n ...selectField,\n type: selectField.type || 'string',\n width: selectField.width || 160,\n header: selectField.header || i18next.t(`field.${selectField.name}`)\n }\n })\n ]\n }\n\n /*\n * 걸러 볼 것이 하나도 없으면 그 영역을 감춘다.\n *\n * **병합된 설정으로 판단한다.** 전에는 밖에서 받은 `columns` 만 보았는데, 기본 열(이름·설명)이\n * 이미 검색 필터를 갖고 있어서 그 판단이 실제와 달랐다.\n */\n requestAnimationFrame(() => {\n /*\n * **찾기가 아닌 필터**가 하나라도 있나로 판단한다. 찾기는 이 영역이 안 그리므로(위 주석),\n * `search` 형 필터만 있는 표에서는 여기 그릴 것이 없다.\n */\n const filterable = this.config.columns.some((column: any) => {\n const filter = column.filter\n return filter && filter !== 'search' && filter?.operator !== 'search'\n })\n\n if (!filterable) {\n this.grist?.hideHeadroom()\n }\n })\n }\n\n getQueryFields() {\n if (this.columns && this.columns.length > 0) {\n return `items {\n ${this.columns.map(selectField => {\n return selectField.type === 'object'\n ? `${selectField.name} { ${\n selectField.subFields && selectField.subFields.length > 0\n ? selectField.subFields.join(' ')\n : `id name description`\n } }`\n : `${selectField.name}`\n })}\n }\n total`\n } else {\n return `\n items {\n id\n name\n description\n }\n total\n `\n }\n }\n\n /**\n * 고른 것. **여럿 고르기에서는 배열이고, 아니면 하나다.**\n *\n * 켠 쪽이 그 모양을 아니까 두 가지를 한 자리에서 낸다.\n */\n get selected(): any {\n var grist = this.renderRoot.querySelector('ox-grist') as DataGrist\n\n var selected = grist.selected || []\n\n if (this.multiple) {\n return selected\n }\n\n return selected.length > 0 ? selected[0] : undefined\n }\n}\n"]}
|
|
1
|
+
{"version":3,"file":"ox-selector-resource-object.js","sourceRoot":"","sources":["../../../src/selector/ox-selector-resource-object.ts"],"names":[],"mappings":"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","sourcesContent":["import '@material/web/icon/icon.js'\nimport '@operato/data-grist'\n\nimport gql from 'graphql-tag'\nimport { css, html, LitElement, PropertyValues } from 'lit'\nimport { customElement, property, query, state } from 'lit/decorators.js'\n\nimport {\n DataGrist,\n FetchHandler,\n GristData,\n GristEventHandler,\n GristRecord,\n ZERO_DATA,\n InheritedValueType\n} from '@operato/data-grist'\nimport { client, gqlContext } from '@operato/graphql'\nimport { i18next } from '@operato/i18n'\nimport { closePopup } from '@operato/popup'\nimport { adjustFilters, isMobileDevice } from '@operato/utils'\nimport { ButtonContainerStyles, CommonHeaderStyles } from '@operato/styles'\n\n@customElement('ox-selector-resource-object')\nexport class OxSelectorResourceObject extends LitElement {\n static styles = [\n CommonHeaderStyles,\n css`\n :host {\n display: flex;\n flex-direction: column;\n\n background-color: var(--md-sys-color-surface);\n\n width: var(--overlay-center-normal-width, 50%);\n height: var(--overlay-center-normal-height, 50%);\n }\n\n ox-grist {\n flex: 1;\n }\n\n .header {\n grid-template-areas: 'filters actions';\n }\n\n [filler] {\n margin-left: auto;\n }\n\n /*\n * 꺼진 단추가 왜 꺼졌는지 말하는 한 줄. **단추 바로 앞에 선다** — 멀리 두면 그 단추의\n * 이야기로 읽히지 않는다.\n *\n * ── 색만 조금 세운다 ──────────────────────────────────────────────────\n * on-surface-variant 로 두었더니 너무 조용했다. 꺼진 단추 옆의 흐린 글자는 그 단추의\n * 일부처럼 보여서, 확인이 왜 꺼졌는지 찾는 눈에 걸리지 않았다.\n *\n * 크기와 배치는 그대로 두고 **잉크만** 바꾼다. 붉은색은 오류이고, 이것은 오류가 아니라\n * 남은 일이다.\n *\n * primary 를 먼저 써 봤는데 이 제품에서는 **흑연**이다(#262e38) — 틀 자체가 무채색이라\n * 강조가 되지 않았다. 강조 잉크는 --ui-accent 이고, 그 색은 **작은 글자를 담을 수 있도록\n * 톤을 낮춰 둔** 것이다.\n *\n * [확인함] 2026-09-05 · --ui-accent #0c7a4c · 팝업 면 #f0f2f4 · 대비 4.79\n * 작은 글자 기준 4.5 를 넘는다\n *\n * accent 가 없는 앱에서는 primary 로 떨어진다.\n */\n [hint] {\n color: var(--ui-accent, var(--md-sys-color-primary));\n font-size: var(--md-sys-typescale-label-medium-size, 0.75rem);\n margin-right: var(--spacing-small, 4px);\n }\n `\n ]\n\n /**\n * 지금 들어 있는 값. **여럿 고르기에서는 배열이다.**\n *\n * 목록을 받아올 때 이 값과 같은 줄을 골라 둔다(fetchHandler). 그래서 팝업을 열면 지금 값이\n * 이미 고른 상태로 보인다.\n */\n @property({ type: Object }) value?: string | string[]\n\n /**\n * 여럿을 고를 수 있나. **부르는 쪽이 켠다.**\n *\n * ── 왜 필요한가 (2026-09-05) ────────────────────────────────────────────\n * 마스터 화면의 관계 칸 다섯이 여러 값을 든다 — 상위 등급(baseIds) · 요구 검사\n * (testSpecificationIds) · 공정의 차례(steps). 이 선택기가 하나만 내주어서 그 칸들이\n * **자유 텍스트로 남아 있었다.**\n *\n * 자유 텍스트는 조용히 틀린다. GUNSAN 을 GUNSAM 이라고 쳐도 저장되고, 그 뒤로 그 참조는\n * 어디에서도 안 걸린다 — 화면에는 값이 있는데.\n *\n * ── 켜면 무엇이 달라지나 ────────────────────────────────────────────────\n * 고르기 줄을 눌러 켜고 끈다(select-row-toggle). 겹쳐 고를 수 있다\n * 두 번 누르기 확정하지 않는다 — 한 줄만 넘기게 되므로\n * confirmCallback 고른 것들의 배열을 넘긴다\n *\n * 켠 쪽이 그 모양을 아니까 콜백을 둘로 나누지 않는다.\n */\n @property({ type: Boolean }) multiple: boolean = false\n @property({ type: Object }) config: any\n @property({ type: Object }) data: GristData = ZERO_DATA\n @property({ type: String }) queryName!: string\n @property({ type: Object }) basicArgs: any\n @property({ type: String }) inherited?: InheritedValueType = InheritedValueType.Include\n @property({ type: Object }) confirmCallback?: (record?: Partial<GristRecord>) => void\n @property({ type: Array }) selectedRecords: GristRecord[] = []\n\n @property({ type: Array }) searchFields: any\n @property({ type: Array }) columns: {\n type: string\n name: string\n header: string\n subFields?: string[]\n hidden: boolean\n queryName: string\n width: number\n }[] = []\n @property({ type: Object }) pagination?: {\n limit: number\n pages: number[]\n }\n @property({ type: Object }) list: any\n @property({ type: String }) valueField: string | ((item: any) => any) = 'id'\n\n /** 설정을 한 번 세웠나 — 열·질의가 바뀔 때만 다시 세운다. */\n private _configured = false\n\n /**\n * 지금 고른 것이 있나 — **바닥 단추가 이 값을 본다.**\n *\n * `selected` 게터는 그리스트를 그때그때 읽으므로 반응형이 아니다. 그래서 단추의 disabled 를\n * 그것으로 묶으면 골라도 다시 그려지지 않는다. 그리스트가 내는 select-record-change 를 듣고\n * 이 상태를 갱신한다.\n */\n @state() private hasSelection = false\n\n @query('ox-grist') grist!: DataGrist\n\n render() {\n return html`\n <!--\n ── .data 를 넘기지 않는다 ────────────────────────────────────────────────\n\n 목록은 아래 fetchHandler 가 채운다. 그런데 .data 로 this.data 를 함께 넘기고\n 있었고, 그 값은 늘 ZERO_DATA(빈 것)였다 — 이 부품에 data 를 주는 부르는 쪽이 없다.\n\n 그리스트는 data 가 설정되면 **reset() 한다**(data-grist 의 updated). 그래서 이 부품이\n 다시 그려지는 순간 빈 값이 다시 내려가 **목록이 통째로 사라진다.**\n\n 여태 안 드러난 이유는 이 부품이 첫 렌더 뒤로 다시 그려질 일이 없었기 때문이다. 바닥\n 단추를 고른 상태에 묶으면서(hasSelection) 비로소 다시 그려지게 되었고, 그때 줄을\n 누르면 목록이 비었다.\n\n [확인함] 2026-09-05 · 라디오 클릭 → reset() 1회(DataGrist.updated) → 20건 → 0건\n\n 고른 상태를 되돌리는 것이 아니라 **이 바인딩을 걷는다.** 넘길 값이 없으면서 넘기고\n 있던 것이고, 남겨 두면 다음 사람이 같은 곳에 빠진다.\n -->\n <ox-grist\n search-mode=\"term\"\n .mode=${isMobileDevice() ? 'LIST' : 'GRID'}\n .config=${this.config}\n .fetchHandler=${this.fetchHandler.bind(this)}\n .selectedRecords=${this.selectedRecords}\n @select-record-change=${() => {\n this.hasSelection = (this.grist?.selected || []).length > 0\n }}\n >\n <div class=\"header\" slot=\"headroom\">\n <div class=\"filters\">\n <!--\n **검색은 이 영역이 그리지 않는다.**\n 찾기는 화면의 제목줄이 든다(페이지의 표준이 그렇다 — 제목 왼쪽, 찾기 오른쪽 끝).\n 여기서도 그리면 검색창이 둘이 된다. 실제로 한 번 그렇게 됐다.\n\n 이 영역은 **찾기가 아닌 필터**의 자리다. 그것이 하나도 없으면 아래 hideHeadroom 이\n 영역째 감춘다 — 열 것이 없는 문을 두지 않는다.\n -->\n <ox-filters-form autofocus without-search></ox-filters-form>\n </div>\n </div>\n </ox-grist>\n\n <!--\n 바닥 — **세 동작의 무게가 다르다.**\n\n 지정 안 함 이 칸에 아무것도 넣지 않는다. 왼쪽 끝에 따로 선다 — 확정과 나란히 두면 잘못 누른다\n 닫기 아무것도 하지 않고 나간다. 글자만\n 확인 이 팝업이 하러 온 일. 하나만 채운다\n\n 아이콘을 떼었다. 「비우기」에는 체크박스 글리프가 붙어 있었는데, 그것은 **입력칸의\n 글리프**여서 동작이 아니라 「끄고 켜는 것」으로 읽혔다. 닫기의 ⊗ 와 확인의 ✓ 도 걷는다 —\n 낱말이 이미 그 일을 말하고, 곁 단추에 아이콘이 붙으면 무게가 주 동작과 비슷해진다.\n -->\n <div class=\"footer\">\n <!--\n 「비우기」였다. **무엇을** 비우는지 말하지 않는다 — 팝업 안에 목록도 검색창도 있어서\n 「목록을 비우나」로도 읽힌다. 이 단추가 하는 일은 그 칸을 빈 값으로 두고 닫는 것이다.\n\n button.empty 를 그대로 쓰지 않는 이유: 그 문자열은 여러 개를 고르는 팝업들도 함께 쓴다\n (결재자 · 수신자 편집기). 거기서는 「고른 것 전부 지우기」이고, 그것은 「지정 안 함」과\n 다른 말이다. 한 문자열이 두 뜻을 겸하고 있었다.\n -->\n <button @click=${this.onEmpty.bind(this)} clear>${i18next.t('button.unset')}</button>\n\n <div filler></div>\n\n <!--\n 힌트는 **단추 두 개 앞**에 선다. 닫기와 확인은 한 쌍이라, 그 사이에 글자가 끼면\n 쌍이 갈라져 보인다.\n -->\n ${!this.hasSelection\n ? html`<span hint\n >${i18next.t(this.multiple ? 'text.pick-from-the-list' : 'text.pick-one-from-the-list')}</span\n >`\n : ''}\n <button @click=${this.onCancel.bind(this)}>${i18next.t('button.cancel')}</button>\n <!--\n ── 고른 것이 없으면 **확인을 끄고, 왜 껐는지 말한다** ─────────────────\n\n 꺼진 단추만 두면 막다른 길이다. 누른 사람은 「왜 안 되나」를 알 길이 없고, 특히 이\n 팝업은 목록이 여러 쪽이라 자기가 무엇을 안 했는지 스스로 떠올리기 어렵다.\n\n 그래서 한 줄을 함께 둔다. **고르면 사라진다** — 할 일이 남아 있는 동안만 보인다.\n\n ── 그 앞에는 더 나빴다 ────────────────────────────────────────────────\n 고른 것이 없을 때 확인이 **값을 지웠다**. 확인이 「지정 안 함」과 같은 일을 했다\n (onConfirm 주석 참조).\n -->\n <button\n @click=${this.onConfirm.bind(this)}\n ?disabled=${!this.hasSelection}\n title=${!this.hasSelection ? i18next.t('text.pick-one-from-the-list') : ''}\n done\n >\n ${i18next.t('button.confirm')}\n </button>\n </div>\n `\n }\n\n searchText(value: string) {\n this.grist.searchText = value\n }\n\n toggleFilter() {\n this.grist.toggleHeadroom()\n }\n\n onEmpty() {\n this.confirmCallback && this.confirmCallback()\n closePopup(this)\n }\n\n onCancel() {\n closePopup(this)\n }\n\n onConfirm() {\n const selected = this.selected\n\n /*\n * ── 고른 것이 없으면 **아무 일도 하지 않는다** ────────────────────────────\n *\n * 전에는 `confirmCallback(this.selected)` 를 그냥 불렀다. 고른 것이 없으면 `selected` 가\n * undefined 이고, 그것은 **아래 onEmpty 가 넘기는 것과 똑같다** — 즉 확인이 「지정 안 함」과\n * 같은 일을 했다. 값을 지우는 단추가 둘이었고, 그중 하나가 주 동작(채운 단추)이었다.\n *\n * 이것이 조용한 데이터 손실로 이어졌다. 고른 표시는 목록을 받아올 때 **그 쪽에서 값을 찾으면**\n * 켜지므로(아래 fetchHandler), 값이 2쪽에 있으면 1쪽만 받은 상태에서는 빈 채다.\n *\n * [확인함] 2026-09-05 · 제조법 · 만드는 품목 · 총 106건\n * value=MT10097283 은 1쪽에 없다 → selectedRecords = []\n * 팝업을 열고 아무것도 만지지 않고 확인 → 칸이 빈 값이 되었다\n *\n * 확인의 뜻은 「이 고른 것으로 한다」다. 고른 것이 없으면 확인할 것이 없으므로 닫기와 같다.\n * **값을 지우는 길은 「지정 안 함」 하나다.**\n */\n if (!selected || (Array.isArray(selected) && selected.length === 0)) {\n closePopup(this)\n return\n }\n\n this.confirmCallback && this.confirmCallback(selected)\n closePopup(this)\n }\n\n /*\n * The title bar's search term goes to the list as its `search`, and the list matches it on the fields it declares\n * searchable. It used to become one filter per column marked `filter: 'search'`, and a picker whose columns marked\n * none - most of them - searched nothing while the box took the typing. The argument is sent only with a term, so a\n * list query that takes no `search` still opens.\n */\n fetchHandler: FetchHandler = async ({ filters = [], page, limit, sortings = [], inherited, search }) => {\n if (this.basicArgs?.filters) {\n filters = adjustFilters(filters, this.basicArgs.filters)\n }\n // basicArgs.sorters가 있으면 기본 정렬로 병합\n if (this.basicArgs?.sorters && sortings.length === 0) {\n sortings = [...this.basicArgs.sorters]\n }\n const pagination = { page, limit }\n const term = search?.trim()\n\n const response = await client.query({\n query: gql`\n query($filters: [Filter!], $pagination: Pagination, $sortings: [Sorting!], $inherited: InheritedValueType${term ? ', $search: String' : ''}) {\n fetch: ${\n this.queryName\n } (filters: $filters, pagination: $pagination, sortings: $sortings, inherited: $inherited${term ? ', search: $search' : ''}) {\n ${this.getQueryFields()}\n }\n }\n `,\n variables: {\n filters,\n pagination,\n sortings,\n inherited: inherited || this.inherited,\n ...(term ? { search: term } : {})\n },\n context: gqlContext()\n })\n\n if (!response.errors) {\n const records = response.data.fetch.items.map((item: any) => {\n let rowValue\n\n if (this.valueField && typeof this.valueField === 'function') {\n rowValue = this.valueField(item)\n } else if (this.valueField) {\n rowValue = item[this.valueField]\n } else {\n rowValue = item.id\n }\n\n /*\n * 지금 값과 같은 줄을 골라 둔다. 여럿 고르기에서는 값이 배열이므로 그 안에 있는지 본다.\n *\n * 값이 이 쪽에 없으면 골라지지 않는다 — 그것이 이 부품의 한계다. 부르는 쪽이 지금 값을\n * 글자로 함께 보여 주는 이유가 그것이다(ops-master 의 참조 팝업).\n */\n const wanted = Array.isArray(this.value) ? this.value : this.value ? [this.value] : []\n\n if (wanted.length && wanted.includes(rowValue)) {\n this.selectedRecords = this.multiple ? [...this.selectedRecords, item] : [item]\n item['__selected__'] = true\n /* 받아온 쪽에서 값을 찾았다 — 바닥 단추도 그것을 알아야 한다. */\n this.hasSelection = true\n }\n\n return item\n })\n const total = response.data.fetch.total\n\n return {\n records,\n total,\n limit,\n page\n }\n }\n }\n\n /**\n * 표의 설정을 **첫 렌더 전에** 세운다.\n *\n * ── 왜 firstUpdated 가 아닌가 ──────────────────────────────────────────────\n * 여기 있던 것을 `firstUpdated` 에서 만들고 있었다. 그러면 `<ox-grist>` 와 그 안의 필터 폼이\n * **먼저 붙고** 설정이 나중에 온다. 필터 폼은 붙는 순간의 설정을 한 번 읽어 두므로, 그 스냅숏이\n * 병합 전 것이었다 — 열 일곱 개 중 넷만 든 설정이라 필터가 하나도 없었고, 그래서 필터 영역이\n * 빈 채로 32px 만 서 있었다.\n *\n * `willUpdate` 는 렌더 전에 돈다. 폼이 붙을 때 이미 완성된 설정을 읽는다.\n */\n willUpdate(changed: PropertyValues<this>) {\n /* 열·질의가 바뀌면 다시 세운다. 그 밖의 변화에는 손대지 않는다. */\n /* multiple 이 바뀌면 거터와 고르기 규칙이 달라지므로 설정을 다시 세운다. */\n if (\n this._configured &&\n !changed.has('columns') &&\n !changed.has('queryName') &&\n !changed.has('list') &&\n !changed.has('multiple')\n ) {\n return\n }\n\n this._configured = true\n\n const config = {\n list: this.list,\n columns: [\n {\n type: 'gutter',\n gutterName: 'sequence'\n },\n {\n type: 'gutter',\n gutterName: 'row-selector',\n multiple: this.multiple\n }\n ],\n rows: {\n selectable: {\n multiple: this.multiple\n },\n handlers: {\n /*\n * 하나만 고를 때는 누르면 그 줄로 바뀐다. 여럿일 때는 눌러 켜고 끈다 — 앞의 것을\n * 풀면서 고르면 여러 개를 담을 수 없다.\n */\n click: this.multiple ? 'select-row-toggle' : 'select-row',\n /*\n * 두 번 눌러 확정하는 것은 하나만 고를 때다. 여럿일 때 그렇게 두면 **그 한 줄만**\n * 넘어가고 앞서 고른 것이 사라진다.\n */\n ...(this.multiple\n ? {}\n : {\n dblclick: ((columns, data, column, record, rowIndex, field) => {\n this.onConfirm()\n }) as GristEventHandler\n })\n },\n appendable: false\n },\n pagination: this.pagination\n }\n\n const columns = this.columns || [\n {\n type: 'string',\n name: 'id',\n header: i18next.t('field.id'),\n hidden: true\n },\n {\n type: 'string',\n name: 'name',\n header: i18next.t('field.name'),\n record: {\n align: 'left'\n },\n sortable: true,\n filter: 'search',\n width: 160\n },\n {\n type: 'string',\n name: 'description',\n header: i18next.t('field.description'),\n record: {\n align: 'left'\n },\n sortable: true,\n filter: 'search',\n /*\n * 남는 폭을 이 열이 채운다.\n *\n * 열 폭은 grid-template-columns 에 그대로 들어가므로 문자열도 된다(data-grist 의\n * setGridTemplateColumns). 고정 300px 이었을 때는 오른쪽에 200px 가까운 죽은 자리와\n * 세로선 하나가 남았다 — 목록이 상자 한쪽에 몰려 보인다.\n */\n width: 'minmax(240px, 1fr)'\n }\n ]\n\n /*\n * **밖에서 준 설정을 덮어쓰지 않는다.**\n *\n * 여기서 `this.config` 를 통째로 갈아 끼우고 있었다. 그래서 부르는 쪽이 `config` 를 주어도\n * 사라졌다 — 빈 목록에 무슨 말을 할지(`emptyNote`)를 밖에서 정할 길이 없었다.\n *\n * 빈 목록은 **두 가지 다른 사실**을 말해야 하는 자리다. 「이 표에 아직 아무것도 없다」와\n * 「검색에 걸린 것이 없다」는 다르고, 하나로 합치면 결함이 「고를 게 없다」로 보인다. 실제로\n * 그런 일이 있었다(배열 모양이 달라 목록이 비었는데 아무도 결함인 줄 몰랐다).\n */\n const given = this.config || {}\n\n this.config = {\n ...config,\n ...given,\n columns: [\n ...config.columns,\n ...columns.map(selectField => {\n return {\n ...selectField,\n type: selectField.type || 'string',\n width: selectField.width || 160,\n header: selectField.header || i18next.t(`field.${selectField.name}`)\n }\n })\n ]\n }\n\n /*\n * 걸러 볼 것이 하나도 없으면 그 영역을 감춘다.\n *\n * **병합된 설정으로 판단한다.** 전에는 밖에서 받은 `columns` 만 보았는데, 기본 열(이름·설명)이\n * 이미 검색 필터를 갖고 있어서 그 판단이 실제와 달랐다.\n */\n requestAnimationFrame(() => {\n /*\n * **찾기가 아닌 필터**가 하나라도 있나로 판단한다. 찾기는 이 영역이 안 그리므로(위 주석),\n * `search` 형 필터만 있는 표에서는 여기 그릴 것이 없다.\n */\n const filterable = this.config.columns.some((column: any) => {\n const filter = column.filter\n return filter && filter !== 'search' && filter?.operator !== 'search'\n })\n\n if (!filterable) {\n this.grist?.hideHeadroom()\n }\n })\n }\n\n getQueryFields() {\n if (this.columns && this.columns.length > 0) {\n return `items {\n ${this.columns.map(selectField => {\n return selectField.type === 'object'\n ? `${selectField.name} { ${\n selectField.subFields && selectField.subFields.length > 0\n ? selectField.subFields.join(' ')\n : `id name description`\n } }`\n : `${selectField.name}`\n })}\n }\n total`\n } else {\n return `\n items {\n id\n name\n description\n }\n total\n `\n }\n }\n\n /**\n * 고른 것. **여럿 고르기에서는 배열이고, 아니면 하나다.**\n *\n * 켠 쪽이 그 모양을 아니까 두 가지를 한 자리에서 낸다.\n */\n get selected(): any {\n var grist = this.renderRoot.querySelector('ox-grist') as DataGrist\n\n var selected = grist.selected || []\n\n if (this.multiple) {\n return selected\n }\n\n return selected.length > 0 ? selected[0] : undefined\n }\n}\n"]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { expect } from '@open-wc/testing';
|
|
2
|
+
/**
|
|
3
|
+
* The reference picker's search reaches the list.
|
|
4
|
+
*
|
|
5
|
+
* The popup's title bar has a search box. Its term used to become one `search` filter per column marked
|
|
6
|
+
* `filter: 'search'`; a picker whose columns marked none - an Office deposit or contract picker - sent nothing, and the
|
|
7
|
+
* list came back whole while the box held the typing. The picker's grid now runs in `term` mode and the term goes to the
|
|
8
|
+
* list as its `search`, which the list matches on the fields it declares searchable.
|
|
9
|
+
*
|
|
10
|
+
* Read from the source, as selector-confirm-keeps-value does: the component pulls the GraphQL client in at load.
|
|
11
|
+
*/
|
|
12
|
+
describe('reference picker · the search term goes to the list', () => {
|
|
13
|
+
let source;
|
|
14
|
+
before(async () => {
|
|
15
|
+
const response = await fetch('/src/selector/ox-selector-resource-object.ts');
|
|
16
|
+
expect(response.ok).to.be.true;
|
|
17
|
+
source = await response.text();
|
|
18
|
+
});
|
|
19
|
+
it('runs its grid in term mode', () => {
|
|
20
|
+
const grist = source.slice(source.indexOf('<ox-grist'), source.indexOf('>', source.indexOf('<ox-grist')));
|
|
21
|
+
expect(grist).to.include('search-mode="term"');
|
|
22
|
+
});
|
|
23
|
+
it('sends the term as the list search, and only when there is one', () => {
|
|
24
|
+
const handler = source.slice(source.indexOf(' fetchHandler: FetchHandler'), source.indexOf(' willUpdate('));
|
|
25
|
+
expect(handler).to.include('inherited, search })');
|
|
26
|
+
expect(handler).to.include("${term ? ', $search: String' : ''}");
|
|
27
|
+
expect(handler).to.include("${term ? ', search: $search' : ''}");
|
|
28
|
+
expect(handler).to.include('...(term ? { search: term } : {})');
|
|
29
|
+
});
|
|
30
|
+
});
|
|
31
|
+
//# sourceMappingURL=selector-search-goes-to-the-list.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"selector-search-goes-to-the-list.test.js","sourceRoot":"","sources":["../../test/selector-search-goes-to-the-list.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAA;AAEzC;;;;;;;;;GASG;AACH,QAAQ,CAAC,qDAAqD,EAAE,GAAG,EAAE;IACnE,IAAI,MAAc,CAAA;IAElB,MAAM,CAAC,KAAK,IAAI,EAAE;QAChB,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,8CAA8C,CAAC,CAAA;QAC5E,MAAM,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,IAAI,CAAA;QAC9B,MAAM,GAAG,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAA;IAChC,CAAC,CAAC,CAAA;IAEF,EAAE,CAAC,4BAA4B,EAAE,GAAG,EAAE;QACpC,MAAM,KAAK,GAAG,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,OAAO,CAAC,WAAW,CAAC,EAAE,MAAM,CAAC,OAAO,CAAC,GAAG,EAAE,MAAM,CAAC,OAAO,CAAC,WAAW,CAAC,CAAC,CAAC,CAAA;QACzG,MAAM,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,OAAO,CAAC,oBAAoB,CAAC,CAAA;IAChD,CAAC,CAAC,CAAA;IAEF,EAAE,CAAC,+DAA+D,EAAE,GAAG,EAAE;QACvE,MAAM,OAAO,GAAG,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,OAAO,CAAC,8BAA8B,CAAC,EAAE,MAAM,CAAC,OAAO,CAAC,eAAe,CAAC,CAAC,CAAA;QAC7G,MAAM,CAAC,OAAO,CAAC,CAAC,EAAE,CAAC,OAAO,CAAC,sBAAsB,CAAC,CAAA;QAClD,MAAM,CAAC,OAAO,CAAC,CAAC,EAAE,CAAC,OAAO,CAAC,oCAAoC,CAAC,CAAA;QAChE,MAAM,CAAC,OAAO,CAAC,CAAC,EAAE,CAAC,OAAO,CAAC,oCAAoC,CAAC,CAAA;QAChE,MAAM,CAAC,OAAO,CAAC,CAAC,EAAE,CAAC,OAAO,CAAC,mCAAmC,CAAC,CAAA;IACjE,CAAC,CAAC,CAAA;AACJ,CAAC,CAAC,CAAA","sourcesContent":["import { expect } from '@open-wc/testing'\n\n/**\n * The reference picker's search reaches the list.\n *\n * The popup's title bar has a search box. Its term used to become one `search` filter per column marked\n * `filter: 'search'`; a picker whose columns marked none - an Office deposit or contract picker - sent nothing, and the\n * list came back whole while the box held the typing. The picker's grid now runs in `term` mode and the term goes to the\n * list as its `search`, which the list matches on the fields it declares searchable.\n *\n * Read from the source, as selector-confirm-keeps-value does: the component pulls the GraphQL client in at load.\n */\ndescribe('reference picker · the search term goes to the list', () => {\n let source: string\n\n before(async () => {\n const response = await fetch('/src/selector/ox-selector-resource-object.ts')\n expect(response.ok).to.be.true\n source = await response.text()\n })\n\n it('runs its grid in term mode', () => {\n const grist = source.slice(source.indexOf('<ox-grist'), source.indexOf('>', source.indexOf('<ox-grist')))\n expect(grist).to.include('search-mode=\"term\"')\n })\n\n it('sends the term as the list search, and only when there is one', () => {\n const handler = source.slice(source.indexOf(' fetchHandler: FetchHandler'), source.indexOf(' willUpdate('))\n expect(handler).to.include('inherited, search })')\n expect(handler).to.include(\"${term ? ', $search: String' : ''}\")\n expect(handler).to.include(\"${term ? ', search: $search' : ''}\")\n expect(handler).to.include('...(term ? { search: term } : {})')\n })\n})\n"]}
|