create-mendix-widget-gleam 3.0.2 → 4.0.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/README.md +7 -9
- package/package.json +1 -1
- package/src/i18n.mjs +12 -15
- package/src/index.mjs +20 -33
- package/src/templates/claude_md.mjs +9 -8
- package/src/templates/readme_md.mjs +45 -318
- package/template/docs/glendix_guide.md +178 -131
- package/template/docs/mendraw_guide.md +328 -0
- package/template/gleam.toml +3 -1
- package/template/package.json +1 -6
- package/template/src/__widget_name__.gleam +1 -1
- package/template/src/editor_config.gleam +1 -1
- package/template/src/editor_preview.gleam +1 -1
- package/src/templates/widgets_readme.mjs +0 -275
- package/template/rollup.config.mjs +0 -10
- package/template/widgets/README.md +0 -1
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# glendix
|
|
1
|
+
# glendix v4.0.3 — Agent Reference Guide
|
|
2
2
|
|
|
3
3
|
> 이 문서는 AI 에이전트(LLM)가 glendix 코드를 작성할 때 참조하는 가이드입니다. 각 섹션은 독립적으로 읽을 수 있습니다.
|
|
4
4
|
|
|
@@ -14,9 +14,10 @@ glendix는 Gleam으로 Mendix Pluggable Widget을 작성하는 FFI 라이브러
|
|
|
14
14
|
|--------|------------|-------------|
|
|
15
15
|
| React 바인딩 (엘리먼트, 훅, 이벤트, HTML/SVG) | `redraw`, `redraw_dom` | 사용하지 않음 — 직접 import |
|
|
16
16
|
| TEA 패턴 (Model-Update-View) | `lustre` | `glendix/lustre` 브릿지 제공 |
|
|
17
|
-
| Mendix API (JsProps, EditableValue, ListValue 등) | `
|
|
18
|
-
| 외부 JS 컴포넌트 (widget, binding) → React | `
|
|
19
|
-
|
|
|
17
|
+
| Mendix API (JsProps, EditableValue, ListValue 등) | `mendraw` | 핵심 담당 |
|
|
18
|
+
| 외부 JS 컴포넌트 (widget, binding) → React | `mendraw/interop` | 브릿지 제공 |
|
|
19
|
+
| Mendix 위젯 (.mpk, Classic, 마켓플레이스) | `mendraw` | 핵심 담당 |
|
|
20
|
+
| 빌드/설치 | `glendix` | 핵심 담당 |
|
|
20
21
|
|
|
21
22
|
**의존성 구조:**
|
|
22
23
|
|
|
@@ -25,13 +26,15 @@ glendix는 Gleam으로 Mendix Pluggable Widget을 작성하는 FFI 라이브러
|
|
|
25
26
|
├── redraw ← React 훅, 컴포넌트, fragment 등
|
|
26
27
|
├── redraw_dom ← HTML/SVG 태그, 속성, 이벤트
|
|
27
28
|
├── lustre ← TEA update/view (선택)
|
|
29
|
+
├── mendraw
|
|
30
|
+
│ ├── mendix ← Mendix API 타입 + props 접근
|
|
31
|
+
│ ├── interop ← 외부 JS 컴포넌트 → redraw.Element
|
|
32
|
+
│ ├── widget ← .mpk 위젯 컴포넌트 (gleam.toml 자동 다운로드)
|
|
33
|
+
│ ├── classic ← Classic (Dojo) 위젯
|
|
34
|
+
│ └── marketplace ← Mendix Marketplace 검색/다운로드
|
|
28
35
|
└── glendix
|
|
29
|
-
├── mendix ← Mendix API 타입 + props 접근
|
|
30
|
-
├── interop ← 외부 JS 컴포넌트 → redraw.Element
|
|
31
36
|
├── lustre ← Lustre Element → redraw.Element 브릿지
|
|
32
|
-
├──
|
|
33
|
-
├── binding ← bindings.json 외부 React 컴포넌트
|
|
34
|
-
├── classic ← Classic (Dojo) 위젯
|
|
37
|
+
├── binding ← 외부 React 컴포넌트 (gleam.toml [tools.glendix.bindings])
|
|
35
38
|
└── js/* ← JS interop escape hatch
|
|
36
39
|
```
|
|
37
40
|
|
|
@@ -43,14 +46,15 @@ glendix는 Gleam으로 Mendix Pluggable Widget을 작성하는 FFI 라이브러
|
|
|
43
46
|
|
|
44
47
|
```toml
|
|
45
48
|
[dependencies]
|
|
46
|
-
glendix = ">=
|
|
49
|
+
glendix = ">= 4.0.3 and < 5.0.0"
|
|
50
|
+
mendraw = ">= 1.1.11 and < 2.0.0"
|
|
47
51
|
```
|
|
48
52
|
|
|
49
53
|
Peer dependency (위젯 프로젝트 `package.json`):
|
|
50
54
|
|
|
51
55
|
```json
|
|
52
56
|
{
|
|
53
|
-
"dependencies": { "big.js": "^6.0.0" },
|
|
57
|
+
"dependencies": { "big.js": "^6.0.0" }, // decimal 속성 사용 시
|
|
54
58
|
"overrides": { "react": "19.0.0", "react-dom": "19.0.0", "@types/react": "19.0.0", "@types/react-dom": "19.0.0" },
|
|
55
59
|
"resolutions": { "react": "19.0.0", "react-dom": "19.0.0", "@types/react": "19.0.0", "@types/react-dom": "19.0.0" }
|
|
56
60
|
}
|
|
@@ -60,7 +64,7 @@ Peer dependency (위젯 프로젝트 `package.json`):
|
|
|
60
64
|
> - `overrides`/`resolutions`에서 반드시 **캐럿(`^`) 없이 정확한 버전**을 지정한다. `^19.0.0`은 react와 react-dom이 서로 다른 19.x.x로 해석되어 런타임 버전 불일치 에러를 일으킨다.
|
|
61
65
|
|
|
62
66
|
```bash
|
|
63
|
-
gleam run -m glendix/install # 의존성 설치 + 바인딩 생성
|
|
67
|
+
gleam run -m glendix/install # 의존성 설치 + TOML 위젯 다운로드 + 바인딩 생성
|
|
64
68
|
gleam build # 컴파일 확인
|
|
65
69
|
```
|
|
66
70
|
|
|
@@ -71,13 +75,13 @@ gleam build # 컴파일 확인
|
|
|
71
75
|
모든 Mendix Pluggable Widget은 이 시그니처를 따릅니다:
|
|
72
76
|
|
|
73
77
|
```gleam
|
|
74
|
-
import
|
|
78
|
+
import mendraw/mendix.{type JsProps}
|
|
75
79
|
import redraw.{type Element}
|
|
76
80
|
|
|
77
81
|
pub fn widget(props: JsProps) -> Element
|
|
78
82
|
```
|
|
79
83
|
|
|
80
|
-
- `JsProps` — Mendix가 전달하는 props 객체 (opaque). `
|
|
84
|
+
- `JsProps` — Mendix가 전달하는 props 객체 (opaque). `mendraw/mendix` 모듈의 접근자로만 읽는다.
|
|
81
85
|
- `Element` — redraw의 React 엘리먼트 타입. `redraw/dom/html`, `redraw.fragment()` 등으로 생성한다.
|
|
82
86
|
|
|
83
87
|
---
|
|
@@ -103,7 +107,7 @@ glendix는 두 가지 렌더링 경로를 지원합니다. 둘 다 `redraw.Eleme
|
|
|
103
107
|
### 5.1 필수 import 패턴
|
|
104
108
|
|
|
105
109
|
```gleam
|
|
106
|
-
import
|
|
110
|
+
import mendraw/mendix.{type JsProps} // Mendix props 타입
|
|
107
111
|
import redraw.{type Element} // 반환 타입
|
|
108
112
|
import redraw/dom/html // HTML 태그 함수
|
|
109
113
|
import redraw/dom/attribute // HTML 속성
|
|
@@ -311,7 +315,7 @@ svg.svg([attribute.attribute("viewBox", "0 0 100 100")], [
|
|
|
311
315
|
```gleam
|
|
312
316
|
import gleam/int
|
|
313
317
|
import glendix/lustre as gl
|
|
314
|
-
import
|
|
318
|
+
import mendraw/mendix.{type JsProps}
|
|
315
319
|
import lustre/effect
|
|
316
320
|
import lustre/element/html
|
|
317
321
|
import lustre/event
|
|
@@ -411,27 +415,23 @@ fn view(model: Model) {
|
|
|
411
415
|
|
|
412
416
|
| 컴포넌트 출처 | 사용 모듈 | 예시 |
|
|
413
417
|
|--------------|----------|------|
|
|
414
|
-
| npm 패키지 (React 컴포넌트) | `glendix/binding` + `
|
|
415
|
-
| `.mpk`
|
|
416
|
-
| `.mpk` Classic (Dojo) 위젯 | `glendix/classic` | CameraWidget.mpk |
|
|
418
|
+
| npm 패키지 (React 컴포넌트) | `glendix/binding` + `mendraw/interop` | recharts, @mui |
|
|
419
|
+
| `.mpk` Classic (Dojo) 위젯 (gleam.toml) | `mendraw/classic` | CameraWidget |
|
|
417
420
|
|
|
418
421
|
### 7.2 외부 React 컴포넌트 (binding + interop)
|
|
419
422
|
|
|
420
|
-
**설정:** `
|
|
423
|
+
**설정:** `gleam.toml`에 바인딩 추가 → `npm install 패키지명` → `gleam run -m glendix/install`
|
|
421
424
|
|
|
422
|
-
```
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
"components": ["PieChart", "Pie", "Cell", "Tooltip"]
|
|
426
|
-
}
|
|
427
|
-
}
|
|
425
|
+
```toml
|
|
426
|
+
[tools.glendix.bindings]
|
|
427
|
+
recharts = ["PieChart", "Pie", "Cell", "Tooltip"]
|
|
428
428
|
```
|
|
429
429
|
|
|
430
430
|
**Gleam 래퍼 작성:**
|
|
431
431
|
|
|
432
432
|
```gleam
|
|
433
433
|
import glendix/binding
|
|
434
|
-
import
|
|
434
|
+
import mendraw/interop
|
|
435
435
|
import redraw.{type Element}
|
|
436
436
|
import redraw/dom/attribute.{type Attribute}
|
|
437
437
|
|
|
@@ -454,48 +454,6 @@ pub fn tooltip(attrs: List(Attribute)) -> Element {
|
|
|
454
454
|
| `interop.component_el_(comp, children)` | 자식만 |
|
|
455
455
|
| `interop.void_component_el(comp, attrs)` | self-closing (자식 없음) |
|
|
456
456
|
|
|
457
|
-
### 7.3 .mpk Pluggable 위젯 (widget + interop)
|
|
458
|
-
|
|
459
|
-
**설정:** `.mpk`를 `widgets/`에 배치 → `gleam run -m glendix/install`
|
|
460
|
-
|
|
461
|
-
자동 생성되는 `src/widgets/*.gleam`:
|
|
462
|
-
|
|
463
|
-
```gleam
|
|
464
|
-
import glendix/interop
|
|
465
|
-
import glendix/mendix
|
|
466
|
-
import glendix/mendix.{type JsProps}
|
|
467
|
-
import glendix/widget
|
|
468
|
-
import redraw.{type Element}
|
|
469
|
-
import redraw/dom/attribute
|
|
470
|
-
|
|
471
|
-
pub fn render(props: JsProps) -> Element {
|
|
472
|
-
let boolean_attribute = mendix.get_prop_required(props, "booleanAttribute")
|
|
473
|
-
let comp = widget.component("Switch")
|
|
474
|
-
interop.component_el(comp, [
|
|
475
|
-
attribute.attribute("booleanAttribute", boolean_attribute),
|
|
476
|
-
], [])
|
|
477
|
-
}
|
|
478
|
-
```
|
|
479
|
-
|
|
480
|
-
**위젯 prop 헬퍼:** 코드에서 직접 값을 생성하여 .mpk 위젯에 전달할 때 사용합니다.
|
|
481
|
-
|
|
482
|
-
| 함수 | Mendix 타입 | 용도 |
|
|
483
|
-
|------|------------|------|
|
|
484
|
-
| `widget.prop(key, value)` | DynamicValue | 읽기 전용 (expression, textTemplate) |
|
|
485
|
-
| `widget.editable_prop(key, value, display, set_value)` | EditableValue | 편집 가능한 속성 |
|
|
486
|
-
| `widget.action_prop(key, handler)` | ActionValue | 액션 콜백 (onClick 등) |
|
|
487
|
-
|
|
488
|
-
```gleam
|
|
489
|
-
import glendix/widget
|
|
490
|
-
import glendix/interop
|
|
491
|
-
|
|
492
|
-
let comp = widget.component("Badge button")
|
|
493
|
-
interop.component_el(comp, [
|
|
494
|
-
widget.prop("caption", "제목"),
|
|
495
|
-
widget.editable_prop("textAttr", model.text, model.text, set_text),
|
|
496
|
-
widget.action_prop("onClick", fn() { handle_click() }),
|
|
497
|
-
], [])
|
|
498
|
-
```
|
|
499
457
|
|
|
500
458
|
> Mendix에서 받은 prop (JsProps에서 꺼낸 값)은 이미 올바른 형식이므로 `attribute.attribute(key, value)`로 그대로 전달합니다.
|
|
501
459
|
|
|
@@ -503,7 +461,7 @@ interop.component_el(comp, [
|
|
|
503
461
|
|
|
504
462
|
```gleam
|
|
505
463
|
import gleam/dynamic
|
|
506
|
-
import
|
|
464
|
+
import mendraw/classic
|
|
507
465
|
|
|
508
466
|
classic.render("CameraWidget.widget.CameraWidget", [
|
|
509
467
|
#("mfToExecute", classic.to_dynamic(mf_value)),
|
|
@@ -517,12 +475,12 @@ classic.render("CameraWidget.widget.CameraWidget", [
|
|
|
517
475
|
|
|
518
476
|
## 8. Mendix API 레퍼런스
|
|
519
477
|
|
|
520
|
-
### 8.1 Props 접근 (`
|
|
478
|
+
### 8.1 Props 접근 (`mendraw/mendix`)
|
|
521
479
|
|
|
522
480
|
`JsProps`는 opaque 타입입니다. 접근자 함수로만 읽습니다.
|
|
523
481
|
|
|
524
482
|
```gleam
|
|
525
|
-
import
|
|
483
|
+
import mendraw/mendix
|
|
526
484
|
|
|
527
485
|
// Option 반환 (undefined → None)
|
|
528
486
|
mendix.get_prop(props, "myAttr") // Option(a)
|
|
@@ -542,7 +500,7 @@ mendix.has_prop(props, "onClick") // Bool
|
|
|
542
500
|
Mendix의 모든 동적 값은 상태를 가집니다:
|
|
543
501
|
|
|
544
502
|
```gleam
|
|
545
|
-
import
|
|
503
|
+
import mendraw/mendix.{Available, Loading, Unavailable}
|
|
546
504
|
|
|
547
505
|
case mendix.get_status(some_value) {
|
|
548
506
|
Available -> // 값 사용 가능
|
|
@@ -551,12 +509,12 @@ case mendix.get_status(some_value) {
|
|
|
551
509
|
}
|
|
552
510
|
```
|
|
553
511
|
|
|
554
|
-
### 8.3 EditableValue (`
|
|
512
|
+
### 8.3 EditableValue (`mendraw/mendix/editable_value`)
|
|
555
513
|
|
|
556
514
|
텍스트, 숫자, 날짜 등 편집 가능한 Mendix 속성:
|
|
557
515
|
|
|
558
516
|
```gleam
|
|
559
|
-
import
|
|
517
|
+
import mendraw/mendix/editable_value as ev
|
|
560
518
|
|
|
561
519
|
// 읽기
|
|
562
520
|
ev.value(attr) // Option(a)
|
|
@@ -581,12 +539,12 @@ ev.set_validator(attr, Some(fn(value) {
|
|
|
581
539
|
ev.universe(attr) // Option(List(a))
|
|
582
540
|
```
|
|
583
541
|
|
|
584
|
-
### 8.4 ActionValue (`
|
|
542
|
+
### 8.4 ActionValue (`mendraw/mendix/action`)
|
|
585
543
|
|
|
586
544
|
Mendix 마이크로플로우/나노플로우 실행:
|
|
587
545
|
|
|
588
546
|
```gleam
|
|
589
|
-
import
|
|
547
|
+
import mendraw/mendix/action
|
|
590
548
|
|
|
591
549
|
action.execute(my_action) // 직접 실행
|
|
592
550
|
action.execute_if_can(my_action) // can_execute가 True일 때만
|
|
@@ -596,24 +554,24 @@ action.can_execute(my_action) // Bool
|
|
|
596
554
|
action.is_executing(my_action) // Bool
|
|
597
555
|
```
|
|
598
556
|
|
|
599
|
-
### 8.5 DynamicValue (`
|
|
557
|
+
### 8.5 DynamicValue (`mendraw/mendix/dynamic_value`)
|
|
600
558
|
|
|
601
559
|
읽기 전용 표현식 속성:
|
|
602
560
|
|
|
603
561
|
```gleam
|
|
604
|
-
import
|
|
562
|
+
import mendraw/mendix/dynamic_value as dv
|
|
605
563
|
|
|
606
564
|
dv.value(expr) // Option(a)
|
|
607
565
|
dv.status(expr) // String
|
|
608
566
|
dv.is_available(expr) // Bool
|
|
609
567
|
```
|
|
610
568
|
|
|
611
|
-
### 8.6 ListValue (`
|
|
569
|
+
### 8.6 ListValue (`mendraw/mendix/list_value`)
|
|
612
570
|
|
|
613
571
|
Mendix 데이터 소스 리스트:
|
|
614
572
|
|
|
615
573
|
```gleam
|
|
616
|
-
import
|
|
574
|
+
import mendraw/mendix/list_value as lv
|
|
617
575
|
|
|
618
576
|
// 아이템 접근
|
|
619
577
|
lv.items(list_val) // Option(List(ObjectItem))
|
|
@@ -641,12 +599,12 @@ lv.set_filter(list_val, None) // 필터 해제
|
|
|
641
599
|
lv.reload(list_val)
|
|
642
600
|
```
|
|
643
601
|
|
|
644
|
-
### 8.7 ListAttribute (`
|
|
602
|
+
### 8.7 ListAttribute (`mendraw/mendix/list_attribute`)
|
|
645
603
|
|
|
646
604
|
리스트의 각 아이템에서 속성/액션/위젯 추출:
|
|
647
605
|
|
|
648
606
|
```gleam
|
|
649
|
-
import
|
|
607
|
+
import mendraw/mendix/list_attribute as la
|
|
650
608
|
|
|
651
609
|
la.get_attribute(attr, item) // EditableValue 반환
|
|
652
610
|
la.get_action(action, item) // Option(ActionValue)
|
|
@@ -661,10 +619,10 @@ la.attr_type(attr) // "String", "Integer" 등
|
|
|
661
619
|
la.attr_formatter(attr) // ValueFormatter
|
|
662
620
|
```
|
|
663
621
|
|
|
664
|
-
### 8.8 Selection (`
|
|
622
|
+
### 8.8 Selection (`mendraw/mendix/selection`)
|
|
665
623
|
|
|
666
624
|
```gleam
|
|
667
|
-
import
|
|
625
|
+
import mendraw/mendix/selection
|
|
668
626
|
|
|
669
627
|
// 단일 선택
|
|
670
628
|
selection.selection(single_sel) // Option(ObjectItem)
|
|
@@ -679,8 +637,8 @@ selection.set_selections(multi_sel, [item1, item2])
|
|
|
679
637
|
### 8.9 Reference / ReferenceSet
|
|
680
638
|
|
|
681
639
|
```gleam
|
|
682
|
-
import
|
|
683
|
-
import
|
|
640
|
+
import mendraw/mendix/reference as ref
|
|
641
|
+
import mendraw/mendix/reference_set as ref_set
|
|
684
642
|
|
|
685
643
|
// 단일 참조
|
|
686
644
|
ref.value(my_ref) // Option(a)
|
|
@@ -693,10 +651,10 @@ ref_set.value(my_ref_set) // Option(List(a))
|
|
|
693
651
|
ref_set.set_value(my_ref_set, Some([item1, item2]))
|
|
694
652
|
```
|
|
695
653
|
|
|
696
|
-
### 8.10 Filter (`
|
|
654
|
+
### 8.10 Filter (`mendraw/mendix/filter`)
|
|
697
655
|
|
|
698
656
|
```gleam
|
|
699
|
-
import
|
|
657
|
+
import mendraw/mendix/filter
|
|
700
658
|
|
|
701
659
|
// 비교 연산
|
|
702
660
|
filter.equals(filter.attribute("Status"), filter.literal("Active"))
|
|
@@ -719,12 +677,12 @@ filter.literal(value) // 상수 값
|
|
|
719
677
|
filter.empty() // null 비교용
|
|
720
678
|
```
|
|
721
679
|
|
|
722
|
-
### 8.11 날짜 (`
|
|
680
|
+
### 8.11 날짜 (`mendraw/mendix/date`)
|
|
723
681
|
|
|
724
682
|
> Gleam month는 1-based (1~12), JS는 0-based. glendix가 자동 변환합니다.
|
|
725
683
|
|
|
726
684
|
```gleam
|
|
727
|
-
import
|
|
685
|
+
import mendraw/mendix/date
|
|
728
686
|
|
|
729
687
|
date.now()
|
|
730
688
|
date.from_iso("2024-03-15T10:30:00Z")
|
|
@@ -741,45 +699,74 @@ date.to_input_value(d) // "2024-03-15" (input[type="date"]용)
|
|
|
741
699
|
date.from_input_value(s) // Option(JsDate)
|
|
742
700
|
```
|
|
743
701
|
|
|
744
|
-
### 8.12
|
|
702
|
+
### 8.12 Decimal (`mendraw/mendix/decimal`)
|
|
703
|
+
|
|
704
|
+
Mendix Decimal 속성의 경계 변환 전용. Big.js 객체 ↔ Gleam 값 변환만 담당합니다.
|
|
705
|
+
산술/비교 연산이 필요하면 위젯 프로젝트에서 [dee](https://hexdocs.pm/dee/) 패키지를 추가하세요.
|
|
706
|
+
|
|
707
|
+
```gleam
|
|
708
|
+
import mendraw/mendix/decimal
|
|
709
|
+
|
|
710
|
+
// 생성 (Gleam → Mendix Big.js)
|
|
711
|
+
decimal.from_string("123.456")
|
|
712
|
+
decimal.from_int(100)
|
|
713
|
+
decimal.from_float(3.14) // 정밀도 손실 주의
|
|
714
|
+
|
|
715
|
+
// 변환 (Mendix Big.js → Gleam)
|
|
716
|
+
decimal.to_string(d) // "123.456"
|
|
717
|
+
decimal.to_float(d) // 123.456 (정밀도 손실 가능)
|
|
718
|
+
decimal.to_int(d) // 123 (소수점 이하 버림)
|
|
719
|
+
decimal.to_fixed(d, 2) // "123.46"
|
|
720
|
+
```
|
|
745
721
|
|
|
746
|
-
|
|
722
|
+
**dee와 함께 사용하는 패턴:**
|
|
747
723
|
|
|
748
724
|
```gleam
|
|
749
|
-
import
|
|
725
|
+
import dee
|
|
726
|
+
import mendraw/mendix/decimal
|
|
727
|
+
|
|
728
|
+
// Mendix → dee (연산) → Mendix
|
|
729
|
+
let value = decimal.to_string(mendix_decimal) // Big.js → String
|
|
730
|
+
let result = dee.from_string(value) // String → dee
|
|
731
|
+
|> result.map(fn(d) { dee.add(d, dee.from_int(1)) })
|
|
732
|
+
|> result.map(dee.to_string) // dee → String
|
|
733
|
+
|> result.unwrap("0")
|
|
734
|
+
decimal.from_string(result) // String → Big.js
|
|
735
|
+
```
|
|
750
736
|
|
|
751
|
-
|
|
752
|
-
big.from_int(100)
|
|
737
|
+
### 8.12.1 조건부 CSS 클래스 (`cx`)
|
|
753
738
|
|
|
754
|
-
|
|
755
|
-
big.multiply(a, b) big.divide(a, b)
|
|
756
|
-
big.absolute(a) big.negate(a)
|
|
739
|
+
`mendraw/mendix` 모듈의 `cx` 함수로 CSS 클래스를 조건부 조합합니다:
|
|
757
740
|
|
|
758
|
-
|
|
759
|
-
|
|
741
|
+
```gleam
|
|
742
|
+
import mendraw/mendix
|
|
760
743
|
|
|
761
|
-
|
|
762
|
-
|
|
744
|
+
mendix.cx([
|
|
745
|
+
#("widget-container", True),
|
|
746
|
+
#("active", is_active),
|
|
747
|
+
#("disabled", !is_editable),
|
|
748
|
+
])
|
|
749
|
+
// → "widget-container active" (is_active=True, is_editable=True일 때)
|
|
763
750
|
```
|
|
764
751
|
|
|
765
752
|
### 8.13 File, Icon, Formatter
|
|
766
753
|
|
|
767
754
|
```gleam
|
|
768
755
|
// FileValue / WebImage
|
|
769
|
-
import
|
|
756
|
+
import mendraw/mendix/file
|
|
770
757
|
file.uri(file_val) // String
|
|
771
758
|
file.name(file_val) // Option(String)
|
|
772
759
|
file.image_uri(img) // String
|
|
773
760
|
file.alt_text(img) // Option(String)
|
|
774
761
|
|
|
775
762
|
// WebIcon
|
|
776
|
-
import
|
|
763
|
+
import mendraw/mendix/icon
|
|
777
764
|
icon.icon_type(i) // Glyph | Image | IconFont
|
|
778
765
|
icon.icon_class(i) // String
|
|
779
766
|
icon.icon_url(i) // String
|
|
780
767
|
|
|
781
768
|
// ValueFormatter
|
|
782
|
-
import
|
|
769
|
+
import mendraw/mendix/formatter
|
|
783
770
|
formatter.format(fmt, Some(value)) // String
|
|
784
771
|
formatter.parse(fmt, "123.45") // Result(Option(a), Nil)
|
|
785
772
|
```
|
|
@@ -794,8 +781,8 @@ Studio Pro의 editorConfig 로직을 Gleam으로 작성합니다.
|
|
|
794
781
|
|
|
795
782
|
```gleam
|
|
796
783
|
import glendix/editor_config.{type Properties}
|
|
797
|
-
import
|
|
798
|
-
import
|
|
784
|
+
import mendraw/mendix
|
|
785
|
+
import mendraw/mendix.{type JsProps}
|
|
799
786
|
|
|
800
787
|
const bar_keys = "barWidth,barColor"
|
|
801
788
|
const line_keys = "lineStyle,lineCurve"
|
|
@@ -885,30 +872,93 @@ timer.clear_interval(id)
|
|
|
885
872
|
| 명령어 | 설명 |
|
|
886
873
|
|--------|------|
|
|
887
874
|
| `gleam build` | 컴파일 |
|
|
888
|
-
| `gleam run -m glendix/install` | 의존성 + 바인딩 + 위젯 .gleam 생성 |
|
|
875
|
+
| `gleam run -m glendix/install` | 의존성 + mendraw 위젯 다운로드 + 바인딩 + 위젯 .gleam 생성 |
|
|
889
876
|
| `gleam run -m glendix/dev` | 개발 서버 (HMR) |
|
|
890
877
|
| `gleam run -m glendix/build` | 프로덕션 빌드 (.mpk) |
|
|
891
878
|
| `gleam run -m glendix/start` | Mendix 테스트 프로젝트 연동 |
|
|
892
879
|
| `gleam run -m glendix/release` | 릴리즈 빌드 |
|
|
893
880
|
| `gleam run -m glendix/lint` | ESLint 검사 |
|
|
894
881
|
| `gleam run -m glendix/lint_fix` | ESLint 자동 수정 |
|
|
895
|
-
| `gleam run -m
|
|
882
|
+
| `gleam run -m mendraw/marketplace` | Marketplace 위젯 다운로드 (인터랙티브, gleam.toml 자동 기록) |
|
|
896
883
|
| `gleam run -m glendix/define` | 위젯 프로퍼티 정의 TUI 에디터 |
|
|
897
884
|
|
|
898
|
-
**PM
|
|
885
|
+
**PM 감지:** `gleam.toml`의 `[tools.glendix] pm = "pnpm"` 오버라이드 우선. 없으면 lock 파일 기반: `pnpm-lock.yaml` → pnpm / `bun.lockb`·`bun.lock` → bun / 기본값 → npm
|
|
886
|
+
|
|
887
|
+
---
|
|
888
|
+
|
|
889
|
+
## 12. 멀티 위젯 컴포넌트
|
|
890
|
+
|
|
891
|
+
하나의 `.mpk` 파일에 여러 위젯 컴포넌트를 묶어서 개발할 수 있습니다 (예: Charts.mpk).
|
|
892
|
+
|
|
893
|
+
### 12.1 설정
|
|
894
|
+
|
|
895
|
+
`package.json`에 `widgets` 맵을 추가합니다:
|
|
896
|
+
|
|
897
|
+
```json
|
|
898
|
+
{
|
|
899
|
+
"widgetName": "AreaChart",
|
|
900
|
+
"widgets": {
|
|
901
|
+
"AreaChart": "area_chart",
|
|
902
|
+
"BarChart": "bar_chart",
|
|
903
|
+
"PieChart": "pie_chart"
|
|
904
|
+
}
|
|
905
|
+
}
|
|
906
|
+
```
|
|
907
|
+
|
|
908
|
+
- `widgetName`: 기본 위젯 이름 (`pluggable-widgets-tools` 진입점)
|
|
909
|
+
- `widgets`: 컴포넌트명(PascalCase) → Gleam 함수명(snake_case) 맵
|
|
910
|
+
- `widgets` 미지정 시 기존 단일 위젯 동작 유지 (하위 호환)
|
|
911
|
+
|
|
912
|
+
### 12.2 Gleam 코드
|
|
913
|
+
|
|
914
|
+
메인 모듈에서 모든 위젯 함수를 export합니다:
|
|
915
|
+
|
|
916
|
+
```gleam
|
|
917
|
+
import mendraw/mendix.{type JsProps}
|
|
918
|
+
import redraw.{type Element}
|
|
919
|
+
|
|
920
|
+
pub fn area_chart(props: JsProps) -> Element { ... }
|
|
921
|
+
pub fn bar_chart(props: JsProps) -> Element { ... }
|
|
922
|
+
pub fn pie_chart(props: JsProps) -> Element { ... }
|
|
923
|
+
```
|
|
924
|
+
|
|
925
|
+
### 12.3 CSS 전략
|
|
926
|
+
|
|
927
|
+
위젯별 CSS 파일이 있으면 우선 사용, 없으면 공유 CSS 폴백:
|
|
928
|
+
|
|
929
|
+
1. `src/ui/{ComponentName}.css` 존재 → 해당 파일 import
|
|
930
|
+
2. 미존재 + `src/ui/{widgetName}.css` 존재 → 공유 CSS import
|
|
931
|
+
3. 둘 다 미존재 → CSS import 생략
|
|
932
|
+
|
|
933
|
+
### 12.4 Editor Config / Preview
|
|
934
|
+
|
|
935
|
+
위젯별 파일 → 공유 파일 폴백:
|
|
936
|
+
|
|
937
|
+
1. `src/{snake_fn}_editor_config.gleam` 존재 → 위젯별 editor config
|
|
938
|
+
2. 미존재 + `src/editor_config.gleam` 존재 → 공유 editor config
|
|
939
|
+
3. Preview도 동일 패턴 (`{snake_fn}_editor_preview.gleam`)
|
|
940
|
+
|
|
941
|
+
### 12.5 빌드
|
|
942
|
+
|
|
943
|
+
`gleam run -m glendix/build` 실행 시 자동으로:
|
|
944
|
+
|
|
945
|
+
- 각 위젯별 브릿지 파일 (`src/{ComponentName}.js`) 생성
|
|
946
|
+
- 기본 위젯은 `pluggable-widgets-tools`가 처리
|
|
947
|
+
- 추가 위젯은 rollup config에서 엔트리 자동 추가
|
|
948
|
+
- 각 위젯의 XML에서 `id`를 읽어 출력 경로 결정
|
|
899
949
|
|
|
900
950
|
---
|
|
901
951
|
|
|
902
|
-
##
|
|
952
|
+
## 13. 실전 패턴
|
|
903
953
|
|
|
904
954
|
### 12.1 폼 입력 위젯
|
|
905
955
|
|
|
906
956
|
```gleam
|
|
907
957
|
import gleam/option.{None, Some}
|
|
908
|
-
import
|
|
909
|
-
import
|
|
910
|
-
import
|
|
911
|
-
import
|
|
958
|
+
import mendraw/mendix
|
|
959
|
+
import mendraw/mendix.{type JsProps}
|
|
960
|
+
import mendraw/mendix/action
|
|
961
|
+
import mendraw/mendix/editable_value as ev
|
|
912
962
|
import redraw.{type Element}
|
|
913
963
|
import redraw/dom/attribute
|
|
914
964
|
import redraw/dom/events
|
|
@@ -957,11 +1007,11 @@ pub fn text_input_widget(props: JsProps) -> Element {
|
|
|
957
1007
|
```gleam
|
|
958
1008
|
import gleam/list
|
|
959
1009
|
import gleam/option.{None, Some}
|
|
960
|
-
import
|
|
961
|
-
import
|
|
962
|
-
import
|
|
963
|
-
import
|
|
964
|
-
import
|
|
1010
|
+
import mendraw/mendix
|
|
1011
|
+
import mendraw/mendix.{type JsProps}
|
|
1012
|
+
import mendraw/mendix/editable_value as ev
|
|
1013
|
+
import mendraw/mendix/list_attribute as la
|
|
1014
|
+
import mendraw/mendix/list_value as lv
|
|
965
1015
|
import redraw.{type Element}
|
|
966
1016
|
import redraw/dom/attribute
|
|
967
1017
|
import redraw/dom/html
|
|
@@ -990,10 +1040,10 @@ pub fn data_table(props: JsProps) -> Element {
|
|
|
990
1040
|
|
|
991
1041
|
```gleam
|
|
992
1042
|
import gleam/option.{None, Some}
|
|
993
|
-
import
|
|
994
|
-
import
|
|
995
|
-
import
|
|
996
|
-
import
|
|
1043
|
+
import mendraw/mendix
|
|
1044
|
+
import mendraw/mendix.{type JsProps}
|
|
1045
|
+
import mendraw/mendix/filter
|
|
1046
|
+
import mendraw/mendix/list_value as lv
|
|
997
1047
|
import redraw.{type Element}
|
|
998
1048
|
import redraw/dom/attribute
|
|
999
1049
|
import redraw/dom/events
|
|
@@ -1028,7 +1078,7 @@ pub fn searchable_list(props: JsProps) -> Element {
|
|
|
1028
1078
|
|
|
1029
1079
|
---
|
|
1030
1080
|
|
|
1031
|
-
##
|
|
1081
|
+
## 14. 절대 하지 말 것
|
|
1032
1082
|
|
|
1033
1083
|
| 실수 | 올바른 방법 |
|
|
1034
1084
|
|------|------------|
|
|
@@ -1037,15 +1087,14 @@ pub fn searchable_list(props: JsProps) -> Element {
|
|
|
1037
1087
|
| 조건 안에서 Hook 호출 | Hook은 항상 함수 최상위에서 호출 |
|
|
1038
1088
|
| `html.text("")`로 빈 렌더링 | `html.none()` 사용 |
|
|
1039
1089
|
| `binding.resolve(m(), "pie_chart")` | JS 원본 이름 유지: `"PieChart"` |
|
|
1040
|
-
| 외부 React 컴포넌트용 `.mjs` 직접 작성 | `bindings
|
|
1041
|
-
| `.mpk` 위젯용 `.mjs` 직접 작성 | `widgets/` + `glendix/widget` 사용 |
|
|
1090
|
+
| 외부 React 컴포넌트용 `.mjs` 직접 작성 | `gleam.toml [tools.glendix.bindings]` + `glendix/binding` 사용 |
|
|
1042
1091
|
| `date.month()`에 0-based 값 전달 | glendix가 1↔0 자동 변환 |
|
|
1043
1092
|
| Editor config에서 Gleam List 사용 | 콤마 구분 String 사용 (Jint 호환) |
|
|
1044
1093
|
| FFI `.mjs`에 비즈니스 로직 | `.gleam`에 작성. `.mjs`는 JS 런타임 접근만 |
|
|
1045
1094
|
|
|
1046
1095
|
---
|
|
1047
1096
|
|
|
1048
|
-
##
|
|
1097
|
+
## 15. 트러블슈팅
|
|
1049
1098
|
|
|
1050
1099
|
| 문제 | 원인 | 해결 |
|
|
1051
1100
|
|------|------|------|
|
|
@@ -1053,9 +1102,7 @@ pub fn searchable_list(props: JsProps) -> Element {
|
|
|
1053
1102
|
| `Cannot read property of undefined` | 없는 prop 접근 | `get_prop` (Option) 사용, prop 이름 확인 |
|
|
1054
1103
|
| Hook 순서 에러 | 조건부 Hook 호출 | 항상 동일 순서로 호출 (React Rules) |
|
|
1055
1104
|
| 바인딩 미생성 | `binding_ffi.mjs` 스텁 상태 | `gleam run -m glendix/install` |
|
|
1056
|
-
| 위젯 바인딩 미생성 | `widget_ffi.mjs` 스텁 상태 | `widgets/`에 `.mpk` 배치 후 install |
|
|
1057
1105
|
| `could not be resolved` | npm 패키지 미설치 | `npm install <패키지명>` |
|
|
1058
1106
|
| `.env` PAT 오류 | marketplace 인증 실패 | [Developer Settings](https://user-settings.mendix.com/link/developersettings)에서 PAT 재발급 |
|
|
1059
|
-
| Playwright 오류 | chromium 미설치 | `npx playwright install chromium` |
|
|
1060
1107
|
|
|
1061
1108
|
---
|