@things-factory/shell 10.0.5 → 10.0.7
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/client/themes/layout-theme.css +41 -3
- package/dist-server/service/common-types/list-param.d.ts +2 -0
- package/dist-server/service/common-types/list-param.js +8 -0
- package/dist-server/service/common-types/list-param.js.map +1 -1
- package/dist-server/tsconfig.tsbuildinfo +1 -1
- package/dist-server/utils/condition-builder.js +6 -2
- package/dist-server/utils/condition-builder.js.map +1 -1
- package/dist-server/utils/get-query-builder-from-list-params.d.ts +23 -0
- package/dist-server/utils/get-query-builder-from-list-params.js +98 -18
- package/dist-server/utils/get-query-builder-from-list-params.js.map +1 -1
- package/dist-server/utils/index.d.ts +1 -0
- package/dist-server/utils/index.js +1 -0
- package/dist-server/utils/index.js.map +1 -1
- package/dist-server/utils/keyset-cursor.d.ts +31 -0
- package/dist-server/utils/keyset-cursor.js +74 -0
- package/dist-server/utils/keyset-cursor.js.map +1 -0
- package/package.json +2 -2
|
@@ -1,10 +1,33 @@
|
|
|
1
1
|
body {
|
|
2
|
-
|
|
2
|
+
/*
|
|
3
|
+
* 크롬(헤더바·좌측 레일)의 색. 두 모드가 서로 달라야 하므로 여기서 라이트 값을 두고
|
|
4
|
+
* 파일 아래 .dark 에서 갈라 놓는다. body(0,0,1) 보다 .dark(0,1,0) 가 우선하므로
|
|
5
|
+
* 같은 파일 안에서 결정된다 — 테마 파일 로드 순서에 의존하지 않는다.
|
|
6
|
+
*
|
|
7
|
+
* 라이트 = 브랜드 헤더(primary). primary 는 라이트 스킴에서 중간 톤이라 흰 글자와 짝을 이루는
|
|
8
|
+
* 앱 바로 적절하다. 클래스가 아직 붙지 않은 초기 렌더에도 이 값이 쓰이는데, 그 시점 화면은
|
|
9
|
+
* 라이트이므로 맞다.
|
|
10
|
+
*
|
|
11
|
+
* 앱이 직접 정하려면 --app-bar-background / --app-bar-color / --app-bar-border 를 지정한다.
|
|
12
|
+
*/
|
|
13
|
+
--app-bar-background: var(--md-sys-color-primary);
|
|
14
|
+
--app-bar-color: var(--md-sys-color-on-primary);
|
|
15
|
+
/*
|
|
16
|
+
* 크롬과 본문의 경계. 색차만으로는 팔레트에 따라 구분이 사라진다 —
|
|
17
|
+
* operato-board 다크에서 레일(primary-container rgb(55 51 49))과 본문(background rgb(53 51 48))이
|
|
18
|
+
* 대비 1.02 로 사실상 같은 색이었다. 그래서 경계선을 기본으로 둔다.
|
|
19
|
+
*/
|
|
20
|
+
--app-bar-border: 1px solid var(--md-sys-color-outline-variant);
|
|
21
|
+
|
|
22
|
+
--header-bar-background-color: var(--app-bar-background);
|
|
3
23
|
--header-bar-title: bold 17.5px var(--theme-font);
|
|
4
|
-
--header-bar-color: var(--
|
|
24
|
+
--header-bar-color: var(--app-bar-color);
|
|
25
|
+
--header-bar-border: var(--app-bar-border);
|
|
5
26
|
--header-bar-height: 42px;
|
|
6
27
|
|
|
7
|
-
|
|
28
|
+
/* 레일도 헤더와 같은 크롬 표면으로 묶는다 — 이전에는 서로 다른 역할이라 색이 갈라졌다 */
|
|
29
|
+
--nav-bar-background-color: var(--app-bar-background);
|
|
30
|
+
--nav-bar-border: var(--app-bar-border);
|
|
8
31
|
|
|
9
32
|
--group-bar-background-color: var(--md-sys-color-secondary-container);
|
|
10
33
|
--group-bar-textbutton: 16px/32px var(--theme-font);
|
|
@@ -40,6 +63,21 @@ body {
|
|
|
40
63
|
--splitter-hover-background-color: var(--md-sys-color-surface-variant);
|
|
41
64
|
}
|
|
42
65
|
|
|
66
|
+
/*
|
|
67
|
+
* 다크 크롬 — 팔레트 역할로 구성한다.
|
|
68
|
+
*
|
|
69
|
+
* 다크에서 브랜드 역할을 쓰면 밝은 띠가 되고(primary), primary-container 는 8개 팔레트 중 7개에서
|
|
70
|
+
* 본문보다 **밝아서**(↑) 크롬이 떠 보인다. surface-container-lowest 는 표면 계단의 최하단이라
|
|
71
|
+
* 전 팔레트에서 본문보다 어둡고(↓), 흰 글자 대비도 14.5~17.1 로 넉넉하다.
|
|
72
|
+
*
|
|
73
|
+
* .dark(0,1,0) 가 body(0,0,1) 보다 우선하므로 같은 파일 안에서 결정된다 —
|
|
74
|
+
* 테마 파일 로드 순서에 의존하지 않는다.
|
|
75
|
+
*/
|
|
76
|
+
.dark {
|
|
77
|
+
--app-bar-background: var(--md-sys-color-surface-container-lowest);
|
|
78
|
+
--app-bar-color: var(--md-sys-color-on-surface);
|
|
79
|
+
}
|
|
80
|
+
|
|
43
81
|
@media only screen and (max-width: 460px) {
|
|
44
82
|
body {
|
|
45
83
|
--button-container-margin: 0 -15px;
|
|
@@ -10,6 +10,7 @@ export declare class Sorting {
|
|
|
10
10
|
export declare class Pagination {
|
|
11
11
|
page?: number;
|
|
12
12
|
limit?: number;
|
|
13
|
+
after?: string;
|
|
13
14
|
}
|
|
14
15
|
export declare class Filter {
|
|
15
16
|
name: string;
|
|
@@ -19,6 +20,7 @@ export declare class Filter {
|
|
|
19
20
|
}
|
|
20
21
|
export declare class ListParam {
|
|
21
22
|
filters?: Filter[];
|
|
23
|
+
search?: string;
|
|
22
24
|
pagination?: Pagination;
|
|
23
25
|
sortings?: Sorting[];
|
|
24
26
|
inherited?: InheritedValueType;
|
|
@@ -39,6 +39,10 @@ tslib_1.__decorate([
|
|
|
39
39
|
(0, type_graphql_1.Field)(type => type_graphql_1.Int, { nullable: true, description: 'The maximum number of items per page.' }),
|
|
40
40
|
tslib_1.__metadata("design:type", Number)
|
|
41
41
|
], Pagination.prototype, "limit", void 0);
|
|
42
|
+
tslib_1.__decorate([
|
|
43
|
+
(0, type_graphql_1.Field)({ nullable: true, description: 'Keyset cursor: return rows positioned after this one, per the current sorting. Prefer this over page for feeds that grow at the head (event journals, logs, newest-first lists) where offset paging silently duplicates or skips rows as new rows arrive. Opaque — pass back the nextCursor the previous response returned. Wins over page when both are given.' }),
|
|
44
|
+
tslib_1.__metadata("design:type", String)
|
|
45
|
+
], Pagination.prototype, "after", void 0);
|
|
42
46
|
exports.Pagination = Pagination = tslib_1.__decorate([
|
|
43
47
|
(0, type_graphql_1.InputType)({ description: 'Input type for specifying pagination options in a list query.' })
|
|
44
48
|
], Pagination);
|
|
@@ -71,6 +75,10 @@ tslib_1.__decorate([
|
|
|
71
75
|
(0, type_graphql_1.Field)(type => [Filter], { nullable: true, description: 'An array of filter conditions to apply to the list query.' }),
|
|
72
76
|
tslib_1.__metadata("design:type", Array)
|
|
73
77
|
], ListParam.prototype, "filters", void 0);
|
|
78
|
+
tslib_1.__decorate([
|
|
79
|
+
(0, type_graphql_1.Field)({ nullable: true, description: 'A single free-text term matched across the fields the resolver declares as searchable (OR). Additive to filters: both are applied together. Prefer this over hand-assembling one search filter per field.' }),
|
|
80
|
+
tslib_1.__metadata("design:type", String)
|
|
81
|
+
], ListParam.prototype, "search", void 0);
|
|
74
82
|
tslib_1.__decorate([
|
|
75
83
|
(0, type_graphql_1.Field)(type => Pagination, { nullable: true, description: 'Pagination options for the list query.' }),
|
|
76
84
|
tslib_1.__metadata("design:type", Pagination)
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"list-param.js","sourceRoot":"","sources":["../../../server/service/common-types/list-param.ts"],"names":[],"mappings":";;;;AAAA,+CAAgF;AAEhF,mDAA2C;AAE3C,IAAY,kBAIX;AAJD,WAAY,kBAAkB;IAC5B,mCAAa,CAAA;IACb,mCAAa,CAAA;IACb,yCAAmB,CAAA;AACrB,CAAC,EAJW,kBAAkB,kCAAlB,kBAAkB,QAI7B;AAED,IAAA,+BAAgB,EAAC,kBAAkB,EAAE;IACnC,IAAI,EAAE,oBAAoB;IAC1B,WAAW,EACT,gIAAgI;CACnI,CAAC,CAAA;AAGK,IAAM,OAAO,GAAb,MAAM,OAAO;CAMnB,CAAA;AANY,0BAAO;AAElB;IADC,IAAA,oBAAK,EAAC,EAAE,WAAW,EAAE,wCAAwC,EAAE,CAAC;;qCACrD;AAGZ;IADC,IAAA,oBAAK,EAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,WAAW,EAAE,iEAAiE,EAAE,CAAC;;qCAC5F;kBALH,OAAO;IADnB,IAAA,wBAAS,EAAC,EAAE,WAAW,EAAE,4DAA4D,EAAE,CAAC;GAC5E,OAAO,CAMnB;AAGM,IAAM,UAAU,GAAhB,MAAM,UAAU;
|
|
1
|
+
{"version":3,"file":"list-param.js","sourceRoot":"","sources":["../../../server/service/common-types/list-param.ts"],"names":[],"mappings":"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","sourcesContent":["import { ArgsType, Field, InputType, Int, registerEnumType } from 'type-graphql'\n\nimport { ScalarAny } from './scalar-any.js'\n\nexport enum InheritedValueType {\n None = 'None',\n Only = 'Only',\n Include = 'Include'\n}\n\nregisterEnumType(InheritedValueType, {\n name: 'InheritedValueType',\n description:\n 'Enumeration for inherited value types: None, Only, or Include. Used to specify how values are inherited in queries or filters.'\n})\n\n@InputType({ description: 'Input type for specifying sorting options in a list query.' })\nexport class Sorting {\n @Field({ description: 'The field name to sort the results by.' })\n name: string\n\n @Field({ nullable: true, description: 'Set to true for descending order. Default is false (ascending).' })\n desc?: boolean\n}\n\n@InputType({ description: 'Input type for specifying pagination options in a list query.' })\nexport class Pagination {\n @Field(type => Int, { nullable: true, description: 'The page number to retrieve.' })\n page?: number\n\n @Field(type => Int, { nullable: true, description: 'The maximum number of items per page.' })\n limit?: number\n\n /*\n * 커서(keyset) 페이징 — `page` 대신 \"이 행 다음부터\" 를 준다.\n *\n * ── 왜 필요한가 (성능보다 먼저, 정확성) ─────────────────────────────────────\n * offset 페이징은 **목록이 정지해 있다고 가정한다.** 앞에 행이 새로 쌓이는 목록(이벤트 저널,\n * 알림, 로그, 최신순 게시물)에서는 1페이지를 읽는 사이 들어온 행 수만큼 뒤 페이지가 밀린다 →\n * **이미 본 행이 또 나오거나, 못 본 행이 통째로 건너뛰어진다.** 실시간 목록에서 offset 은\n * 느린 게 아니라 틀린다.\n * 덤으로 `OFFSET 900000` 은 DB 가 앞의 90만 행을 세고 버리므로 깊은 페이지에서 급격히 느려진다.\n *\n * `page` 는 그대로 둔다(하위호환). 둘 다 주면 `after` 가 이긴다.\n */\n @Field({ nullable: true, description: 'Keyset cursor: return rows positioned after this one, per the current sorting. Prefer this over page for feeds that grow at the head (event journals, logs, newest-first lists) where offset paging silently duplicates or skips rows as new rows arrive. Opaque — pass back the nextCursor the previous response returned. Wins over page when both are given.' })\n after?: string\n}\n\n@InputType({ description: 'Input type for specifying a filter condition in a list query.' })\nexport class Filter {\n @Field({ description: 'The field name to apply the filter on.' })\n name: string\n\n @Field({ description: 'The operator to use for filtering (e.g., eq, ne, gt, lt, in, etc.).' })\n operator: string\n\n @Field(type => ScalarAny, { nullable: true, description: 'The value to compare the field against.' })\n value?: any\n\n @Field({ nullable: true, description: 'Set to true if the filter applies to a relation field.' })\n relation?: boolean\n}\n\n@ArgsType()\nexport class ListParam {\n @Field(type => [Filter], { nullable: true, description: 'An array of filter conditions to apply to the list query.' })\n filters?: Filter[]\n\n /*\n * 전역 검색어 — 한 낱말로 여러 축을 한 번에 훑는다.\n *\n * 예전에는 이걸 표현할 자리가 없어서, 소비처마다 `searchables` 와 같은 필드 목록을 손으로 알아내\n * 필드 수만큼 `{name, operator:'search', value}` 를 조립해 보냈다(같은 조립이 수백 곳에 복제됐다).\n * 게다가 그 목록이 서버의 `searchables` 와 어긋나면 조용히 무시됐다 — 화면은 걸린 줄 아는데 안 걸린다.\n *\n * 이 필드를 주면 **서버가 자기 `searchables` 로** OR 를 만든다. 클라이언트는 무엇으로 찾을 수 있는지\n * 알 필요가 없다. 기존 `filters` 방식은 그대로 동작한다(둘을 함께 줘도 AND 로 합쳐진다).\n */\n @Field({ nullable: true, description: 'A single free-text term matched across the fields the resolver declares as searchable (OR). Additive to filters: both are applied together. Prefer this over hand-assembling one search filter per field.' })\n search?: string\n\n @Field(type => Pagination, { nullable: true, description: 'Pagination options for the list query.' })\n pagination?: Pagination\n\n @Field(type => [Sorting], { nullable: true, description: 'Sorting options for the list query.' })\n sortings?: Sorting[]\n\n @Field(type => InheritedValueType, { nullable: true, description: 'Inherited value type for the list query.' })\n inherited?: InheritedValueType\n}\n"]}
|