@simplysm/sd-claude 14.0.87 → 14.0.88
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/claude/references/sd-simplysm14/README.md +17 -18
- package/claude/references/sd-simplysm14/apis/angular/README.md +61 -0
- package/claude/references/sd-simplysm14/apis/angular/controls.md +119 -0
- package/claude/references/sd-simplysm14/apis/angular/crud.md +50 -0
- package/claude/references/sd-simplysm14/apis/angular/directives.md +44 -0
- package/claude/references/sd-simplysm14/apis/angular/features.md +55 -0
- package/claude/references/sd-simplysm14/apis/angular/infra.md +74 -0
- package/claude/references/sd-simplysm14/apis/angular/layout.md +55 -0
- package/claude/references/sd-simplysm14/apis/angular/overlay.md +115 -0
- package/claude/references/sd-simplysm14/apis/angular/routing-appstructure.md +64 -0
- package/claude/references/sd-simplysm14/apis/angular/selection-managers.md +43 -0
- package/claude/references/sd-simplysm14/apis/angular/shared-data.md +70 -0
- package/claude/references/sd-simplysm14/apis/angular/sheet.md +78 -0
- package/claude/references/sd-simplysm14/apis/capacitor-plugin-auto-update/README.md +80 -0
- package/claude/references/sd-simplysm14/apis/capacitor-plugin-file-system/README.md +66 -0
- package/claude/references/sd-simplysm14/apis/capacitor-plugin-intent/README.md +71 -0
- package/claude/references/sd-simplysm14/apis/capacitor-plugin-usb-storage/README.md +67 -0
- package/claude/references/sd-simplysm14/apis/core-browser/README.md +83 -0
- package/claude/references/sd-simplysm14/apis/core-browser/indexed-db.md +79 -0
- package/claude/references/sd-simplysm14/apis/core-common/README.md +138 -0
- package/claude/references/sd-simplysm14/apis/core-common/array-ext.md +72 -0
- package/claude/references/sd-simplysm14/apis/core-common/datetime.md +95 -0
- package/claude/references/sd-simplysm14/apis/core-common/json-transfer.md +47 -0
- package/claude/references/sd-simplysm14/apis/core-common/obj.md +53 -0
- package/claude/references/sd-simplysm14/apis/core-node/README.md +14 -0
- package/claude/references/sd-simplysm14/apis/core-node/consola.md +51 -0
- package/claude/references/sd-simplysm14/apis/core-node/cpx.md +39 -0
- package/claude/references/sd-simplysm14/apis/core-node/fs-watcher.md +38 -0
- package/claude/references/sd-simplysm14/apis/core-node/fsx.md +86 -0
- package/claude/references/sd-simplysm14/apis/core-node/pathx.md +42 -0
- package/claude/references/sd-simplysm14/apis/core-node/worker.md +54 -0
- package/claude/references/sd-simplysm14/apis/excel/README.md +43 -0
- package/claude/references/sd-simplysm14/apis/excel/cell.md +54 -0
- package/claude/references/sd-simplysm14/apis/excel/conditional-format.md +51 -0
- package/claude/references/sd-simplysm14/apis/excel/style.md +67 -0
- package/claude/references/sd-simplysm14/apis/excel/utils.md +35 -0
- package/claude/references/sd-simplysm14/apis/excel/workbook-worksheet.md +97 -0
- package/claude/references/sd-simplysm14/apis/excel/wrapper.md +83 -0
- package/claude/references/sd-simplysm14/apis/lint/README.md +43 -0
- package/claude/references/sd-simplysm14/apis/lint/rules.md +90 -0
- package/claude/references/sd-simplysm14/apis/orm-common/README.md +67 -0
- package/claude/references/sd-simplysm14/apis/orm-common/db-context.md +80 -0
- package/claude/references/sd-simplysm14/apis/orm-common/expr.md +113 -0
- package/claude/references/sd-simplysm14/apis/orm-common/query-builder.md +29 -0
- package/claude/references/sd-simplysm14/apis/orm-common/queryable.md +111 -0
- package/claude/references/sd-simplysm14/apis/orm-common/schema.md +162 -0
- package/claude/references/sd-simplysm14/apis/orm-common/types.md +52 -0
- package/claude/references/sd-simplysm14/apis/orm-node/README.md +53 -0
- package/claude/references/sd-simplysm14/apis/orm-node/db-conn.md +94 -0
- package/claude/references/sd-simplysm14/apis/sd-cli/README.md +29 -0
- package/claude/references/sd-simplysm14/apis/sd-cli/SdTsCompiler.md +70 -0
- package/claude/references/sd-simplysm14/apis/sd-cli/sd-config-types.md +173 -0
- package/claude/references/sd-simplysm14/apis/service-client/README.md +152 -0
- package/claude/references/sd-simplysm14/apis/service-client/orm.md +45 -0
- package/claude/references/sd-simplysm14/apis/service-client/transport.md +36 -0
- package/claude/references/sd-simplysm14/apis/service-common/README.md +70 -0
- package/claude/references/sd-simplysm14/apis/service-common/app-structure.md +48 -0
- package/claude/references/sd-simplysm14/apis/service-common/protocol.md +72 -0
- package/claude/references/sd-simplysm14/apis/service-server/README.md +102 -0
- package/claude/references/sd-simplysm14/apis/service-server/service-authoring.md +74 -0
- package/claude/references/sd-simplysm14/apis/service-server/transport-internals.md +51 -0
- package/claude/references/sd-simplysm14/apis/service-server/v1-legacy.md +50 -0
- package/claude/references/sd-simplysm14/apis/storage/README.md +114 -0
- package/claude/skills/sd-docs/SKILL.md +17 -29
- package/claude/skills/sd-docs/references/{subagent-prompt.md → doc-rules.md} +25 -40
- package/package.json +1 -1
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
# @simplysm/angular — 모달·토스트·Busy·인쇄 (오버레이)
|
|
2
|
+
|
|
3
|
+
프로그래밍 방식으로 동적 컴포넌트를 body 에 부착하는 루트 프로바이더 4종(모달/토스트/Busy/인쇄)과 콘텐츠 컴포넌트가 구현할 인터페이스. 모두 `createComponent` + `ApplicationRef.attachView` 로 동작.
|
|
4
|
+
|
|
5
|
+
## SdModalProvider
|
|
6
|
+
|
|
7
|
+
`class SdModalProvider` — 모달을 프로그래밍 방식으로 띄우는 루트 프로바이더.
|
|
8
|
+
|
|
9
|
+
- `modalCount: Signal<number>` — 현재 열린 모달 수.
|
|
10
|
+
- `showAsync<T>(modal: SdModalInfo<T>, options?: SdModalOptions): Promise<결과 | undefined>` — 모달 표시. 콘텐츠 컴포넌트의 `close` emit 값으로 resolve, 배경/ESC/닫기버튼이면 `undefined`. 닫힘 애니메이션·포커스 복원·z-index 스택 처리.
|
|
11
|
+
|
|
12
|
+
`SdModalInfo<T, X>`:
|
|
13
|
+
- `title: string` — 모달 헤더 제목.
|
|
14
|
+
- `type: Type<T>` — 콘텐츠 컴포넌트 클래스(`SdModalContentDef` 구현).
|
|
15
|
+
- `inputs` — 콘텐츠 컴포넌트의 input 값 객체(`initialized`/`close`/`actionTplRef` 제외, `_optionalModalInputs` 로 지정된 키는 optional).
|
|
16
|
+
|
|
17
|
+
`SdModalOptions`(모두 optional):
|
|
18
|
+
- `key` — 설정 키. 지정 시 크기·위치를 `SdSystemConfigProvider` 에 영속화.
|
|
19
|
+
- `hideHeader` — 헤더 전체 숨김.
|
|
20
|
+
- `hideCloseButton` — 닫기(X) 버튼만 숨김.
|
|
21
|
+
- `headerStyle` — 헤더 인라인 스타일.
|
|
22
|
+
- `useCloseByBackdrop` — 배경 클릭으로 닫기 허용. 기본 true.
|
|
23
|
+
- `useCloseByEscapeKey` — ESC 로 닫기 허용. 기본 true.
|
|
24
|
+
- `float` — 배경 없는 플로팅 모달(다른 조작 가능).
|
|
25
|
+
- `fill` — 전체화면 채움.
|
|
26
|
+
- `resizable` — 모서리/변 드래그 리사이즈.
|
|
27
|
+
- `movable` — 헤더 드래그 이동.
|
|
28
|
+
- `position` — `"bottom-right"|"top-right"`. 고정 위치.
|
|
29
|
+
- `minHeightPx`/`minWidthPx`/`heightPx`/`widthPx` — 크기 px.
|
|
30
|
+
- `noFirstControlFocusing` — 첫 입력요소 자동 포커스 끔(true 면 dialog 자체 포커스).
|
|
31
|
+
|
|
32
|
+
`SdModalContentDef<O>`(콘텐츠 컴포넌트 구현 인터페이스):
|
|
33
|
+
- `initialized: Signal<boolean>` — 초기화 완료 신호.
|
|
34
|
+
- `close: OutputEmitterRef<O | undefined>` — 결과 emit 시 모달 닫힘.
|
|
35
|
+
- `actionTplRef?: TemplateRef<any>` — 헤더 우측 액션 영역 템플릿.
|
|
36
|
+
- `_optionalModalInputs?: string` — optional 처리할 input 키(타입 마커).
|
|
37
|
+
|
|
38
|
+
```typescript
|
|
39
|
+
const result = await sdModal.showAsync(
|
|
40
|
+
{ title: "사용자 선택", type: UserSelectModal, inputs: { deptId } },
|
|
41
|
+
{ resizable: true, key: "user-select" },
|
|
42
|
+
);
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
## SdModal
|
|
46
|
+
|
|
47
|
+
`<sd-modal>` — 모달 셸 컴포넌트. 보통 `SdModalProvider` 가 내부 생성하지만 선언적으로도 사용 가능.
|
|
48
|
+
|
|
49
|
+
- `open = model(false)` — 열림 여부 양방향.
|
|
50
|
+
- `title`/`hideHeader`/`hideCloseButton`/`headerStyle`/`useCloseByBackdrop`/`useCloseByEscapeKey`/`float`/`fill`/`resizable`/`movable`/`position`/`minHeightPx`/`minWidthPx`/`heightPx`/`widthPx`/`actionTplRef` — `SdModalOptions` 와 동일 의미의 input.
|
|
51
|
+
- `key` — 크기·위치 영속화 키.
|
|
52
|
+
- `closeRequest = output<void>()` — 배경/ESC/닫기버튼으로 닫기 요청 시 emit.
|
|
53
|
+
|
|
54
|
+
## SdActivatedModalProvider
|
|
55
|
+
|
|
56
|
+
`class SdActivatedModalProvider<T>` — 모달 콘텐츠 내부에서 inject 해 모달 컨텍스트에 접근.
|
|
57
|
+
|
|
58
|
+
- `modalComponent: Signal<SdModal | undefined>` — 부모 `SdModal` 인스턴스.
|
|
59
|
+
- `contentComponent: Signal<T | undefined>` — 콘텐츠 컴포넌트 인스턴스.
|
|
60
|
+
- `canDeactivateFn: () => boolean` — 닫기 차단 함수(true 면 닫힘 허용). `setupCanDeactivate` 가 설정.
|
|
61
|
+
|
|
62
|
+
## SdPromptModal / SdConfirmModal
|
|
63
|
+
|
|
64
|
+
내장 범용 모달 콘텐츠. `SdModalProvider.showAsync` 의 `type` 으로 사용.
|
|
65
|
+
|
|
66
|
+
- `SdPromptModal` — `SdModalContentDef<string>`. `message: input.required<string>()` 표시 후 텍스트 입력. 확인 시 입력값 emit(필수 검증), 취소 시 `undefined`.
|
|
67
|
+
- `SdConfirmModal` — `SdModalContentDef<boolean>`. `message` 표시 후 확인 시 `true`, 취소 시 `undefined` emit.
|
|
68
|
+
|
|
69
|
+
## SdToastProvider
|
|
70
|
+
|
|
71
|
+
`class SdToastProvider` — 토스트 알림 루트 프로바이더.
|
|
72
|
+
|
|
73
|
+
- `alertThemes: WritableSignal<SdToastSeverity[]>` — 이 심각도는 토스트 대신 `window.alert` 로 표시.
|
|
74
|
+
- `overlap: WritableSignal<boolean>` — true 면 새 토스트가 기존 토스트를 모두 치우고 겹쳐 표시.
|
|
75
|
+
- `beforeShowFn?: (theme) => void` — 토스트 표시 직전 콜백.
|
|
76
|
+
- `info/success/warning/danger(message, useProgress?)` — 심각도별 토스트. `useProgress: true` 면 `WritableSignal<number>`(0~100) 반환(100 도달 1초 후 자동 해제), 아니면 3초 후 자동 해제(호버 시 지연).
|
|
77
|
+
- `notify<T>(input: SdToastInput<T>): Promise<결과|undefined>` — 커스텀 컴포넌트 토스트. `close` emit 값으로 resolve, 5초 후 자동 `undefined`.
|
|
78
|
+
- `try<R>(fn, messageFn?): Promise<R|undefined>` — `fn` 실행, `Error` 발생 시 `danger` 토스트 + 시스템로그 후 `undefined` 반환(비-Error 는 rethrow). `messageFn` 으로 메시지 커스터마이즈.
|
|
79
|
+
|
|
80
|
+
타입:
|
|
81
|
+
- `SdToastSeverity` — `"info"|"success"|"warning"|"danger"`.
|
|
82
|
+
- `SdToastTheme` — `"primary"|"secondary"|SdToastSeverity|"gray"|"blue-gray"`.
|
|
83
|
+
- `SdToastContentDef<O>` — `{ close: OutputEmitterRef<O|undefined> }`. notify 콘텐츠 구현.
|
|
84
|
+
- `SdToastInput<T>` — `{ type: Type<T>; inputs }`(`close` 제외 input).
|
|
85
|
+
|
|
86
|
+
```typescript
|
|
87
|
+
sdToast.danger("저장 실패");
|
|
88
|
+
const progress = sdToast.info("업로드 중", true);
|
|
89
|
+
progress.set(50);
|
|
90
|
+
await sdToast.try(() => api.saveAsync());
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
## SdToast / SdToastContainer
|
|
94
|
+
|
|
95
|
+
- `<sd-toast>` — 개별 토스트. `open`/`progress`/`message` 는 `model`, `useProgress`/`theme` 는 input. theme 에 따라 `role`/`aria-live`(info·success=polite/status, warning·danger=assertive/alert) 자동.
|
|
96
|
+
- `<sd-toast-container>` — 토스트 컨테이너. `overlap = input(false)` — 겹침 모드.
|
|
97
|
+
|
|
98
|
+
## SdBusyProvider / SdBusyContainer
|
|
99
|
+
|
|
100
|
+
전역·지역 로딩 표시.
|
|
101
|
+
|
|
102
|
+
- `SdBusyProvider` — `type: WritableSignal<SdBusyType>` 기본 스피너 종류, `globalBusyCount: WritableSignal<number>` 0 초과 시 전역 busy 오버레이 표시. 라우팅·인쇄가 자동 증감.
|
|
103
|
+
- `SdBusyType` — `"spinner"|"bar"|"cube"`. 인디케이터 모양.
|
|
104
|
+
- `<sd-busy-container>` — 지역 busy 래퍼. `busy = input(false)` 표시 여부, `message`/`type`(미지정 시 프로바이더 type 따름)/`progressPercent`(0~100 막대) input. busy 중 키입력 차단.
|
|
105
|
+
|
|
106
|
+
## SdPrintProvider
|
|
107
|
+
|
|
108
|
+
`class SdPrintProvider` — 컴포넌트를 인쇄하거나 PDF 버퍼로 만드는 루트 프로바이더. 진행 중 전역 busy 증가.
|
|
109
|
+
|
|
110
|
+
- `printAsync<T>(template: SdPrintInput<T>, options?: { size?: string; margin?: string }): Promise<void>` — 템플릿 컴포넌트를 body 에 부착·`@page` 스타일 주입 후 `window.print()`. 이미지 로드 대기.
|
|
111
|
+
- `getPdfBufferAsync<T>(template, options?: { orientation?: "portrait"|"landscape"; pageSize?: string }): Promise<Uint8Array>` — `.page` 요소(없으면 전체)를 캔버스화해 jsPDF 로 PDF 버퍼 생성.
|
|
112
|
+
|
|
113
|
+
타입:
|
|
114
|
+
- `SdPrint` — `{ initialized: Signal<boolean>; _optionalPrintInputs?: string }`. 인쇄 콘텐츠 구현.
|
|
115
|
+
- `SdPrintInput<T, X>` — `{ type: Type<T>; inputs }`(`_optionalPrintInputs` 키는 optional).
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
# @simplysm/angular — 라우팅 / 앱 구조(메뉴·권한)
|
|
2
|
+
|
|
3
|
+
해시 기반 SPA 내비게이션, 현재 페이지 코드·뷰타입·제목 신호, 앱 구조(AppStructureItem 트리)→메뉴/권한 변환, 권한 테이블 UI. 메뉴 컴포넌트(layout 군)·CRUD 컨테이너(crud 군)가 이 신호·타입을 소비.
|
|
4
|
+
|
|
5
|
+
## SdNavigateWindowProvider
|
|
6
|
+
|
|
7
|
+
`class SdNavigateWindowProvider` — 해시 라우트를 새 창/탭으로 여는 루트 프로바이더.
|
|
8
|
+
|
|
9
|
+
- `isWindow: boolean` — 현재가 별도 창 모드인지(해시 쿼리 `window=true`).
|
|
10
|
+
- `open(navigate, params?, features?)` — 라우트 열기. 창 모드이거나 `features` 있으면 `window.open` 으로 새 창(부모 unload 시 자동 닫힘), 아니면 새 탭(`_blank`).
|
|
11
|
+
|
|
12
|
+
## SdRouterLink
|
|
13
|
+
|
|
14
|
+
`[sdRouterLink]` 디렉티브 — 클릭 시 라우팅. Ctrl/Shift 클릭은 새 창, 창 모드는 팝업으로 분기.
|
|
15
|
+
|
|
16
|
+
- `option = input<{ link; params?; window?: { width?; height? }; outletName?; queryParams? } | undefined>()`(alias `sdRouterLink`) — 이동 정보. `link`(경로), `params`(matrix 파라미터), `queryParams`, `outletName`(보조 outlet), `window`(팝업 크기). `undefined` 면 비활성.
|
|
17
|
+
|
|
18
|
+
## 페이지 코드·타이틀·뷰타입 신호
|
|
19
|
+
|
|
20
|
+
컴포넌트 생성자/필드에서 호출하는 inject 함수. 라우터·활성모달 컨텍스트에서 값 도출.
|
|
21
|
+
|
|
22
|
+
- `injectFullPageCodeSignal(): Signal<string>` — 전체 라우트 경로를 `.` 연결 페이지 코드로 변환(예: `sale.order`). 메뉴 선택표시·제목 조회 기준.
|
|
23
|
+
- `injectCurrentPageCodeSignal(): Signal<string> | undefined` — 현재 활성 라우트 세그먼트 기준 코드(중첩 라우트용). 라우트 없으면 `undefined`.
|
|
24
|
+
- `injectViewTitleSignal(): Signal<string>` — 화면 제목. 활성 모달이면 모달 제목, 아니면 앱 구조에서 코드로 제목 조회.
|
|
25
|
+
- `injectViewTypeSignal(): Signal<SdViewType>` — `"page"|"modal"|"control"`. 모달 컨텍스트면 modal, 라우트 페이지 컴포넌트면 page, 그 외 control. `SdViewType` 타입 동봉.
|
|
26
|
+
- `setupCanDeactivate(fn: () => boolean): void` — 이탈 차단 등록. 모달이면 `canDeactivateFn`, 라우트면 route 의 `canDeactivate` 가드에 추가(파괴 시 해제). `fn()` 이 false 면 닫기/이탈 차단.
|
|
27
|
+
|
|
28
|
+
## 메뉴 유틸
|
|
29
|
+
|
|
30
|
+
`SdMenu` 를 라우터링크/선택판정으로 변환(layout 메뉴 컴포넌트가 사용).
|
|
31
|
+
|
|
32
|
+
- `getMenuRouterLinkOption(menu: SdMenu): { link; queryParams } | undefined` — 리프 메뉴면 `/home/{codeChain}` 링크 + 쿼리파라미터, 그룹/외부url 메뉴면 `undefined`.
|
|
33
|
+
- `getIsMenuSelected(menu, fullPageCode, customFn?): boolean` — 선택 여부. `customFn` 있으면 그 결과, 없으면 `fullPageCode === codeChain.join(".")`.
|
|
34
|
+
|
|
35
|
+
## SdAppStructureProvider
|
|
36
|
+
|
|
37
|
+
`class SdAppStructureProvider<TModule>` — 앱 구조(메뉴·권한) 트리 보관·계산 루트 프로바이더.
|
|
38
|
+
|
|
39
|
+
- `usableModules: WritableSignal<TModule[] | undefined>` — 활성 모듈 목록(메뉴/권한 필터링 기준).
|
|
40
|
+
- `permRecord: WritableSignal<Record<string, boolean> | undefined>` — 권한 레코드(`{코드.use: bool}`).
|
|
41
|
+
- `items: WritableSignal<AppStructureItem<TModule>[]>` — 원본 구조. `initialize(items)` 로 설정.
|
|
42
|
+
- `usableMenus: Signal<SdMenu[]>` — 모듈·권한 적용된 메뉴 트리.
|
|
43
|
+
- `usableFlatMenus: Signal<SdFlatMenu<TModule>[]>` — 평탄화 메뉴 목록.
|
|
44
|
+
- `getPermissionsByStructure(items, codeChain?)` / `getTitleByFullCode(fullCode)`(없으면 throw) / `findTitleByFullCode(fullCode)`(없으면 undefined, 결측 보존) / `getItemChainByFullCode(fullCode)` / `getPermsByFullCode(fullCodes, permKeys)`.
|
|
45
|
+
|
|
46
|
+
`injectPermsSignal<K>(viewCodes: string[], keys: K[]): Signal<K[]>` — 특정 화면코드들의 권한 중 보유 키만 반환하는 computed.
|
|
47
|
+
|
|
48
|
+
## SdAppStructureUtils
|
|
49
|
+
|
|
50
|
+
`abstract class SdAppStructureUtils` — 앱 구조 변환 정적 유틸(프로바이더가 위임). `getMenus`/`getFlatMenus`/`getPermissions`/`getFlatPermissions`/`getTitleByFullCode`/`findTitleByFullCode`/`getItemChainByFullCode`/`getPermsByFullCode` 정적 메서드 제공. 모듈 활성/권한 use 여부로 필터링.
|
|
51
|
+
|
|
52
|
+
## 타입
|
|
53
|
+
|
|
54
|
+
- `SdMenu` — 메뉴 노드. `title`/`codeChain: string[]`/`url?`/`icon?`/`children?`.
|
|
55
|
+
- `SdFlatMenu<TModule>` — 평탄 메뉴. `titleChain`/`codeChain`/`modulesChain`.
|
|
56
|
+
- `SdPermission<TModule>` — 권한 노드. `title`/`codeChain`/`modules`/`perms: ("use"|"edit")[] | undefined`/`children`.
|
|
57
|
+
|
|
58
|
+
## SdPermissionTable
|
|
59
|
+
|
|
60
|
+
`<sd-permission-table>` — 권한 트리 체크박스 표(use/edit 토글, 상하위 연동). 제네릭 `<TModule>`.
|
|
61
|
+
|
|
62
|
+
- `value = model<Record<string, boolean>>({})` — 권한 체크 상태(`{코드.use|edit: bool}`). use 해제 시 edit 자동 해제, edit 은 use 체크 후에만.
|
|
63
|
+
- `items = input<SdPermission<TModule>[]>([])` — 권한 트리(`getPermissionsByStructure` 결과).
|
|
64
|
+
- `disabled = input(false)` — 전체 비활성.
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
# @simplysm/angular — 선택·정렬·확장 매니저
|
|
2
|
+
|
|
3
|
+
리스트/시트류 컴포넌트 내부에서 signal 바인딩으로 선택·다중정렬·트리확장 상태를 다루는 컴포저블 함수. 컴포넌트 클래스 필드 초기화 시 호출하며, signal 입력(items/selectedKeys/sorts 등)과 메서드 묶음을 반환. `sd-sheet` 가 셋 모두 사용.
|
|
4
|
+
|
|
5
|
+
## useSelectionManager
|
|
6
|
+
|
|
7
|
+
`useSelectionManager<TItem, TKey>(options): { ... }` — 단일/다중 선택 상태 관리.
|
|
8
|
+
|
|
9
|
+
options(모두 Signal/WritableSignal):
|
|
10
|
+
- `displayItems: Signal<TItem[]>` — 현재 표시 항목.
|
|
11
|
+
- `selectedKeys: WritableSignal<TKey[]>` — 선택 키 배열(양방향).
|
|
12
|
+
- `selectMode: Signal<"single"|"multi"|undefined>` — 선택 모드. `undefined` 면 선택 비활성.
|
|
13
|
+
- `getItemSelectableFn: Signal<((item) => boolean | string) | undefined>` — 선택 가능 판정. `string` 이면 비활성 + 사유.
|
|
14
|
+
- `trackByFn: Signal<(item, index) => TKey>` — 키 추출.
|
|
15
|
+
|
|
16
|
+
반환: `hasSelectable`/`isAllSelected: Signal<boolean>`, `getSelectable(item): true | string | undefined`, `getCanChangeFn(item): () => boolean`, `select`/`deselect`/`toggle`/`toggleAll`/`isSelected(item)`. single 모드 select 는 단일 키로 교체, multi 는 추가/제거.
|
|
17
|
+
|
|
18
|
+
## useSortingManager
|
|
19
|
+
|
|
20
|
+
`useSortingManager(options: { sorts: WritableSignal<SortingDef[]> }): { ... }` — 다중 정렬 토글·적용.
|
|
21
|
+
|
|
22
|
+
- `options.sorts` — 정렬 정의 배열(양방향). `SortingDef = { key: string; desc: boolean }`.
|
|
23
|
+
- 반환:
|
|
24
|
+
- `defMap: Signal<Map<key, { indexText?; desc }>>` — 컬럼별 정렬 상태(다중 시 순번 indexText).
|
|
25
|
+
- `toggle(key, multiple)` — 정렬 토글. `multiple=true`(Shift) 면 누적, 동일 키 재클릭 시 asc→desc→해제 순환.
|
|
26
|
+
- `sort<T>(items): T[]` — 현재 정렬 정의로 배열 정렬(null 은 먼저, 문자열은 localeCompare).
|
|
27
|
+
|
|
28
|
+
## useExpandingManager
|
|
29
|
+
|
|
30
|
+
`useExpandingManager<T>(binding): { ... }` — 트리 펼침/접힘 상태 관리.
|
|
31
|
+
|
|
32
|
+
binding:
|
|
33
|
+
- `items: Signal<T[]>` — 루트 항목.
|
|
34
|
+
- `expandedItems: WritableSignal<T[]>` — 펼친 항목(양방향).
|
|
35
|
+
- `getChildrenFn: Signal<((item, index) => T[] | undefined) | undefined>` — 자식 추출.
|
|
36
|
+
- `sort: (items) => T[]` — 각 레벨 정렬 함수.
|
|
37
|
+
- 반환:
|
|
38
|
+
- `displayItems: Signal<T[]>` — 펼침 반영된 평탄 표시 목록.
|
|
39
|
+
- `hasExpandable`/`isAllExpanded: Signal<boolean>`.
|
|
40
|
+
- `toggle(item)`/`toggleAll()` — 펼침 토글.
|
|
41
|
+
- `isVisible(item): boolean` — 모든 조상이 펼쳐졌는지.
|
|
42
|
+
- `def(item): ExpandItemDef<T>` — 항목 메타(없으면 throw).
|
|
43
|
+
- `ExpandItemDef<T>` — `{ item; parentDef?; hasChildren; depth }`.
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
# @simplysm/angular — 공유 데이터(shared-data)
|
|
2
|
+
|
|
3
|
+
서버 마스터데이터(코드·분류 등)를 클라이언트에 캐시하고 서버 변경 이벤트로 동기화하며, 선택 UI 로 노출. `SdSharedDataProvider` 를 상속해 데이터셋을 등록하고, 선택 컨트롤(select/select-button/select-list)이 그 핸들의 items 를 소비.
|
|
4
|
+
|
|
5
|
+
## SdSharedDataProvider
|
|
6
|
+
|
|
7
|
+
`abstract class SdSharedDataProvider<T extends Record<string, SharedDataBase<...>>>` — 공유 데이터 등록·로드·동기화 추상 프로바이더. 앱별로 상속해 `initialize()` 에서 `register` 호출.
|
|
8
|
+
|
|
9
|
+
- `loadingCount: WritableSignal<number>` — 로딩 중 데이터 수.
|
|
10
|
+
- `abstract initialize(): void` — 앱이 구현. 보통 여러 `register` 호출.
|
|
11
|
+
- `register<K>(name, info: SharedDataInfo<T[K]>)` — 데이터셋 등록(재호출 시 갱신·리스너 재설정).
|
|
12
|
+
- `getHandle<K>(name): SharedDataHandle<T[K]>` — 핸들 반환. 최초 접근 시 lazy 로드·이벤트 리스너 등록. 미등록이면 throw.
|
|
13
|
+
- `emitAsync<K>(name, changeKeys?)` — 변경 이벤트 발행. `changeKeys` 없으면 전체 리로드 신호, 있으면 해당 키만 부분 갱신.
|
|
14
|
+
- `wait(): Promise<void>` — `loadingCount<=0` 까지 대기(CRUD 기반 컨테이너가 초기 로드 동기화에 사용).
|
|
15
|
+
|
|
16
|
+
타입:
|
|
17
|
+
- `SharedDataBase<TKey>` — 공유 항목 기본형. `__valueKey`(고유키), `__searchText`(검색대상), `__isHidden`(숨김), `__parentKey?`(트리 부모키).
|
|
18
|
+
- `SharedDataInfo<T>` — 등록 정보. `serviceKey`(서비스 클라이언트 키), `getter: (changeKeys?) => Promise<T[]>`(전체/부분 로더), `filter?`(이벤트 필터), `orderBy?`(부분 갱신 후 정렬 키).
|
|
19
|
+
- `SharedDataHandle<T>` — `{ items: Signal<T[]>; get(key) => T | undefined }`.
|
|
20
|
+
- `SdSharedDataChangeEvent` — 변경 동기화에 쓰는 `defineEvent` 정의(서버↔클라이언트).
|
|
21
|
+
|
|
22
|
+
## SdSharedDataSelect
|
|
23
|
+
|
|
24
|
+
`<sd-shared-data-select>` — 공유데이터 드롭다운 선택(검색·트리·모달 연계). 제네릭 `<TItem, TMode, TModal>`.
|
|
25
|
+
|
|
26
|
+
- `value = model<...>()` — 선택 키(single=단일, multi=배열). 값 타입은 `TItem["__valueKey"] | undefined`.
|
|
27
|
+
- `items = input.required<TItem[]>()` — 공유 항목 배열(핸들의 items).
|
|
28
|
+
- `selectMode: TMode("single"|"multi")` — 선택 모드. 기본 `"single"`.
|
|
29
|
+
- `disabled`/`required`/`inset`/`inline`/`size` — 공통.
|
|
30
|
+
- `useUndefined` — multi 에서도 "미지정" 항목 노출.
|
|
31
|
+
- `filterFn?: (item, index, ...params) => boolean` / `filterFnParams?: any[]` — 추가 필터.
|
|
32
|
+
- `modal?: SdSelectModalInfo<TModal>` — 검색 아이콘 → 선택 모달.
|
|
33
|
+
- `editModal?: SdModalInfo<SdModalContentDef<boolean>>` — 편집 아이콘 → 편집 모달.
|
|
34
|
+
- `selectClass`/`multiSelectionDisplayDirection: "vertical"` — 표시 옵션.
|
|
35
|
+
- `getIsHiddenFn?: (item, index) => boolean` — 숨김 판정(기본 `__isHidden`, 숨김 항목은 취소선).
|
|
36
|
+
- `getSearchTextFn?: (item, index) => string` — 검색 대상 텍스트(기본 `__searchText`).
|
|
37
|
+
- `displayOrderByFn?: (item) => 정렬키` — 표시 정렬.
|
|
38
|
+
- 항목 템플릿은 `<ng-template [itemOf]>`, 미지정 표시는 `#undefinedTpl`. `__parentKey` 있으면 트리.
|
|
39
|
+
|
|
40
|
+
## SdSharedDataSelectButton
|
|
41
|
+
|
|
42
|
+
`<sd-shared-data-select-button>` — 모달 선택 버튼 + 선택값 인라인 표시. 제네릭 `<TItem, TMode, TModal>`.
|
|
43
|
+
|
|
44
|
+
- `value = model<...>()` — 선택 키(single/multi).
|
|
45
|
+
- `items = input<TItem[]>([])` — 표시 라벨 조회용 전체 항목.
|
|
46
|
+
- `modal = input.required<SdSelectModalInfo<TModal>>()` — 선택 모달.
|
|
47
|
+
- `selectMode: TMode` — 기본 `"single"`.
|
|
48
|
+
- `disabled`/`required`/`inset`/`size` — 공통.
|
|
49
|
+
- 선택 항목 라벨은 `<ng-template [itemOf]>`(required)로 렌더.
|
|
50
|
+
|
|
51
|
+
## SdSharedDataSelectList
|
|
52
|
+
|
|
53
|
+
`<sd-shared-data-select-list>` — 검색·페이징되는 리스트형 단일 선택(패널 영역용). 제네릭 `<TItem, TModal>`.
|
|
54
|
+
|
|
55
|
+
- `selectedItem = model<TItem>()` — 선택 항목(객체). `canChangeFn?: (item) => boolean|Promise<boolean>` 로 변경 차단.
|
|
56
|
+
- `items = input.required<TItem[]>()` — 항목 배열. `__isHidden` 항목은 제외.
|
|
57
|
+
- `selectedIcon?` — 선택 표시 아이콘.
|
|
58
|
+
- `useUndefined` — "미지정" 항목 표시.
|
|
59
|
+
- `filterFn?: (item, index) => boolean` — 추가 필터.
|
|
60
|
+
- `modal?: SdSelectModalInfo<TModal>` — 외부 검색 모달 버튼.
|
|
61
|
+
- `header?` — 패널 헤더 텍스트.
|
|
62
|
+
- `pageItemCount?` — 페이지당 항목 수(지정 시 페이지네이션).
|
|
63
|
+
- 슬롯: `#headerTpl`/`#filterTpl`(기본 검색 textfield 대체)/`[itemOf]` 항목/`#undefinedTpl`.
|
|
64
|
+
|
|
65
|
+
## matchesSearchText
|
|
66
|
+
|
|
67
|
+
`matchesSearchText(itemText: string, searchQuery: string | undefined): boolean` — 공백 분리 AND 부분일치 검색 매처(소문자 비교). 위 선택 컨트롤이 검색에 사용.
|
|
68
|
+
|
|
69
|
+
- `itemText` — 항목의 검색 대상 텍스트.
|
|
70
|
+
- `searchQuery` — 검색어. 빈/`undefined` 면 모두 매치(true). 각 단어가 모두 포함돼야 매치.
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
# @simplysm/angular — 시트 (sd-sheet)
|
|
2
|
+
|
|
3
|
+
선택·다중정렬·트리확장·컬럼 고정/숨김/리사이즈·페이지네이션·셀편집·설정 영속화를 갖춘 데이터 그리드. 컬럼은 `<sd-sheet-column>` 디렉티브 + `<ng-template [cell]>` 셀 템플릿으로 선언. 선택/정렬/확장은 selection-managers 컴포저블을 내부 사용.
|
|
4
|
+
|
|
5
|
+
## SdSheet
|
|
6
|
+
|
|
7
|
+
`<sd-sheet>` — 데이터 그리드. 제네릭 `<TItem>`.
|
|
8
|
+
|
|
9
|
+
inputs:
|
|
10
|
+
- `key = input<string>()` — 설정 영속화 키. 지정 시 설정 버튼·컬럼폭/순서/고정/숨김을 `SdSystemConfigProvider` 에 저장.
|
|
11
|
+
- `items = input<TItem[]>([])` — 표시 데이터.
|
|
12
|
+
- `trackByFn = input<(item, index) => unknown>` — 행 키 추출. 기본 `(item) => item`. 선택키·정렬 추적 기준.
|
|
13
|
+
- `selectMode: "single"|"multi"` — 선택 모드. 미지정 시 선택 비활성. multi 면 헤더 전체선택 체크박스.
|
|
14
|
+
- `autoSelect: "click"|"focus"` — 자동 선택 트리거. `"click"` = 행/셀 클릭 시 선택, `"focus"` = 셀 포커스 시 선택. 키보드 위주 화면이면 `"focus"`.
|
|
15
|
+
- `getItemSelectableFn?: (item) => boolean | string` — 행 선택 가능 여부. `string` 반환 시 비활성 + 그 문자열을 툴팁 사유로 표시.
|
|
16
|
+
- `getChildrenFn?: (item, index) => TItem[] | undefined` — 트리 자식. 지정 시 확장 토글 컬럼 노출.
|
|
17
|
+
- `useAutoSort = input(false)` — 클라이언트 정렬. true 면 `sorts` 변경 시 시트가 직접 `items` 정렬. 서버측 정렬/페이징이면 false 로 두고 외부에서 재조회.
|
|
18
|
+
- `visiblePageCount = input(10)`/`totalPageCount = input(0)`/`itemsPerPage = input(0)` — 페이지네이션. `totalPageCount>0` 면 서버 페이징, `itemsPerPage>0` 면 클라이언트 페이징.
|
|
19
|
+
- `focusMode: "row"|"cell"` — 키보드 포커스 단위. `"row"` = 행 강조만, `"cell"` = 셀 단위 포커스/편집. 기본 `"cell"`.
|
|
20
|
+
- `inset = input(false)` — 테두리 없는 삽입형.
|
|
21
|
+
- `contentStyle` — 스크롤 컨테이너 인라인 스타일.
|
|
22
|
+
- `getItemCellClassFn?: (item, colKey) => string` / `getItemCellStyleFn?: (item, colKey) => string | undefined` — 셀별 동적 클래스/스타일.
|
|
23
|
+
- `hideConfigBar = input(false)` — 상단 설정/페이지 바 숨김.
|
|
24
|
+
- `columnControlsInput = input<readonly SdSheetColumn[]>([])` — 투영 외 추가 컬럼 컨트롤(예: CRUD 목록이 삭제 컬럼 주입).
|
|
25
|
+
|
|
26
|
+
outputs:
|
|
27
|
+
- `itemKeydown = output<SdSheetItemKeydownEventParam<TItem>>()` — 행에서 키 입력(`{ item, event }`).
|
|
28
|
+
- `cellKeydown = output<SdSheetCellKeydownEventParam<TItem>>()` — 셀에서 키 입력(`{ item, key, event }`, key=컬럼키).
|
|
29
|
+
|
|
30
|
+
models(양방향):
|
|
31
|
+
- `selectedKeys = model<unknown[]>([])` — 선택 행 키 배열.
|
|
32
|
+
- `expandedItems = model<TItem[]>([])` — 펼친 트리 항목.
|
|
33
|
+
- `sorts = model<SortingDef[]>([])` — 정렬 정의(`{ key, desc }[]`). 헤더 클릭으로 토글(Shift=다중).
|
|
34
|
+
- `currentPage = model(0)` — 현재 페이지(0-base).
|
|
35
|
+
|
|
36
|
+
## SdSheetColumn
|
|
37
|
+
|
|
38
|
+
`<sd-sheet-column>` 디렉티브 — 컬럼 정의. 제네릭 `<T>`.
|
|
39
|
+
|
|
40
|
+
- `key = input.required<string>()` — 컬럼 식별키(설정 저장·정렬·이벤트 기준).
|
|
41
|
+
- `header: string | string[]` — 헤더 텍스트. 배열이면 다단(그룹) 헤더.
|
|
42
|
+
- `headerStyle`/`tooltip` — 헤더 스타일·툴팁.
|
|
43
|
+
- `width` — 컬럼 폭(CSS 값).
|
|
44
|
+
- `fixed = input(false)` — 좌측 고정.
|
|
45
|
+
- `hidden = input(false)` — 숨김.
|
|
46
|
+
- `collapse = input(false)` — 접힘.
|
|
47
|
+
- `disableSorting = input(false)` — 정렬 비활성.
|
|
48
|
+
- `disableResizing = input(false)` — 폭 조절 비활성.
|
|
49
|
+
- `ordering = input(0)` — 컬럼 정렬 순서.
|
|
50
|
+
- 셀 본문은 `<ng-template [cell]="items()" let-item="item" let-edit="edit">`, 헤더 커스텀은 `#headerTpl`, 합계행은 `#summaryTpl`.
|
|
51
|
+
|
|
52
|
+
`SdSheetCellContext<T>` (셀 템플릿 컨텍스트): `$implicit`/`item`(행 데이터), `index`(행 인덱스), `depth`(트리 깊이), `edit`(현재 셀 편집모드 여부).
|
|
53
|
+
|
|
54
|
+
## SdSheetColumnCellTemplate
|
|
55
|
+
|
|
56
|
+
`<ng-template [cell]>` 디렉티브 — 셀 본문 템플릿 마커. `cell = input.required<T[]>()` 로 타입 추론용 items 를 받아 `SdSheetCellContext<T>` 컨텍스트 타입 가드 제공.
|
|
57
|
+
|
|
58
|
+
## SdSheetConfigModal
|
|
59
|
+
|
|
60
|
+
`<sd-sheet-config-modal>` — 시트 설정(컬럼 표시/순서/고정/폭) 편집 모달. `sd-sheet` 의 설정 버튼이 내부적으로 띄움. 직접 사용은 드묾.
|
|
61
|
+
|
|
62
|
+
## 타입
|
|
63
|
+
|
|
64
|
+
- `SdSheetColumnDef` — 해석된 컬럼 정의(`key`/`header`/`width`/`fixed`/`hidden`/`collapse`/`disableSorting`/`disableResizing`/`ordering`/`headerStyle`/`tooltip`).
|
|
65
|
+
- `SdSheetHeaderDef` — 헤더 셀 메타(`text`/`colspan`/`rowspan`/`isLastRow`/`fixed`/`colDef`/`colIndex`).
|
|
66
|
+
- `SdSheetConfig` — 영속화 형태. `columnRecord: Record<키, { width?; hidden?; fixed?; ordering? }>`.
|
|
67
|
+
- `SdSheetItemKeydownEventParam<T>` — `{ item, event }`.
|
|
68
|
+
- `SdSheetCellKeydownEventParam<T>` — `{ item, key, event }`.
|
|
69
|
+
- `SortingDef`(re-export) — `{ key: string; desc: boolean }`.
|
|
70
|
+
|
|
71
|
+
```html
|
|
72
|
+
<sd-sheet [items]="rows()" [(selectedKeys)]="sel" [selectMode]="'multi'"
|
|
73
|
+
[(sorts)]="sorts" [useAutoSort]="true" key="order-sheet">
|
|
74
|
+
<sd-sheet-column key="name" header="이름" [fixed]="true">
|
|
75
|
+
<ng-template [cell]="rows()" let-item="item">{{ item.name }}</ng-template>
|
|
76
|
+
</sd-sheet-column>
|
|
77
|
+
</sd-sheet>
|
|
78
|
+
```
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
# @simplysm/capacitor-plugin-auto-update
|
|
2
|
+
|
|
3
|
+
Android 앱(Capacitor)에서 APK 설치 및 자동 업데이트를 수행하는 플러그인. APK 설치 인텐트 실행·설치 권한 관리·서버/외부저장소 기반 최신 버전 감지 후 다운로드·설치 흐름을 제공. 모든 공개 심볼은 `static` 멤버만 가진 abstract 클래스이거나 타입이므로 인스턴스 생성 없이 클래스명으로 직접 호출.
|
|
4
|
+
|
|
5
|
+
## 사용 트리거 인덱스
|
|
6
|
+
|
|
7
|
+
- **AutoUpdate** — 앱 시작 시 최신 버전을 감지해 APK 를 받아 설치하는 전체 자동 업데이트 흐름을 돌릴 때. 서버 조회 방식(`run`)과 외부 저장소 디렉토리 방식(`runByExternalStorage`).
|
|
8
|
+
- **ApkInstaller** — 자동 업데이트 흐름을 직접 구성할 때 쓰는 저수준 API. 설치 권한 확인·요청, APK 설치 인텐트 실행, 현재 앱 버전 조회.
|
|
9
|
+
- **ApkInstallerPlugin / VersionInfo** — Capacitor 네이티브 브리지 인터페이스 타입과 버전 정보 형태. 플러그인을 직접 등록·구현하거나 반환 타입을 참조할 때.
|
|
10
|
+
|
|
11
|
+
## AutoUpdate
|
|
12
|
+
|
|
13
|
+
`abstract class AutoUpdate` — 정적 메서드만 가진 고수준 오케스트레이터. 두 진입점 모두 내부에서 Android 여부 확인 → 설치 권한 확인/요청 → 버전 비교 → 다운로드/설치 → 무한 freeze 까지 일괄 처리. 도중 발생한 모든 예외를 잡아 `log` 로 표시한 뒤 `_freezeApp()`(영원히 resolve 되지 않는 무한 대기)으로 진입함. Android 가 아니면(`navigator.userAgent` 에 "android" 없음) `"Android만 지원됩니다."` throw. 버전 비교는 `semver.gt` 로 대상 버전이 현재보다 클 때만 진행.
|
|
14
|
+
|
|
15
|
+
```ts
|
|
16
|
+
static run(opt: { log: (messageHtml: string) => void; serviceClient: ServiceClient }): Promise<void>
|
|
17
|
+
static runByExternalStorage(opt: { log: (messageHtml: string) => void; dirPath: string }): Promise<void>
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
- `run` — 서버 `AutoUpdate` 서비스에서 최신 버전을 조회해 업데이트. 서버 연동 배포일 때 사용.
|
|
21
|
+
- `log: (messageHtml: string) => void` — 진행/오류 상태를 HTML 문자열로 전달받는 콜백. "최신 버전 확인 중...", "권한 확인 중...", 다운로드 진행률 `(NN.NN%)`, 권한 활성화/재시도 버튼 HTML, 오류 메시지 등이 인자로 들어옴. 화면에 그대로 innerHTML 로 렌더하는 용도.
|
|
22
|
+
- `serviceClient: ServiceClient` — `@simplysm/service-client` 의 서비스 클라이언트. 내부에서 `getService<AutoUpdateService>("AutoUpdate").getLastVersion("android")` 로 최신 버전·다운로드 경로를 조회하고, `serviceClient.hostUrl + downloadPath` 로 `fetchUrlBytes` 다운로드. 받은 바이트는 `appCache` 저장소의 `latest.apk` 로 써서 설치. 서버 버전 정보 없으면 무동작 반환.
|
|
23
|
+
- `runByExternalStorage` — 외부 저장소의 지정 디렉토리에서 APK 파일들을 스캔해 업데이트. 서버 없이 외부 저장소로 사이드로딩 배포할 때 사용.
|
|
24
|
+
- `log: (messageHtml: string) => void` — `run` 과 동일한 진행/오류 상태 콜백.
|
|
25
|
+
- `dirPath: string` — 외부 저장소(`FileSystem.getStoragePath("external")`) 기준 상대 디렉토리 경로. 이 폴더의 비-디렉토리 항목 중 확장자가 `.apk` 이고 파일명(확장자 제외)이 `[0-9.]` 로만 이뤄진 것을 버전으로 보고 `semver.maxSatisfying(..., "*")` 로 최신을 선정. 파일 없거나 유효 semver 없으면 반환. 설치 파일은 `<dirPath>/<version>.apk`.
|
|
26
|
+
|
|
27
|
+
```ts
|
|
28
|
+
// 앱 부트스트랩에서
|
|
29
|
+
await AutoUpdate.run({ log: (h) => (statusEl.innerHTML = h), serviceClient });
|
|
30
|
+
await AutoUpdate.runByExternalStorage({ log: (h) => (statusEl.innerHTML = h), dirPath: "myapp-apks" });
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
주의: 업데이트가 없으면 그냥 반환하지만, 업데이트를 진행하거나 오류가 나면 `_freezeApp()` 으로 영원히 resolve 되지 않음 — 호출 후속 코드에 의존하지 말 것. 권한 미승인 시 설정 화면으로 보낸 뒤 최대 5분(1초 간격 폴링) 동안 권한 부여를 대기. APK manifest 에 `REQUEST_INSTALL_PACKAGES` 가 없거나(`manifest:false`) 권한 확인 자체가 실패하면 "APK 파일을 다시 다운로드하여 설치해야 합니다(코드)" 안내(다운로드 링크 버튼 포함)와 함께 throw.
|
|
34
|
+
|
|
35
|
+
## ApkInstaller
|
|
36
|
+
|
|
37
|
+
`abstract class ApkInstaller` — 정적 메서드만 가진 저수준 설치 래퍼. Android 는 네이티브 인텐트/권한을 다루고, 브라우저(web)는 `ApkInstallerWeb` 가 대체 구현(설치 시 알림 표시 후 정상 반환, 권한은 항상 `granted:true`/`manifest:true`). `AutoUpdate` 가 내부적으로 사용하며, 자동 업데이트 UX 를 직접 구성할 때 사용.
|
|
38
|
+
|
|
39
|
+
```ts
|
|
40
|
+
static checkPermissions(): Promise<{ granted: boolean; manifest: boolean }>
|
|
41
|
+
static requestPermissions(): Promise<void>
|
|
42
|
+
static install(apkUri: string): Promise<void>
|
|
43
|
+
static getVersionInfo(): Promise<VersionInfo>
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
- `checkPermissions()` — 설치 권한 상태 조회. 설치 시도 전 사전 점검에 사용.
|
|
47
|
+
- `granted: boolean` — `REQUEST_INSTALL_PACKAGES` 설치 권한이 사용자에게 승인되었는지. false 면 `requestPermissions` 로 유도해야 함.
|
|
48
|
+
- `manifest: boolean` — 해당 권한이 앱 manifest 에 선언되어 있는지. false 면 권한 요청 자체가 불가하므로 APK 재설치가 필요한 상황.
|
|
49
|
+
- `requestPermissions()` — 권한 미승인 시 호출. 시스템 설정 화면으로 이동시키며 즉시 부여되진 않으므로 이후 `checkPermissions` 폴링이 필요.
|
|
50
|
+
- `install(apkUri)` — APK 설치 인텐트 실행.
|
|
51
|
+
- `apkUri: string` — `content://` 형태의 FileProvider URI. 로컬 파일 경로가 아니라 `@simplysm/capacitor-plugin-file-system` 의 `FileSystem.getUri(filePath)` 로 변환한 content URI 를 넘겨야 함.
|
|
52
|
+
- `getVersionInfo()` — 현재 설치된 앱의 버전 정보(`VersionInfo`) 조회. 서버/외부 버전과 비교할 현재 버전 기준값. (web 구현은 `env("__VER__") ?? "0.0.0"` / `versionCode "0"` 반환.)
|
|
53
|
+
|
|
54
|
+
```ts
|
|
55
|
+
const { granted, manifest } = await ApkInstaller.checkPermissions();
|
|
56
|
+
if (!granted) await ApkInstaller.requestPermissions();
|
|
57
|
+
const uri = await FileSystem.getUri(apkFilePath);
|
|
58
|
+
await ApkInstaller.install(uri);
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
## ApkInstallerPlugin / VersionInfo
|
|
62
|
+
|
|
63
|
+
Capacitor 네이티브 브리지 타입. `ApkInstaller` 의 정적 메서드는 `registerPlugin("ApkInstaller")` 로 등록된 이 인터페이스 구현체(네이티브 또는 `ApkInstallerWeb`)에 위임함. 일반적으로 직접 쓰지 않고 `ApkInstaller` 를 통해 접근하며, 타입 참조·웹 구현체 작성 시에만 사용.
|
|
64
|
+
|
|
65
|
+
```ts
|
|
66
|
+
interface VersionInfo { versionName: string; versionCode: string; }
|
|
67
|
+
interface ApkInstallerPlugin {
|
|
68
|
+
install(options: { uri: string }): Promise<void>;
|
|
69
|
+
checkPermissions(): Promise<{ granted: boolean; manifest: boolean }>;
|
|
70
|
+
requestPermissions(): Promise<void>;
|
|
71
|
+
getVersionInfo(): Promise<VersionInfo>;
|
|
72
|
+
}
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
- `VersionInfo.versionName: string` — 사람이 읽는 표시 버전(semver, 예 `"1.2.3"`). `AutoUpdate` 의 버전 비교(`semver.gt`)는 이 값을 semver 로 사용.
|
|
76
|
+
- `VersionInfo.versionCode: string` — Android 빌드 버전 코드의 문자열 표현. 표시·식별용.
|
|
77
|
+
- `ApkInstallerPlugin.install(options: { uri: string })` — content URI APK 설치. `uri` 는 `content://` FileProvider URI.
|
|
78
|
+
- `ApkInstallerPlugin.checkPermissions()` — 설치 권한 승인 여부(`granted`)와 manifest 선언 여부(`manifest`) 조회.
|
|
79
|
+
- `ApkInstallerPlugin.requestPermissions()` — 설치 권한 요청(설정 화면 이동).
|
|
80
|
+
- `ApkInstallerPlugin.getVersionInfo()` — 현재 앱 버전 정보(`VersionInfo`) 조회.
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
# @simplysm/capacitor-plugin-file-system
|
|
2
|
+
|
|
3
|
+
Capacitor 기반 네이티브 파일 시스템 접근 플러그인. Android 는 외부/앱 저장소 직접 접근(11+ 는 `MANAGE_EXTERNAL_STORAGE`, 10- 는 `READ/WRITE_EXTERNAL_STORAGE` 권한), Browser 는 IndexedDB 기반 에뮬레이션으로 동작.
|
|
4
|
+
|
|
5
|
+
## 사용 트리거 인덱스
|
|
6
|
+
|
|
7
|
+
- **FileSystem** — 파일 읽기/쓰기, 디렉토리 조회/생성, 삭제, 권한 확인/요청, 저장소 경로 조회가 필요할 때 쓰는 정적(static) 진입점 클래스(주 사용처).
|
|
8
|
+
- **StorageType** — `FileSystem.getStoragePath` 인자로 넘기는 저장소 위치 유형 리터럴을 고를 때.
|
|
9
|
+
- **FileInfo** — `FileSystem.readdir` 가 반환하는 항목 정보(이름 + 디렉토리 여부). 디렉토리 순회 시.
|
|
10
|
+
- **FileSystemPlugin** — Capacitor 네이티브 브릿지 원형 인터페이스 타입. 보통 `FileSystem` 가 래핑하므로 직접 호출 불필요. 커스텀 web 구현 작성이나 타입 참조 시에만.
|
|
11
|
+
|
|
12
|
+
## FileSystem (static 클래스)
|
|
13
|
+
|
|
14
|
+
모든 메서드 `static async`. 인스턴스 생성 없이 `FileSystem.xxx()` 호출. 내부적으로 `registerPlugin<FileSystemPlugin>("FileSystem")` 으로 얻은 네이티브/web 구현에 위임하고, 플러그인의 `{ ... }` 래퍼 결과를 평탄화해 반환.
|
|
15
|
+
|
|
16
|
+
- `checkPermissions(): Promise<boolean>` — 파일 접근 권한 보유 여부. true 면 권한 있음. 읽기/쓰기 전 사전 확인용. 플러그인의 `{ granted }` 를 boolean 으로 풀어 반환.
|
|
17
|
+
- `requestPermissions(): Promise<void>` — 권한 요청. Android 11+ 는 설정 화면으로 이동, Android 10- 는 권한 대화상자 표시. `checkPermissions()` 가 false 일 때 호출.
|
|
18
|
+
- `readdir(dirPath: string): Promise<FileInfo[]>` — 디렉토리 하위 항목 목록 조회. `dirPath` = 조회할 디렉토리 경로. 각 항목은 `FileInfo`(이름·디렉토리 여부).
|
|
19
|
+
- `getStoragePath(type: StorageType): Promise<string>` — 지정 유형 저장소의 절대 경로 조회. 경로를 직접 조립하지 말고 이 메서드로 베이스 경로를 얻어 join. `type` 풀이는 아래 StorageType 참조.
|
|
20
|
+
- `getUri(filePath: string): Promise<string>` — 파일을 FileProvider 기반 content URI 로 변환. 다른 앱(공유·열기 인텐트)에 파일을 넘길 때. `filePath` = 대상 파일 경로.
|
|
21
|
+
- `writeFile(filePath: string, data: string | Bytes): Promise<void>` — 파일 쓰기. `data` 가 `string` 이면 utf8 인코딩으로, `Bytes`(Uint8Array)면 base64 로 인코딩해(`bytes.toBase64`, cross-realm 안전) 저장. 텍스트면 string, 바이너리면 Bytes 를 넘김.
|
|
22
|
+
- `readFile(filePath: string): Promise<Bytes>` / `readFile(filePath: string, encoding: "utf8"): Promise<string>` — 파일 읽기 오버로드. `encoding` 생략 시 base64 로 읽어 `Bytes`(`bytes.fromBase64`) 반환(바이너리용), `"utf8"` 지정 시 텍스트 `string` 반환. 반환 타입이 오버로드로 갈리므로 바이너리는 인자 없이, 텍스트는 `"utf8"` 명시.
|
|
23
|
+
- `remove(targetPath: string): Promise<void>` — 파일 또는 디렉토리 삭제(재귀). `targetPath` = 삭제 대상 경로. 디렉토리면 하위까지 모두 제거.
|
|
24
|
+
- `mkdir(targetPath: string): Promise<void>` — 디렉토리 생성(재귀). `targetPath` = 생성할 경로. 중간 상위 디렉토리가 없으면 함께 생성.
|
|
25
|
+
- `exists(targetPath: string): Promise<boolean>` — 경로 존재 여부 확인. true 면 존재. 쓰기 전 충돌 확인이나 읽기 전 가드용.
|
|
26
|
+
|
|
27
|
+
```ts
|
|
28
|
+
import { FileSystem } from "@simplysm/capacitor-plugin-file-system";
|
|
29
|
+
|
|
30
|
+
if (!(await FileSystem.checkPermissions())) await FileSystem.requestPermissions();
|
|
31
|
+
|
|
32
|
+
const base = await FileSystem.getStoragePath("externalFiles");
|
|
33
|
+
await FileSystem.mkdir(`${base}/notes`);
|
|
34
|
+
await FileSystem.writeFile(`${base}/notes/memo.txt`, "hello"); // utf8
|
|
35
|
+
const text = await FileSystem.readFile(`${base}/notes/memo.txt`, "utf8"); // string
|
|
36
|
+
const raw = await FileSystem.readFile(`${base}/notes/memo.txt`); // Bytes
|
|
37
|
+
for (const f of await FileSystem.readdir(`${base}/notes`)) {
|
|
38
|
+
console.log(f.name, f.isDirectory);
|
|
39
|
+
}
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
## StorageType / FileInfo / FileSystemPlugin
|
|
43
|
+
|
|
44
|
+
- `StorageType` — `getStoragePath(type)` 의 저장소 위치 선택 유니온. 값별 디렉토리 매핑:
|
|
45
|
+
- `"external"` — 외부 저장소 루트(`Environment.getExternalStorageDirectory`). 사용자에게 노출되는 공용 영역 전체 접근 시. MANAGE_EXTERNAL_STORAGE 권한 필요.
|
|
46
|
+
- `"externalFiles"` — 앱 전용 외부 파일 디렉토리. 앱이 소유하는 외부 영속 파일용(앱 제거 시 함께 삭제).
|
|
47
|
+
- `"externalCache"` — 앱 전용 외부 캐시 디렉토리. 시스템이 공간 부족 시 회수할 수 있는 임시 캐시용.
|
|
48
|
+
- `"externalMedia"` — 앱 전용 외부 미디어 디렉토리. 이미지·동영상 등 미디어 산출물용.
|
|
49
|
+
- `"appData"` — 앱 데이터 디렉토리(내부 저장소). 앱 비공개 데이터용.
|
|
50
|
+
- `"appFiles"` — 앱 파일 디렉토리(내부 저장소). 앱 비공개 영속 파일용.
|
|
51
|
+
- `"appCache"` — 앱 캐시 디렉토리(내부 저장소). 앱 비공개 임시 캐시용.
|
|
52
|
+
- `FileInfo` — `readdir` 결과 배열의 각 항목.
|
|
53
|
+
- `name: string` — 항목(파일·디렉토리) 이름.
|
|
54
|
+
- `isDirectory: boolean` — 디렉토리 여부. true 면 디렉토리, false 면 파일. 재귀 순회 시 분기 기준.
|
|
55
|
+
- `FileSystemPlugin` — `registerPlugin` 에 넘기는 네이티브 브릿지 인터페이스. 메서드는 `FileSystem` 정적 메서드의 저수준 원형으로, 옵션 객체를 받고 래핑 전 결과를 `{ ... }` 로 반환:
|
|
56
|
+
- `checkPermissions(): Promise<{ granted: boolean }>` — 권한 보유 여부를 `granted` 로 반환.
|
|
57
|
+
- `requestPermissions(): Promise<void>` — 권한 요청.
|
|
58
|
+
- `readdir(options: { path: string }): Promise<{ files: FileInfo[] }>` — `path` 디렉토리 항목 목록을 `files` 로 반환.
|
|
59
|
+
- `getStoragePath(options: { type: StorageType }): Promise<{ path: string }>` — `type` 저장소의 실제 경로를 `path` 로 반환.
|
|
60
|
+
- `getUri(options: { path: string }): Promise<{ uri: string }>` — `path` 파일의 FileProvider URI 를 `uri` 로 반환.
|
|
61
|
+
- `writeFile(options: { path: string; data: string; encoding?: "utf8" | "base64" }): Promise<void>` — `data`(문자열)를 `encoding`(`"utf8"`=텍스트, `"base64"`=바이너리 디코드, 생략 시 구현 기본)으로 `path` 에 기록.
|
|
62
|
+
- `readFile(options: { path: string; encoding?: "utf8" | "base64" }): Promise<{ data: string }>` — `path` 내용을 `encoding`(`"utf8"`=텍스트, `"base64"`=바이너리 인코딩 문자열)으로 읽어 `data` 로 반환.
|
|
63
|
+
- `remove(options: { path: string }): Promise<void>` — `path` 파일/디렉토리 재귀 삭제.
|
|
64
|
+
- `mkdir(options: { path: string }): Promise<void>` — `path` 디렉토리 재귀 생성.
|
|
65
|
+
- `exists(options: { path: string }): Promise<{ exists: boolean }>` — `path` 존재 여부를 `exists` 로 반환.
|
|
66
|
+
- 직접 호출 대신 `FileSystem` 사용 권장. 커스텀 web 구현 작성 시 이 인터페이스를 구현.
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
# @simplysm/capacitor-plugin-intent
|
|
2
|
+
|
|
3
|
+
Android 인텐트 송수신 Capacitor 플러그인. 브로드캐스트 구독/전송, 실행 인텐트 조회, `startActivityForResult` 외부 Activity 실행을 제공하며 바코드 스캐너·PDA 등 산업용 디바이스 연동에 사용. 웹 환경은 미지원 스텁(경고 로그 후 빈/스텁 결과)으로 동작.
|
|
4
|
+
|
|
5
|
+
## 사용 트리거 인덱스
|
|
6
|
+
|
|
7
|
+
- **Intent** — 모든 인텐트 작업의 정적 진입점. 브로드캐스트 구독/전송, 실행 인텐트 조회, newIntent 리스너, 외부 Activity 실행 시 사용. 아래 인라인 섹션 참조.
|
|
8
|
+
- **IntentResult / StartActivityForResultOptions / StartActivityForResultResult** — `Intent` 메서드의 입출력 타입. 콜백 결과·옵션·반환값을 타입 처리할 때 참조. 아래 인라인 섹션 참조.
|
|
9
|
+
- **IntentPlugin** — Capacitor 네이티브 플러그인 인터페이스 원형. 보통 직접 호출하지 않고 `Intent` 정적 메서드를 통해 사용. 웹 스텁/네이티브 구현의 계약 정의용. 아래 인라인 섹션 참조.
|
|
10
|
+
|
|
11
|
+
## Intent (정적 클래스)
|
|
12
|
+
|
|
13
|
+
`abstract class Intent` — 정적 메서드만 가진 진입점. 인스턴스화 불가, `Intent.xxx()` 형태로 호출. 모든 메서드는 비동기(`Promise`).
|
|
14
|
+
|
|
15
|
+
- `Intent.subscribe(filters: string[], callback: (result: IntentResult) => void): Promise<() => Promise<void>>` — 브로드캐스트 수신기 등록. `filters` = 수신할 인텐트 액션 문자열 배열(예: DataWedge RESULT_ACTION). `callback` = 매칭 브로드캐스트 도착 시 호출(`result.action == null` 인 초기 등록 resolve 이벤트는 내부에서 걸러져 호출 안 됨). 반환값은 구독 해제 함수이며 호출 시 내부 `id` 로 해당 수신기만 해제. 스캐너 등 지속 수신 화면 진입 시 등록하고 이탈 시 반환 함수로 해제.
|
|
16
|
+
- `Intent.unsubscribeAll(): Promise<void>` — 등록된 모든 브로드캐스트 수신기를 한 번에 해제. 개별 해제 함수를 보관하지 않았거나 전체 정리가 필요할 때 사용.
|
|
17
|
+
- `Intent.send(options: { action: string; extras?: Record<string, unknown> }): Promise<void>` — 브로드캐스트 전송. `action`(필수) = 전송할 인텐트 액션 문자열, `extras`(선택) = 함께 보낼 추가 키-값 데이터. 스캐너 트리거 토글 등 디바이스에 명령을 보낼 때 사용.
|
|
18
|
+
- `Intent.getLaunchIntent(): Promise<IntentResult>` — 앱을 실행시킨 인텐트를 조회. 외부 인텐트로 앱이 기동됐는지·그 extras 를 앱 시작 시점에 읽을 때 사용. 웹에서는 항상 빈 객체 `{}` 반환.
|
|
19
|
+
- `Intent.addListener(eventName: "newIntent", callback: (result: IntentResult) => void): Promise<PluginListenerHandle>` — 앱 실행 중 새로 들어오는 인텐트 리스너 등록. `eventName` 은 `"newIntent"` 리터럴 고정. 반환된 핸들의 `handle.remove()` 로 개별 해제. 이미 떠 있는 앱에 인텐트가 재전달되는 경우(singleTop 등)를 처리할 때 사용.
|
|
20
|
+
- `Intent.removeAllListeners(): Promise<void>` — `addListener` 로 등록한 모든 이벤트 리스너를 일괄 제거. 화면 정리 시 newIntent 리스너를 한 번에 해제할 때 사용.
|
|
21
|
+
- `Intent.startActivityForResult(options: StartActivityForResultOptions): Promise<StartActivityForResultResult>` — 외부 Activity 를 실행하고 결과를 수신. 결제·서명 등 외부 앱에 작업을 위임하고 결과를 받아야 할 때 사용. 반환 `resultCode` 로 성공 판정(Android 관례상 `-1` = RESULT_OK, `0` = RESULT_CANCELED).
|
|
22
|
+
|
|
23
|
+
사용 예:
|
|
24
|
+
|
|
25
|
+
```ts
|
|
26
|
+
const unsub = await Intent.subscribe(
|
|
27
|
+
["com.symbol.datawedge.api.RESULT_ACTION"],
|
|
28
|
+
(result) => console.log(result.action, result.extras),
|
|
29
|
+
);
|
|
30
|
+
await Intent.send({
|
|
31
|
+
action: "com.symbol.datawedge.api.ACTION",
|
|
32
|
+
extras: { "com.symbol.datawedge.api.SOFT_SCAN_TRIGGER": "TOGGLE_SCANNING" },
|
|
33
|
+
});
|
|
34
|
+
const res = await Intent.startActivityForResult({ action: "com.example.PAY", extras: { amount: 1000 } });
|
|
35
|
+
if (res.resultCode === -1) { /* RESULT_OK */ }
|
|
36
|
+
await unsub(); // 개별 해제
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
주의: 웹 플랫폼에서는 `subscribe`/`send`/`startActivityForResult` 가 실제 동작 없이 경고 로그(`createLogger("capacitor:intent")` → "웹 환경에서는 지원하지 않습니다.")만 남기고, `subscribe` 는 `{ id: "web-stub" }`, `getLaunchIntent` 는 `{}`, `startActivityForResult` 는 `{ resultCode: 0 }` 을 반환한다. 실제 인텐트 처리는 Android 디바이스에서만 일어난다.
|
|
40
|
+
|
|
41
|
+
## 입출력 타입
|
|
42
|
+
|
|
43
|
+
`Intent` 메서드의 결과·옵션을 타입 처리할 때 참조하는 인터페이스들.
|
|
44
|
+
|
|
45
|
+
- `IntentResult` — `subscribe`/`addListener` 콜백 인자 및 `getLaunchIntent` 반환 타입.
|
|
46
|
+
- `action?: string` — 인텐트(브로드캐스트) 액션 문자열. 미수신/미설정일 수 있어 선택. `subscribe` 콜백에는 이 값이 채워진 실제 이벤트만 전달됨.
|
|
47
|
+
- `extras?: Record<string, unknown>` — 인텐트에 담긴 추가 키-값 데이터(스캔된 바코드 값 등).
|
|
48
|
+
- `StartActivityForResultOptions` — `startActivityForResult` 입력. 모든 필드 선택이며 실행할 인텐트를 명시적/암시적으로 구성.
|
|
49
|
+
- `action?: string` — 인텐트 액션(암시적 인텐트 지정용).
|
|
50
|
+
- `uri?: string` — 인텐트 data URI.
|
|
51
|
+
- `extras?: Record<string, unknown>` — 대상 Activity 로 전달할 추가 데이터.
|
|
52
|
+
- `type?: string` — MIME 타입. data 와 함께 처리 대상 필터링에 사용.
|
|
53
|
+
- `packageName?: string` — 실행할 특정 앱 패키지명 지정(명시적 인텐트화).
|
|
54
|
+
- `className?: string` — 실행할 특정 Activity 클래스명 지정.
|
|
55
|
+
- `flags?: number` — Android Intent flags 비트값(예: 새 태스크 시작 등).
|
|
56
|
+
- `StartActivityForResultResult` — `startActivityForResult` 반환.
|
|
57
|
+
- `resultCode: number` — Activity 결과 코드. Android 규약상 `-1` = RESULT_OK, `0` = RESULT_CANCELED. 웹 스텁은 `0` 반환.
|
|
58
|
+
- `data?: { action?: string; uri?: string; extras?: Record<string, unknown> }` — 결과 인텐트 데이터. `action` = 결과 인텐트 액션, `uri` = 결과 data URI, `extras` = 결과 추가 데이터. 취소 등으로 데이터가 없으면 부재.
|
|
59
|
+
|
|
60
|
+
## IntentPlugin
|
|
61
|
+
|
|
62
|
+
`interface IntentPlugin` — `registerPlugin<IntentPlugin>("Intent")` 에 쓰이는 Capacitor 네이티브 플러그인 계약. 웹 구현(`IntentWeb`)과 Android 네이티브가 이를 구현. 일반 사용 코드는 `Intent` 정적 메서드를 쓰고 이 인터페이스를 직접 호출하지 않음(저수준 계약 참조용).
|
|
63
|
+
|
|
64
|
+
- `subscribe(options: { filters: string[] }, callback: (result: IntentResult) => void): Promise<{ id: string }>` — 수신기 등록 후 해제용 `id` 반환. `Intent.subscribe` 가 이 `id` 를 보관해 반환 함수에서 `unsubscribe` 에 넘김.
|
|
65
|
+
- `unsubscribe(options: { id: string }): Promise<void>` — 주어진 `id` 의 수신기만 해제.
|
|
66
|
+
- `unsubscribeAll(): Promise<void>` — 모든 수신기 해제.
|
|
67
|
+
- `send(options: { action: string; extras?: Record<string, unknown> }): Promise<void>` — 브로드캐스트 전송.
|
|
68
|
+
- `getLaunchIntent(): Promise<IntentResult>` — 실행 인텐트 조회.
|
|
69
|
+
- `addListener(eventName: "newIntent", listenerFunc: (data: IntentResult) => void): Promise<PluginListenerHandle>` — newIntent 이벤트 리스너 등록, 해제용 핸들 반환.
|
|
70
|
+
- `removeAllListeners(): Promise<void>` — 모든 리스너 제거.
|
|
71
|
+
- `startActivityForResult(options: StartActivityForResultOptions): Promise<StartActivityForResultResult>` — 외부 Activity 실행 및 결과 수신.
|