create-mendix-widget-gleam 2.0.13 → 2.0.14
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/README.md +3 -2
- package/package.json +1 -1
- package/src/templates/readme_md.mjs +6 -6
- package/template/docs/glendix_guide.md +204 -1
- package/template/gleam.toml +1 -1
package/README.md
CHANGED
|
@@ -30,11 +30,11 @@ my-widget/
|
|
|
30
30
|
widgets/ # .mpk 위젯 파일 (glendix/widget로 바인딩)
|
|
31
31
|
bindings.json # 외부 React 컴포넌트 바인딩 설정
|
|
32
32
|
package.json # npm 의존성 (React, 외부 라이브러리 등)
|
|
33
|
-
gleam.toml # Gleam 프로젝트 설정 (glendix >= 2.0.
|
|
33
|
+
gleam.toml # Gleam 프로젝트 설정 (glendix >= 2.0.14 의존성 포함)
|
|
34
34
|
CLAUDE.md # AI 어시스턴트용 프로젝트 컨텍스트
|
|
35
35
|
```
|
|
36
36
|
|
|
37
|
-
React/Mendix FFI 바인딩은 프로젝트에 포함되지 않으며, [glendix](https://hexdocs.pm/glendix/) Hex 패키지로 제공된다.
|
|
37
|
+
React/Mendix FFI 및 JS Interop 바인딩은 프로젝트에 포함되지 않으며, [glendix](https://hexdocs.pm/glendix/) Hex 패키지로 제공된다.
|
|
38
38
|
|
|
39
39
|
## 생성 후 시작하기
|
|
40
40
|
|
|
@@ -52,6 +52,7 @@ gleam run -m glendix/marketplace # Marketplace 위젯 검색/다운로드
|
|
|
52
52
|
|
|
53
53
|
- **React** — `react`, `react/attribute`, `react/hook`, `react/event`, `react/html`, `react/svg`, `react/svg_attribute`, `binding`, `widget`
|
|
54
54
|
- **Mendix** — `mendix`, `mendix/editable_value`, `mendix/action`, `mendix/list_value`, `mendix/selection`, `mendix/reference`, `mendix/reference_set`, `mendix/date`, `mendix/big`, `mendix/filter` 등
|
|
55
|
+
- **JS Interop** — `js/array`, `js/object`, `js/json`, `js/promise`, `js/dom`, `js/timer`
|
|
55
56
|
|
|
56
57
|
## 라이선스
|
|
57
58
|
|
package/package.json
CHANGED
|
@@ -125,7 +125,7 @@ bindings.json # External React component binding configurat
|
|
|
125
125
|
package.json # npm dependencies (React, external libraries, etc.)
|
|
126
126
|
\`\`\`
|
|
127
127
|
|
|
128
|
-
React/Mendix FFI bindings are provided by the [glendix](https://hexdocs.pm/glendix/) Hex package.
|
|
128
|
+
React/Mendix FFI and JS Interop bindings are provided by the [glendix](https://hexdocs.pm/glendix/) Hex package.
|
|
129
129
|
|
|
130
130
|
## Using External React Components
|
|
131
131
|
|
|
@@ -266,7 +266,7 @@ Widget names use the \`<name>\` value from the \`.mpk\`'s internal XML, and prop
|
|
|
266
266
|
## Tech Stack
|
|
267
267
|
|
|
268
268
|
- **Gleam** → JavaScript compilation
|
|
269
|
-
- **[glendix](https://hexdocs.pm/glendix/)** — React + Mendix API Gleam bindings
|
|
269
|
+
- **[glendix](https://hexdocs.pm/glendix/)** — React + Mendix API + JS Interop Gleam bindings
|
|
270
270
|
- **Mendix Pluggable Widget** (React 19)
|
|
271
271
|
- **${pm}** — Package manager
|
|
272
272
|
|
|
@@ -379,7 +379,7 @@ bindings.json # 외부 React 컴포넌트 바인딩 설정
|
|
|
379
379
|
package.json # npm 의존성 (React, 외부 라이브러리 등)
|
|
380
380
|
\`\`\`
|
|
381
381
|
|
|
382
|
-
React/Mendix FFI 바인딩은 [glendix](https://hexdocs.pm/glendix/) Hex 패키지로 제공됩니다.
|
|
382
|
+
React/Mendix FFI 및 JS Interop 바인딩은 [glendix](https://hexdocs.pm/glendix/) Hex 패키지로 제공됩니다.
|
|
383
383
|
|
|
384
384
|
## 외부 React 컴포넌트 사용
|
|
385
385
|
|
|
@@ -520,7 +520,7 @@ switch.render(props)
|
|
|
520
520
|
## 기술 스택
|
|
521
521
|
|
|
522
522
|
- **Gleam** → JavaScript 컴파일
|
|
523
|
-
- **[glendix](https://hexdocs.pm/glendix/)** — React + Mendix API Gleam 바인딩
|
|
523
|
+
- **[glendix](https://hexdocs.pm/glendix/)** — React + Mendix API + JS Interop Gleam 바인딩
|
|
524
524
|
- **Mendix Pluggable Widget** (React 19)
|
|
525
525
|
- **${pm}** — 패키지 매니저
|
|
526
526
|
|
|
@@ -633,7 +633,7 @@ bindings.json # 外部Reactコンポーネントバイン
|
|
|
633
633
|
package.json # npm依存関係(React、外部ライブラリなど)
|
|
634
634
|
\`\`\`
|
|
635
635
|
|
|
636
|
-
React/Mendix FFIバインディングは[glendix](https://hexdocs.pm/glendix/) Hexパッケージとして提供される。
|
|
636
|
+
React/Mendix FFIおよびJS Interopバインディングは[glendix](https://hexdocs.pm/glendix/) Hexパッケージとして提供される。
|
|
637
637
|
|
|
638
638
|
## 外部Reactコンポーネントの使用
|
|
639
639
|
|
|
@@ -774,7 +774,7 @@ switch.render(props)
|
|
|
774
774
|
## 技術スタック
|
|
775
775
|
|
|
776
776
|
- **Gleam** → JavaScriptコンパイル
|
|
777
|
-
- **[glendix](https://hexdocs.pm/glendix/)** — React + Mendix API Gleamバインディング
|
|
777
|
+
- **[glendix](https://hexdocs.pm/glendix/)** — React + Mendix API + JS Interop Gleamバインディング
|
|
778
778
|
- **Mendix Pluggable Widget**(React 19)
|
|
779
779
|
- **${pm}** — パッケージマネージャー
|
|
780
780
|
|
|
@@ -2179,7 +2179,210 @@ camera_widget.render(props)
|
|
|
2179
2179
|
|
|
2180
2180
|
---
|
|
2181
2181
|
|
|
2182
|
-
## 6.
|
|
2182
|
+
## 6. JS Interop — 외부 JS 라이브러리 연동
|
|
2183
|
+
|
|
2184
|
+
`glendix/js/` 모듈은 외부 JavaScript 라이브러리(SpreadJS, Chart.js 등)와 직접 상호작용할 때 사용하는 저수준 interop API를 제공합니다. React 컴포넌트 바인딩(`glendix/binding`)으로 해결되지 않는 경우의 escape hatch입니다.
|
|
2185
|
+
|
|
2186
|
+
> 모든 JS 값은 `Dynamic` 타입으로 표현됩니다. 타입 안전성보다 유연성을 우선하는 설계이므로, 가능하면 `glendix/binding`이나 전용 opaque 래퍼를 먼저 고려하세요.
|
|
2187
|
+
|
|
2188
|
+
### 6.1 배열 변환 — `js/array`
|
|
2189
|
+
|
|
2190
|
+
Gleam List ↔ JS Array 변환 유틸리티입니다.
|
|
2191
|
+
|
|
2192
|
+
```gleam
|
|
2193
|
+
import gleam/dynamic.{type Dynamic}
|
|
2194
|
+
import glendix/js/array
|
|
2195
|
+
|
|
2196
|
+
// Gleam List → JS Array (Dynamic)
|
|
2197
|
+
let js_arr = array.from_list([1, 2, 3])
|
|
2198
|
+
|
|
2199
|
+
// JS Array → Gleam List
|
|
2200
|
+
let gleam_list: List(Int) = array.to_list(js_arr)
|
|
2201
|
+
```
|
|
2202
|
+
|
|
2203
|
+
### 6.2 객체 조작 — `js/object`
|
|
2204
|
+
|
|
2205
|
+
JS 객체 생성, 속성 접근, 메서드 호출을 제공합니다.
|
|
2206
|
+
|
|
2207
|
+
```gleam
|
|
2208
|
+
import gleam/dynamic
|
|
2209
|
+
import glendix/js/object
|
|
2210
|
+
|
|
2211
|
+
// 객체 생성
|
|
2212
|
+
let config = object.object([
|
|
2213
|
+
#("width", dynamic.from(800)),
|
|
2214
|
+
#("height", dynamic.from(600)),
|
|
2215
|
+
#("editable", dynamic.from(True)),
|
|
2216
|
+
])
|
|
2217
|
+
|
|
2218
|
+
// 빈 객체
|
|
2219
|
+
let obj = object.empty()
|
|
2220
|
+
|
|
2221
|
+
// 속성 읽기/쓰기 (set은 mutation — 원본 반환)
|
|
2222
|
+
let width = object.get(config, "width")
|
|
2223
|
+
let config = object.set(config, "theme", dynamic.from("dark"))
|
|
2224
|
+
|
|
2225
|
+
// 속성 삭제/존재 확인
|
|
2226
|
+
let config = object.delete(config, "editable")
|
|
2227
|
+
let has_theme = object.has(config, "theme") // True
|
|
2228
|
+
|
|
2229
|
+
// 메서드 호출
|
|
2230
|
+
let result = object.call_method(spreadsheet, "getCell", [
|
|
2231
|
+
dynamic.from(0),
|
|
2232
|
+
dynamic.from(0),
|
|
2233
|
+
])
|
|
2234
|
+
let value = object.call_method_0(cell, "getValue")
|
|
2235
|
+
|
|
2236
|
+
// new 연산자로 인스턴스 생성
|
|
2237
|
+
let instance = object.new_instance(constructor, [
|
|
2238
|
+
dynamic.from("arg1"),
|
|
2239
|
+
])
|
|
2240
|
+
```
|
|
2241
|
+
|
|
2242
|
+
### 6.3 JSON — `js/json`
|
|
2243
|
+
|
|
2244
|
+
JSON 직렬화/역직렬화입니다.
|
|
2245
|
+
|
|
2246
|
+
```gleam
|
|
2247
|
+
import gleam/dynamic
|
|
2248
|
+
import glendix/js/json
|
|
2249
|
+
|
|
2250
|
+
// 직렬화
|
|
2251
|
+
let json_str = json.stringify(dynamic.from(config))
|
|
2252
|
+
|
|
2253
|
+
// 역직렬화 (Result 반환)
|
|
2254
|
+
case json.parse("{\"key\": \"value\"}") {
|
|
2255
|
+
Ok(data) -> object.get(data, "key")
|
|
2256
|
+
Error(msg) -> // 파싱 에러 메시지
|
|
2257
|
+
}
|
|
2258
|
+
```
|
|
2259
|
+
|
|
2260
|
+
### 6.4 Promise — `js/promise`
|
|
2261
|
+
|
|
2262
|
+
Promise 체이닝, 에러 처리, 병렬 실행을 제공합니다. `glendix/react`의 `Promise(a)` 타입을 사용합니다.
|
|
2263
|
+
|
|
2264
|
+
```gleam
|
|
2265
|
+
import gleam/dynamic.{type Dynamic}
|
|
2266
|
+
import glendix/js/promise
|
|
2267
|
+
import glendix/react.{type Promise}
|
|
2268
|
+
|
|
2269
|
+
// 즉시 이행/거부
|
|
2270
|
+
let p = promise.resolve(42)
|
|
2271
|
+
let err = promise.reject("something went wrong")
|
|
2272
|
+
|
|
2273
|
+
// 체이닝 (flatMap)
|
|
2274
|
+
promise.then_(fetch_data(), fn(data) {
|
|
2275
|
+
promise.resolve(transform(data))
|
|
2276
|
+
})
|
|
2277
|
+
|
|
2278
|
+
// 값 변환 (map)
|
|
2279
|
+
promise.map(fetch_data(), fn(data) {
|
|
2280
|
+
object.get(data, "name")
|
|
2281
|
+
})
|
|
2282
|
+
|
|
2283
|
+
// 에러 처리
|
|
2284
|
+
promise.catch_(risky_operation(), fn(error: Dynamic) {
|
|
2285
|
+
promise.resolve(fallback_value)
|
|
2286
|
+
})
|
|
2287
|
+
|
|
2288
|
+
// 병렬 실행 (모든 Promise 대기)
|
|
2289
|
+
promise.all([fetch_users(), fetch_roles()])
|
|
2290
|
+
|> promise.map(fn(results) {
|
|
2291
|
+
// results: List(a)
|
|
2292
|
+
})
|
|
2293
|
+
|
|
2294
|
+
// 가장 빠른 결과
|
|
2295
|
+
promise.race([fetch_from_primary(), fetch_from_backup()])
|
|
2296
|
+
|
|
2297
|
+
// fire-and-forget 콜백
|
|
2298
|
+
promise.await_(save_data(), fn(result) {
|
|
2299
|
+
// 이행 시 실행, 반환값 무시
|
|
2300
|
+
Nil
|
|
2301
|
+
})
|
|
2302
|
+
```
|
|
2303
|
+
|
|
2304
|
+
### 6.5 DOM 조작 — `js/dom`
|
|
2305
|
+
|
|
2306
|
+
DOM 요소에 대한 직접 조작 유틸리티입니다. `hook.use_ref`로 얻은 ref의 current 값과 함께 사용합니다.
|
|
2307
|
+
|
|
2308
|
+
```gleam
|
|
2309
|
+
import glendix/js/dom
|
|
2310
|
+
import glendix/react/hook
|
|
2311
|
+
|
|
2312
|
+
let input_ref = hook.use_ref(Nil)
|
|
2313
|
+
|
|
2314
|
+
// 포커스/블러/클릭
|
|
2315
|
+
dom.focus(hook.get_ref(input_ref))
|
|
2316
|
+
dom.blur(hook.get_ref(input_ref))
|
|
2317
|
+
dom.click(hook.get_ref(input_ref))
|
|
2318
|
+
|
|
2319
|
+
// 스크롤
|
|
2320
|
+
dom.scroll_into_view(hook.get_ref(input_ref))
|
|
2321
|
+
|
|
2322
|
+
// 위치/크기 정보 (DOMRect)
|
|
2323
|
+
let rect = dom.get_bounding_client_rect(hook.get_ref(input_ref))
|
|
2324
|
+
|
|
2325
|
+
// CSS 선택자로 하위 요소 검색
|
|
2326
|
+
case dom.query_selector(hook.get_ref(container_ref), ".target") {
|
|
2327
|
+
Some(el) -> dom.focus(el)
|
|
2328
|
+
None -> Nil
|
|
2329
|
+
}
|
|
2330
|
+
```
|
|
2331
|
+
|
|
2332
|
+
### 6.6 타이머 — `js/timer`
|
|
2333
|
+
|
|
2334
|
+
setTimeout/setInterval 래퍼입니다. `TimerId`는 opaque type으로 숫자 조작을 방지합니다.
|
|
2335
|
+
|
|
2336
|
+
```gleam
|
|
2337
|
+
import glendix/js/timer
|
|
2338
|
+
|
|
2339
|
+
// 지연 실행
|
|
2340
|
+
let id = timer.set_timeout(fn() {
|
|
2341
|
+
// 1초 후 실행
|
|
2342
|
+
Nil
|
|
2343
|
+
}, 1000)
|
|
2344
|
+
|
|
2345
|
+
// 취소
|
|
2346
|
+
timer.clear_timeout(id)
|
|
2347
|
+
|
|
2348
|
+
// 반복 실행
|
|
2349
|
+
let interval_id = timer.set_interval(fn() {
|
|
2350
|
+
// 500ms마다 실행
|
|
2351
|
+
Nil
|
|
2352
|
+
}, 500)
|
|
2353
|
+
|
|
2354
|
+
// 반복 취소
|
|
2355
|
+
timer.clear_interval(interval_id)
|
|
2356
|
+
```
|
|
2357
|
+
|
|
2358
|
+
#### useEffect와 함께 사용 (클린업 패턴)
|
|
2359
|
+
|
|
2360
|
+
```gleam
|
|
2361
|
+
hook.use_effect_once_cleanup(fn() {
|
|
2362
|
+
let id = timer.set_interval(fn() {
|
|
2363
|
+
update_count(fn(prev) { prev + 1 })
|
|
2364
|
+
Nil
|
|
2365
|
+
}, 1000)
|
|
2366
|
+
|
|
2367
|
+
// 언마운트 시 타이머 정리
|
|
2368
|
+
fn() { timer.clear_interval(id) }
|
|
2369
|
+
})
|
|
2370
|
+
```
|
|
2371
|
+
|
|
2372
|
+
### 6.7 모듈 요약
|
|
2373
|
+
|
|
2374
|
+
| 모듈 | 용도 | FFI |
|
|
2375
|
+
|------|------|-----|
|
|
2376
|
+
| `js/array` | Gleam List ↔ JS Array 변환 | 없음 (react_ffi.mjs 재사용) |
|
|
2377
|
+
| `js/object` | 객체 생성/속성 CRUD/메서드 호출 | object_ffi.mjs |
|
|
2378
|
+
| `js/json` | JSON stringify/parse | json_ffi.mjs |
|
|
2379
|
+
| `js/promise` | Promise 체이닝/병렬/에러 처리 | promise_ffi.mjs |
|
|
2380
|
+
| `js/dom` | DOM 포커스/클릭/스크롤/쿼리 | dom_ffi.mjs |
|
|
2381
|
+
| `js/timer` | setTimeout/setInterval | timer_ffi.mjs |
|
|
2382
|
+
|
|
2383
|
+
---
|
|
2384
|
+
|
|
2385
|
+
## 7. 트러블슈팅
|
|
2183
2386
|
|
|
2184
2387
|
### 빌드 에러
|
|
2185
2388
|
|
package/template/gleam.toml
CHANGED