@iyulab/data-components 0.3.0 → 0.4.1
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 +65 -2
- package/README.md +7 -17
- package/dist/_virtual/_@oxc-project_runtime@0.127.0/helpers/decorate.js +9 -0
- package/dist/_virtual/_@oxc-project_runtime@0.127.0/helpers/decorateMetadata.js +6 -0
- package/dist/components/data-view/UDataView.d.ts +44 -3
- package/dist/components/data-view/UDataView.js +172 -4
- package/dist/components/data-view/UDataView.styles.js +4 -83
- package/dist/components/simple-sheet/USimpleSheet.d.ts +167 -3
- package/dist/components/simple-sheet/USimpleSheet.js +934 -4
- package/dist/components/simple-sheet/USimpleSheet.styles.js +4 -4
- package/dist/components/u-rich-table/URichTable.d.ts +70 -3
- package/dist/components/u-rich-table/URichTable.js +618 -4
- package/dist/components/u-rich-table/styles.js +4 -4
- package/dist/components/u-rich-table/utils/clipboard.js +26 -29
- package/dist/index.d.ts +0 -2
- package/dist/index.js +4 -7
- package/package.json +16 -46
- package/dist/components/data-grid/UDataGrid.d.ts +0 -2
- package/dist/components/data-grid/UDataGrid.js +0 -174
- package/dist/components/data-grid/UDataGrid.types.d.ts +0 -42
- package/dist/components/data-view/UDataView.component.d.ts +0 -47
- package/dist/components/data-view/UDataView.component.js +0 -226
- package/dist/components/simple-sheet/USimpleSheet.component.d.ts +0 -167
- package/dist/components/simple-sheet/USimpleSheet.component.js +0 -1034
- package/dist/components/u-rich-table/URichTable.component.d.ts +0 -70
- package/dist/components/u-rich-table/URichTable.component.js +0 -672
- package/dist/utilities/devExtremeCssInjection.d.ts +0 -23
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,43 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [0.4.1] - 2026-05-07
|
|
4
|
+
|
|
5
|
+
### Fixed
|
|
6
|
+
- eslint.config.js에서 제거된 React 플러그인 참조 삭제 (eslint-plugin-react-hooks, eslint-plugin-react-refresh)
|
|
7
|
+
|
|
8
|
+
## [0.4.0] - 2026-05-07
|
|
9
|
+
|
|
10
|
+
### BREAKING CHANGES
|
|
11
|
+
- `UDataGrid` 컴포넌트 제거 — DevExtreme 상용 라이선스 의존성 해소
|
|
12
|
+
- `src/components/data-grid/UDataGrid.tsx`, `UDataGrid.types.ts` 삭제
|
|
13
|
+
- `src/utilities/devExtremeCssInjection.ts`, `src/styles/devextreme-overrides.css` 삭제
|
|
14
|
+
- `exports['./data-grid']` 제거
|
|
15
|
+
- `dependencies`에서 `devextreme`, `devextreme-react`, `react`, `react-dom` 제거
|
|
16
|
+
|
|
17
|
+
### Migration
|
|
18
|
+
UDataGrid 대체: `@iyulab/flex-table` + `@iyulab/flex-table/odata` 조합 사용
|
|
19
|
+
→ [마이그레이션 가이드](./docs/migrating-from-datagrid.md)
|
|
20
|
+
|
|
21
|
+
## [0.3.2] - 2026-05-07
|
|
22
|
+
|
|
23
|
+
### Added
|
|
24
|
+
- `docs/migrating-from-datagrid.md`: UDataGrid(DevExtreme)에서 `@iyulab/flex-table`로 전환하는 마이그레이션 가이드 추가 (yesung-oms 9개 페이지 실증 기반)
|
|
25
|
+
- README에 UDataGrid 대안 섹션 및 마이그레이션 가이드 링크 추가
|
|
26
|
+
- `docs/UDataGrid.md` 상단에 마이그레이션 가이드 링크 추가
|
|
27
|
+
|
|
28
|
+
## [0.3.1] - 2026-04-01
|
|
29
|
+
### Changed
|
|
30
|
+
- 컴포넌트 파일 구조 리팩토링: `.component.ts` 파일을 기존 `.ts`로 병합 (UDataView, USimpleSheet, URichTable)
|
|
31
|
+
- UDataView: 다크모드 하드코딩 스타일 제거 — CSS 변수 기반으로 전환
|
|
32
|
+
- 의존성 업데이트: devextreme 25.2.5, vite 8.0.3, typescript-eslint 8.57.2 등
|
|
33
|
+
|
|
34
|
+
## [0.3.0] - 2026-03-26
|
|
35
|
+
### Added
|
|
36
|
+
- URichTable: 코어 컴포넌트 추가 — 렌더링, 정렬, 필터, 편집, 페이지네이션
|
|
37
|
+
- URichTable: 엑셀 스타일 클립보드(복사/붙여넣기) + 키보드 네비게이션
|
|
38
|
+
- URichTable: 다크모드 지원 (`:host-context([theme="dark"])`)
|
|
39
|
+
- USimpleSheet: `setSelection()`, `selectAll()` 공개 API 추가
|
|
40
|
+
|
|
3
41
|
## [0.1.8] - 2026-03-08
|
|
4
42
|
### Added
|
|
5
43
|
- USimpleSheet: `format` 속성 — Intl.NumberFormatOptions(통화, 퍼센트 등) 또는 콜백 함수로 표시 포맷 지정
|
|
@@ -13,5 +51,30 @@
|
|
|
13
51
|
- 에러 발생 시 빈 문자열 표시
|
|
14
52
|
- 붙여넣기/Fill 시 compute 열 건너뛰기
|
|
15
53
|
|
|
16
|
-
## [0.1.6]
|
|
17
|
-
|
|
54
|
+
## [0.1.6] - 2026-03-08
|
|
55
|
+
### Added
|
|
56
|
+
- USimpleSheet: 드롭다운 셀렉터 기능 (`options`/`strict` 속성)
|
|
57
|
+
- 드롭다운 상태 관리, 필터링, 키보드 네비게이션
|
|
58
|
+
- 다크모드 지원
|
|
59
|
+
### Fixed
|
|
60
|
+
- USimpleSheet: 문자 입력 시 이중 입력 방지 (`preventDefault` 추가)
|
|
61
|
+
- USimpleSheet: strict 모드에서 `_isDropdownClick` 리셋 및 안전한 스크롤 처리
|
|
62
|
+
|
|
63
|
+
## [0.1.4] - 2026-03-07
|
|
64
|
+
### Changed
|
|
65
|
+
- UDataView, USimpleSheet: 다크모드 스타일 업데이트
|
|
66
|
+
|
|
67
|
+
## [0.1.3] - 2026-03-07
|
|
68
|
+
### Fixed
|
|
69
|
+
- Vite 빌드에 컴포넌트 엔트리 포인트 추가 (sub-path exports 수정)
|
|
70
|
+
|
|
71
|
+
## [0.1.2] - 2026-03-07
|
|
72
|
+
### Added
|
|
73
|
+
- 컴포넌트별 독립 임포트를 위한 sub-path exports 추가
|
|
74
|
+
|
|
75
|
+
## [0.1.1] - 2026-03-06
|
|
76
|
+
### Changed
|
|
77
|
+
- 재배포를 위한 버전 범프
|
|
78
|
+
|
|
79
|
+
## [0.1.0] - 2026-03-06
|
|
80
|
+
- 초기 라이브러리 릴리스
|
package/README.md
CHANGED
|
@@ -6,7 +6,11 @@
|
|
|
6
6
|
|
|
7
7
|
- **USimpleSheet** — 엑셀 호환 스프레드시트 입력 컴포넌트 (Lit, compute 자동 계산 지원)
|
|
8
8
|
- **UDataView** — Grid / List / Table 뷰 전환 컴포넌트 (Lit)
|
|
9
|
-
- **
|
|
9
|
+
- **URichTable** — 정렬/필터/편집/페이지네이션 지원 테이블 (Lit)
|
|
10
|
+
|
|
11
|
+
> **UDataGrid (DevExtreme)는 v0.4.0에서 제거되었습니다.**
|
|
12
|
+
> OData 서버 사이드 그리드가 필요하면 `@iyulab/flex-table`을 사용하세요.
|
|
13
|
+
> → [마이그레이션 가이드](./docs/migrating-from-datagrid.md)
|
|
10
14
|
|
|
11
15
|
## Installation
|
|
12
16
|
|
|
@@ -73,26 +77,12 @@ npm install @iyulab/data-components
|
|
|
73
77
|
></u-data-view>
|
|
74
78
|
```
|
|
75
79
|
|
|
76
|
-
### UDataGrid (React)
|
|
77
|
-
|
|
78
|
-
```tsx
|
|
79
|
-
import { UDataGrid } from '@iyulab/data-components';
|
|
80
|
-
|
|
81
|
-
<UDataGrid
|
|
82
|
-
dataSourceUrl="https://api.example.com/odata/products"
|
|
83
|
-
keyField="id"
|
|
84
|
-
columns={[
|
|
85
|
-
{ dataField: 'name', caption: '상품명' },
|
|
86
|
-
{ dataField: 'price', caption: '가격', dataType: 'number' },
|
|
87
|
-
]}
|
|
88
|
-
/>
|
|
89
|
-
```
|
|
90
|
-
|
|
91
80
|
## Documentation
|
|
92
81
|
|
|
93
82
|
- [USimpleSheet](./docs/USimpleSheet.md)
|
|
94
83
|
- [UDataView](./docs/UDataView.md)
|
|
95
|
-
- [
|
|
84
|
+
- [URichTable](./docs/URichTable.md)
|
|
85
|
+
- [UDataGrid 마이그레이션 가이드](./docs/migrating-from-datagrid.md)
|
|
96
86
|
|
|
97
87
|
## Theming
|
|
98
88
|
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
//#region \0@oxc-project+runtime@0.127.0/helpers/decorate.js
|
|
2
|
+
function __decorate(decorators, target, key, desc) {
|
|
3
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
4
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
5
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
6
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
7
|
+
}
|
|
8
|
+
//#endregion
|
|
9
|
+
export { __decorate };
|
|
@@ -1,8 +1,49 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { TemplateResult } from 'lit';
|
|
2
|
+
import { UElement } from '@iyulab/components/dist/components/UElement.js';
|
|
3
|
+
export type ViewMode = 'grid' | 'list' | 'table';
|
|
4
|
+
export interface DataColumn {
|
|
5
|
+
key: string;
|
|
6
|
+
label?: string;
|
|
7
|
+
width?: string;
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* Data View Component
|
|
11
|
+
* 데이터를 3가지 레이아웃(grid, list, table)으로 표시하는 컴포넌트
|
|
12
|
+
*/
|
|
13
|
+
export declare class UDataView extends UElement {
|
|
14
|
+
static styles: import('lit').CSSResultGroup[];
|
|
15
|
+
/** 표시할 데이터 배열 */
|
|
16
|
+
items: any[];
|
|
17
|
+
/** 현재 뷰 모드 */
|
|
18
|
+
mode: ViewMode;
|
|
19
|
+
/** 표시할 컬럼 설정 (미지정시 자동 감지) */
|
|
20
|
+
columns?: DataColumn[];
|
|
21
|
+
/** 그리드 모드 최소 폭 */
|
|
22
|
+
gridMinWidth: string;
|
|
23
|
+
/** 아이템 간격 */
|
|
24
|
+
gap: string;
|
|
25
|
+
/** 커스텀 렌더 함수 (grid/list 카드용) */
|
|
26
|
+
renderCard?: (item: any, index: number) => TemplateResult;
|
|
27
|
+
/** 커스텀 셀 렌더 함수 (table용) */
|
|
28
|
+
renderCell?: (item: any, column: DataColumn, index: number) => TemplateResult | string;
|
|
29
|
+
private selectedIndex;
|
|
30
|
+
render(): TemplateResult<1>;
|
|
31
|
+
private renderToolbar;
|
|
32
|
+
private renderViewButton;
|
|
33
|
+
private renderContent;
|
|
34
|
+
private renderGrid;
|
|
35
|
+
private renderList;
|
|
36
|
+
private renderTable;
|
|
37
|
+
private renderGridItem;
|
|
38
|
+
private renderListItem;
|
|
39
|
+
private renderDefaultCard;
|
|
40
|
+
private getCellContent;
|
|
41
|
+
private getColumns;
|
|
42
|
+
private formatLabel;
|
|
43
|
+
private formatValue;
|
|
44
|
+
}
|
|
2
45
|
declare global {
|
|
3
46
|
interface HTMLElementTagNameMap {
|
|
4
47
|
"u-data-view": UDataView;
|
|
5
48
|
}
|
|
6
49
|
}
|
|
7
|
-
export { UDataView };
|
|
8
|
-
export type { Column, ViewMode } from './UDataView.component.js';
|
|
@@ -1,5 +1,173 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
1
|
+
import { styles } from "./UDataView.styles.js";
|
|
2
|
+
import { __decorateMetadata } from "../../_virtual/_@oxc-project_runtime@0.127.0/helpers/decorateMetadata.js";
|
|
3
|
+
import { __decorate } from "../../_virtual/_@oxc-project_runtime@0.127.0/helpers/decorate.js";
|
|
4
|
+
import { html } from "lit";
|
|
5
|
+
import { customElement, property, state } from "lit/decorators.js";
|
|
6
|
+
import "@iyulab/components/dist/components/icon/UIcon.js";
|
|
7
|
+
import "@iyulab/components/dist/components/button/UButton.js";
|
|
8
|
+
import { UElement } from "@iyulab/components/dist/components/UElement.js";
|
|
9
|
+
//#region src/components/data-view/UDataView.ts
|
|
10
|
+
var UDataView = class UDataView extends UElement {
|
|
11
|
+
constructor(..._args) {
|
|
12
|
+
super(..._args);
|
|
13
|
+
this.items = [];
|
|
14
|
+
this.mode = "grid";
|
|
15
|
+
this.gridMinWidth = "200px";
|
|
16
|
+
this.gap = "1rem";
|
|
17
|
+
this.selectedIndex = null;
|
|
18
|
+
}
|
|
19
|
+
static {
|
|
20
|
+
this.styles = [super.styles, styles];
|
|
21
|
+
}
|
|
22
|
+
render() {
|
|
23
|
+
return html`
|
|
24
|
+
<div class="data-view">
|
|
25
|
+
${this.renderToolbar()}
|
|
26
|
+
${this.renderContent()}
|
|
27
|
+
</div>
|
|
28
|
+
`;
|
|
29
|
+
}
|
|
30
|
+
renderToolbar() {
|
|
31
|
+
return html`
|
|
32
|
+
<div class="toolbar">
|
|
33
|
+
<div class="view-toggles">
|
|
34
|
+
${this.renderViewButton("grid", "grid-3x3-gap", "Grid")}
|
|
35
|
+
${this.renderViewButton("list", "list-ul", "List")}
|
|
36
|
+
${this.renderViewButton("table", "table", "Table")}
|
|
37
|
+
</div>
|
|
38
|
+
<div class="info">
|
|
39
|
+
${this.items.length} items
|
|
40
|
+
</div>
|
|
41
|
+
</div>
|
|
42
|
+
`;
|
|
43
|
+
}
|
|
44
|
+
renderViewButton(mode, icon, label) {
|
|
45
|
+
return html`
|
|
46
|
+
<u-button
|
|
47
|
+
?active=${this.mode === mode}
|
|
48
|
+
title=${label}
|
|
49
|
+
>
|
|
50
|
+
<u-icon lib="bootstrap" name=${icon}></u-icon>
|
|
51
|
+
</u-button>
|
|
52
|
+
`;
|
|
53
|
+
}
|
|
54
|
+
renderContent() {
|
|
55
|
+
if (!this.items?.length) return html`<div class="empty">No data available</div>`;
|
|
56
|
+
switch (this.mode) {
|
|
57
|
+
case "grid": return this.renderGrid();
|
|
58
|
+
case "list": return this.renderList();
|
|
59
|
+
case "table": return this.renderTable();
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
renderGrid() {
|
|
63
|
+
return html`
|
|
64
|
+
<div class="grid" style="--min-width: ${this.gridMinWidth}; --gap: ${this.gap};">
|
|
65
|
+
${this.items.map((item, index) => this.renderGridItem(item, index))}
|
|
66
|
+
</div>
|
|
67
|
+
`;
|
|
68
|
+
}
|
|
69
|
+
renderList() {
|
|
70
|
+
return html`
|
|
71
|
+
<div class="list" style="--gap: ${this.gap};">
|
|
72
|
+
${this.items.map((item, index) => this.renderListItem(item, index))}
|
|
73
|
+
</div>
|
|
74
|
+
`;
|
|
75
|
+
}
|
|
76
|
+
renderTable() {
|
|
77
|
+
const cols = this.getColumns();
|
|
78
|
+
return html`
|
|
79
|
+
<div class="table-wrapper">
|
|
80
|
+
<table>
|
|
81
|
+
<thead>
|
|
82
|
+
<tr>
|
|
83
|
+
${cols.map((col) => html`
|
|
84
|
+
<th style=${col.width ? `width: ${col.width}` : ""}>
|
|
85
|
+
${col.label || this.formatLabel(col.key)}
|
|
86
|
+
</th>
|
|
87
|
+
`)}
|
|
88
|
+
</tr>
|
|
89
|
+
</thead>
|
|
90
|
+
<tbody>
|
|
91
|
+
${this.items.map((item, index) => html`
|
|
92
|
+
<tr
|
|
93
|
+
class=${this.selectedIndex === index ? "selected" : ""}
|
|
94
|
+
>
|
|
95
|
+
${cols.map((col) => html`
|
|
96
|
+
<td>${this.getCellContent(item, col, index)}</td>
|
|
97
|
+
`)}
|
|
98
|
+
</tr>
|
|
99
|
+
`)}
|
|
100
|
+
</tbody>
|
|
101
|
+
</table>
|
|
102
|
+
</div>
|
|
103
|
+
`;
|
|
104
|
+
}
|
|
105
|
+
renderGridItem(item, index) {
|
|
106
|
+
const content = this.renderCard ? this.renderCard(item, index) : this.renderDefaultCard(item);
|
|
107
|
+
return html`
|
|
108
|
+
<div
|
|
109
|
+
class="card ${this.selectedIndex === index ? "selected" : ""}"
|
|
110
|
+
>
|
|
111
|
+
${content}
|
|
112
|
+
</div>
|
|
113
|
+
`;
|
|
114
|
+
}
|
|
115
|
+
renderListItem(item, index) {
|
|
116
|
+
const content = this.renderCard ? this.renderCard(item, index) : this.renderDefaultCard(item);
|
|
117
|
+
return html`
|
|
118
|
+
<div
|
|
119
|
+
class="card list-card ${this.selectedIndex === index ? "selected" : ""}"
|
|
120
|
+
>
|
|
121
|
+
${content}
|
|
122
|
+
</div>
|
|
123
|
+
`;
|
|
124
|
+
}
|
|
125
|
+
renderDefaultCard(item) {
|
|
126
|
+
return html`
|
|
127
|
+
<div class="card-content">
|
|
128
|
+
${this.getColumns().slice(0, 5).map((col) => {
|
|
129
|
+
const value = item[col.key];
|
|
130
|
+
return html`
|
|
131
|
+
<div class="card-field">
|
|
132
|
+
<span class="label">${col.label || this.formatLabel(col.key)}:</span>
|
|
133
|
+
<span class="value">${this.formatValue(value)}</span>
|
|
134
|
+
</div>
|
|
135
|
+
`;
|
|
136
|
+
})}
|
|
137
|
+
</div>
|
|
138
|
+
`;
|
|
139
|
+
}
|
|
140
|
+
getCellContent(item, column, index) {
|
|
141
|
+
if (this.renderCell) return this.renderCell(item, column, index);
|
|
142
|
+
return this.formatValue(item[column.key]);
|
|
143
|
+
}
|
|
144
|
+
getColumns() {
|
|
145
|
+
if (this.columns?.length) return this.columns;
|
|
146
|
+
if (this.items.length > 0) {
|
|
147
|
+
const firstItem = this.items[0];
|
|
148
|
+
return Object.keys(firstItem).map((key) => ({ key }));
|
|
149
|
+
}
|
|
150
|
+
return [];
|
|
151
|
+
}
|
|
152
|
+
formatLabel(key) {
|
|
153
|
+
return key.replace(/([A-Z])/g, " $1").replace(/^./, (str) => str.toUpperCase()).trim();
|
|
154
|
+
}
|
|
155
|
+
formatValue(value) {
|
|
156
|
+
if (value == null) return "—";
|
|
157
|
+
if (typeof value === "boolean") return value ? "✓" : "✗";
|
|
158
|
+
if (value instanceof Date) return value.toLocaleString();
|
|
159
|
+
if (typeof value === "object") return JSON.stringify(value);
|
|
160
|
+
return String(value);
|
|
161
|
+
}
|
|
162
|
+
};
|
|
163
|
+
__decorate([property({ type: Array }), __decorateMetadata("design:type", Array)], UDataView.prototype, "items", void 0);
|
|
164
|
+
__decorate([property({ type: String }), __decorateMetadata("design:type", Object)], UDataView.prototype, "mode", void 0);
|
|
165
|
+
__decorate([property({ type: Array }), __decorateMetadata("design:type", Array)], UDataView.prototype, "columns", void 0);
|
|
166
|
+
__decorate([property({ type: String }), __decorateMetadata("design:type", Object)], UDataView.prototype, "gridMinWidth", void 0);
|
|
167
|
+
__decorate([property({ type: String }), __decorateMetadata("design:type", Object)], UDataView.prototype, "gap", void 0);
|
|
168
|
+
__decorate([property({ attribute: false }), __decorateMetadata("design:type", Function)], UDataView.prototype, "renderCard", void 0);
|
|
169
|
+
__decorate([property({ attribute: false }), __decorateMetadata("design:type", Function)], UDataView.prototype, "renderCell", void 0);
|
|
170
|
+
__decorate([state(), __decorateMetadata("design:type", Object)], UDataView.prototype, "selectedIndex", void 0);
|
|
171
|
+
UDataView = __decorate([customElement("u-data-view")], UDataView);
|
|
172
|
+
//#endregion
|
|
5
173
|
export { UDataView };
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { css } from
|
|
2
|
-
|
|
3
|
-
|
|
1
|
+
import { css } from "lit";
|
|
2
|
+
//#region src/components/data-view/UDataView.styles.ts
|
|
3
|
+
var styles = css`
|
|
4
4
|
:host {
|
|
5
5
|
display: block;
|
|
6
6
|
width: 100%;
|
|
@@ -175,85 +175,6 @@ const styles = css`
|
|
|
175
175
|
color: var(--u-txt-color-weak);
|
|
176
176
|
font-size: 1rem;
|
|
177
177
|
}
|
|
178
|
-
|
|
179
|
-
/* ── Dark mode ──
|
|
180
|
-
외부에서 --u-* CSS 변수가 제공되면 그 값을 사용하고,
|
|
181
|
-
제공되지 않으면 자체 dark fallback 값을 적용한다. */
|
|
182
|
-
|
|
183
|
-
:host-context([theme="dark"]) .toolbar {
|
|
184
|
-
background: var(--u-bg-color, #121212);
|
|
185
|
-
border-color: var(--u-border-color, #3D3D3D);
|
|
186
|
-
}
|
|
187
|
-
|
|
188
|
-
:host-context([theme="dark"]) .view-toggles u-button[active] {
|
|
189
|
-
background: var(--u-blue-600, #87B8F5);
|
|
190
|
-
color: var(--u-neutral-0, #000000);
|
|
191
|
-
}
|
|
192
|
-
|
|
193
|
-
:host-context([theme="dark"]) .info {
|
|
194
|
-
color: var(--u-txt-color-weak, #8A8A8A);
|
|
195
|
-
}
|
|
196
|
-
|
|
197
|
-
:host-context([theme="dark"]) .card {
|
|
198
|
-
background: var(--u-bg-color, #121212);
|
|
199
|
-
border-color: var(--u-border-color, #3D3D3D);
|
|
200
|
-
}
|
|
201
|
-
|
|
202
|
-
:host-context([theme="dark"]) .card:hover {
|
|
203
|
-
border-color: var(--u-blue-600, #87B8F5);
|
|
204
|
-
box-shadow: 0 4px 12px var(--u-shadow-color-weak, rgba(0, 0, 0, 0.25));
|
|
205
|
-
}
|
|
206
|
-
|
|
207
|
-
:host-context([theme="dark"]) .card.selected {
|
|
208
|
-
border-color: var(--u-blue-600, #87B8F5);
|
|
209
|
-
background: var(--u-bg-color-active, #3D3D3D);
|
|
210
|
-
box-shadow: 0 0 0 3px var(--u-blue-200, #2B4F7E);
|
|
211
|
-
}
|
|
212
|
-
|
|
213
|
-
:host-context([theme="dark"]) .card-field .label {
|
|
214
|
-
color: var(--u-txt-color-weak, #8A8A8A);
|
|
215
|
-
}
|
|
216
|
-
|
|
217
|
-
:host-context([theme="dark"]) .card-field .value {
|
|
218
|
-
color: var(--u-txt-color, #D4D4D4);
|
|
219
|
-
}
|
|
220
|
-
|
|
221
|
-
:host-context([theme="dark"]) .table-wrapper {
|
|
222
|
-
border-color: var(--u-border-color, #3D3D3D);
|
|
223
|
-
}
|
|
224
|
-
|
|
225
|
-
:host-context([theme="dark"]) table {
|
|
226
|
-
background: var(--u-bg-color, #121212);
|
|
227
|
-
}
|
|
228
|
-
|
|
229
|
-
:host-context([theme="dark"]) thead {
|
|
230
|
-
background: var(--u-neutral-50, #0A0A0A);
|
|
231
|
-
}
|
|
232
|
-
|
|
233
|
-
:host-context([theme="dark"]) th {
|
|
234
|
-
color: var(--u-txt-color-weak, #8A8A8A);
|
|
235
|
-
border-bottom-color: var(--u-border-color, #3D3D3D);
|
|
236
|
-
}
|
|
237
|
-
|
|
238
|
-
:host-context([theme="dark"]) tbody tr:hover {
|
|
239
|
-
background: var(--u-bg-color-hover, #2A2A2A);
|
|
240
|
-
}
|
|
241
|
-
|
|
242
|
-
:host-context([theme="dark"]) tbody tr.selected {
|
|
243
|
-
background: var(--u-bg-color-active, #3D3D3D);
|
|
244
|
-
}
|
|
245
|
-
|
|
246
|
-
:host-context([theme="dark"]) tbody tr:not(:last-child) {
|
|
247
|
-
border-bottom-color: var(--u-border-color-weak, #2A2A2A);
|
|
248
|
-
}
|
|
249
|
-
|
|
250
|
-
:host-context([theme="dark"]) td {
|
|
251
|
-
color: var(--u-txt-color, #D4D4D4);
|
|
252
|
-
}
|
|
253
|
-
|
|
254
|
-
:host-context([theme="dark"]) .empty {
|
|
255
|
-
color: var(--u-txt-color-weak, #8A8A8A);
|
|
256
|
-
}
|
|
257
178
|
`;
|
|
258
|
-
|
|
179
|
+
//#endregion
|
|
259
180
|
export { styles };
|
|
@@ -1,8 +1,172 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { TemplateResult } from 'lit';
|
|
2
|
+
import { UElement } from '@iyulab/components/dist/components/UElement.js';
|
|
3
|
+
export interface SheetColumn {
|
|
4
|
+
/** 데이터 키 (getDataAsObjects() 반환시 객체 키로 사용) */
|
|
5
|
+
key?: string;
|
|
6
|
+
/** 헤더 표시 레이블 */
|
|
7
|
+
label?: string;
|
|
8
|
+
/** 열 너비 (px) */
|
|
9
|
+
width?: number;
|
|
10
|
+
/** 읽기 전용 열 */
|
|
11
|
+
readonly?: boolean;
|
|
12
|
+
/** 선택 가능한 옵션 목록 (정적 배열 또는 동적 콜백) */
|
|
13
|
+
options?: string[] | ((row: number, col: number) => string[]);
|
|
14
|
+
/** true이면 목록에 있는 값만 입력 가능 (기본: false = 자유 입력 허용) */
|
|
15
|
+
strict?: boolean;
|
|
16
|
+
/** 자동 계산 함수. 설정 시 해당 열은 자동 readonly. 열 순서(좌→우), 행 순서(위→아래)로 계산. */
|
|
17
|
+
compute?: (rowIndex: number, data: string[][]) => string;
|
|
18
|
+
/** 표시 포맷. Intl.NumberFormatOptions(숫자) 또는 커스텀 콜백. 원본 데이터는 유지. */
|
|
19
|
+
format?: Intl.NumberFormatOptions | ((value: string, rowIndex: number) => string);
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* USimpleSheet - 심플 스프레드시트 컴포넌트
|
|
23
|
+
*
|
|
24
|
+
* 편집 편의 기능:
|
|
25
|
+
* - Undo/Redo: Ctrl+Z / Ctrl+Y / Ctrl+Shift+Z
|
|
26
|
+
* - Ctrl+Enter: 편집 확정 후 현재 셀 유지
|
|
27
|
+
* - Shift+Enter: 위로 이동
|
|
28
|
+
* - Home/End: 행 처음/끝 (Ctrl+Home/End: 시트 처음/끝)
|
|
29
|
+
* - Page Up/Down: 10행 단위 이동
|
|
30
|
+
* - Ctrl+A: 전체 선택
|
|
31
|
+
* - Tab 사이클: 마지막 열 → 다음 행 첫 열
|
|
32
|
+
* - Ctrl+D: Fill Down (선택 영역 첫 행 값 아래로 채우기)
|
|
33
|
+
* - Ctrl+R: Fill Right (선택 영역 첫 열 값 오른쪽으로 채우기)
|
|
34
|
+
* - 행/열 헤더 클릭: 전체 행/열 선택
|
|
35
|
+
* - 엑셀 호환 Ctrl+C/V (TSV), Delete/Backspace
|
|
36
|
+
* - 다중 셀 선택: Shift+클릭, Shift+Arrow, 마우스 드래그
|
|
37
|
+
* - columns 미설정시 A, B, C... 자동 헤더
|
|
38
|
+
*/
|
|
39
|
+
export declare class USimpleSheet extends UElement {
|
|
40
|
+
static styles: import('lit').CSSResultGroup[];
|
|
41
|
+
/** 초기 데이터 (2D 문자열 배열) */
|
|
42
|
+
data: string[][];
|
|
43
|
+
/** 열 정의 (미설정시 A, B, C... 자동 헤더) */
|
|
44
|
+
columns?: SheetColumn[];
|
|
45
|
+
/** 초기 행 수 */
|
|
46
|
+
rows: number;
|
|
47
|
+
/** 초기 열 수 (columns 미설정시 사용) */
|
|
48
|
+
cols: number;
|
|
49
|
+
/** 읽기 전용 모드 */
|
|
50
|
+
readonly: boolean;
|
|
51
|
+
private _sel;
|
|
52
|
+
private _editing;
|
|
53
|
+
private _editVal;
|
|
54
|
+
private _replaceOnEdit;
|
|
55
|
+
private _dropdownItems;
|
|
56
|
+
private _dropdownIndex;
|
|
57
|
+
private _isDropdownClick;
|
|
58
|
+
private _data;
|
|
59
|
+
private _colWidths;
|
|
60
|
+
private _isMouseSelecting;
|
|
61
|
+
private _containerEl;
|
|
62
|
+
private _resizing;
|
|
63
|
+
private static readonly MIN_COL_WIDTH;
|
|
64
|
+
private static readonly DEFAULT_COL_WIDTH;
|
|
65
|
+
private _history;
|
|
66
|
+
private _historyIndex;
|
|
67
|
+
private static readonly HISTORY_LIMIT;
|
|
68
|
+
connectedCallback(): void;
|
|
69
|
+
disconnectedCallback(): void;
|
|
70
|
+
protected willUpdate(changed: Map<string, unknown>): void;
|
|
71
|
+
private _syncDataFromProps;
|
|
72
|
+
private _syncData;
|
|
73
|
+
private get _rowCount();
|
|
74
|
+
private get _colCount();
|
|
75
|
+
private _pushHistory;
|
|
76
|
+
private _undo;
|
|
77
|
+
private _redo;
|
|
78
|
+
/** 열 인덱스를 A, B, ..., Z, AA, AB, ... 형태로 변환 */
|
|
79
|
+
private _colLabel;
|
|
80
|
+
private _colWidthStyle;
|
|
81
|
+
private _inSel;
|
|
82
|
+
private _isAnchor;
|
|
83
|
+
private _isColSelected;
|
|
84
|
+
private _isRowSelected;
|
|
85
|
+
render(): TemplateResult<1>;
|
|
86
|
+
private _renderCell;
|
|
87
|
+
private _refContainer;
|
|
88
|
+
private _onContainerFocus;
|
|
89
|
+
private _onContainerKeyDown;
|
|
90
|
+
private _onInputChange;
|
|
91
|
+
private _onInputKeyDown;
|
|
92
|
+
private _onInputBlur;
|
|
93
|
+
private _onDropdownItemMouseDown;
|
|
94
|
+
private _onTableMouseDown;
|
|
95
|
+
private _onTableMouseOver;
|
|
96
|
+
private _onTableDblClick;
|
|
97
|
+
private _onDocMouseUp;
|
|
98
|
+
/** 열 너비 조절 시작 */
|
|
99
|
+
private _onResizeStart;
|
|
100
|
+
private _onResizeMove;
|
|
101
|
+
private _onResizeEnd;
|
|
102
|
+
/** 코너 클릭 → 전체 선택 */
|
|
103
|
+
private _onSelectAll;
|
|
104
|
+
/** 열 헤더 클릭 → 전체 열 선택 */
|
|
105
|
+
private _onColHeaderMouseDown;
|
|
106
|
+
/** 행 헤더 클릭 → 전체 행 선택 */
|
|
107
|
+
private _onRowHeaderMouseDown;
|
|
108
|
+
private _onCopy;
|
|
109
|
+
private _onPaste;
|
|
110
|
+
private _selectionToTSV;
|
|
111
|
+
private _pasteFromText;
|
|
112
|
+
/** Ctrl+D: 선택 영역 첫 행 값을 아래로 채우기 */
|
|
113
|
+
private _fillDown;
|
|
114
|
+
/** Ctrl+R: 선택 영역 첫 열 값을 오른쪽으로 채우기 */
|
|
115
|
+
private _fillRight;
|
|
116
|
+
/**
|
|
117
|
+
* @param typedChar - 타이핑으로 시작할 경우 첫 글자. 미제공시 기존 값 유지.
|
|
118
|
+
*/
|
|
119
|
+
private _startEdit;
|
|
120
|
+
private _commitEdit;
|
|
121
|
+
private _cancelEdit;
|
|
122
|
+
private _clearSelection;
|
|
123
|
+
private _select;
|
|
124
|
+
private _scrollCellIntoView;
|
|
125
|
+
private _getCellFromEvent;
|
|
126
|
+
private _isColReadonly;
|
|
127
|
+
private _isColComputed;
|
|
128
|
+
private _isNumeric;
|
|
129
|
+
/** 열의 format 설정에 따라 표시값을 반환. format 미설정 시 원본 반환. */
|
|
130
|
+
private _formatValue;
|
|
131
|
+
/** compute 열을 재계산 (열 순서 좌→우, 행 순서 위→아래) */
|
|
132
|
+
private _recompute;
|
|
133
|
+
private _getColOptions;
|
|
134
|
+
private _filterOptions;
|
|
135
|
+
private _emitChange;
|
|
136
|
+
/** 현재 데이터를 2D 배열로 반환 */
|
|
137
|
+
getData(): string[][];
|
|
138
|
+
/**
|
|
139
|
+
* 정의된 columns의 key를 기준으로 객체 배열로 반환.
|
|
140
|
+
* columns 미설정시 A, B, C... 키를 사용.
|
|
141
|
+
*/
|
|
142
|
+
getDataAsObjects(): Record<string, string>[];
|
|
143
|
+
/** 데이터를 직접 설정하고 재렌더링 */
|
|
144
|
+
setData(data: string[][]): void;
|
|
145
|
+
/** 특정 셀 값 설정 */
|
|
146
|
+
setCell(row: number, col: number, value: string): void;
|
|
147
|
+
/** 현재 선택 영역 반환 */
|
|
148
|
+
getSelection(): {
|
|
149
|
+
minRow: number;
|
|
150
|
+
maxRow: number;
|
|
151
|
+
minCol: number;
|
|
152
|
+
maxCol: number;
|
|
153
|
+
} | null;
|
|
154
|
+
/** 선택 영역을 프로그래밍 방식으로 설정 */
|
|
155
|
+
setSelection(range: {
|
|
156
|
+
minRow: number;
|
|
157
|
+
maxRow: number;
|
|
158
|
+
minCol: number;
|
|
159
|
+
maxCol: number;
|
|
160
|
+
}): void;
|
|
161
|
+
/** 전체 셀 선택 */
|
|
162
|
+
selectAll(): void;
|
|
163
|
+
/** Undo 가능 여부 */
|
|
164
|
+
get canUndo(): boolean;
|
|
165
|
+
/** Redo 가능 여부 */
|
|
166
|
+
get canRedo(): boolean;
|
|
167
|
+
}
|
|
2
168
|
declare global {
|
|
3
169
|
interface HTMLElementTagNameMap {
|
|
4
170
|
'u-simple-sheet': USimpleSheet;
|
|
5
171
|
}
|
|
6
172
|
}
|
|
7
|
-
export { USimpleSheet };
|
|
8
|
-
export type { SheetColumn } from './USimpleSheet.component.js';
|