create-mendix-widget-gleam 3.0.0 → 3.0.2

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-mendix-widget-gleam",
3
- "version": "3.0.0",
3
+ "version": "3.0.2",
4
4
  "description": "Scaffold a Mendix Pluggable Widget powered by Gleam",
5
5
  "type": "module",
6
6
  "license": "Apache-2.0",
@@ -43,15 +43,22 @@ glendix는 Gleam으로 Mendix Pluggable Widget을 작성하는 FFI 라이브러
43
43
 
44
44
  ```toml
45
45
  [dependencies]
46
- glendix = ">= 3.0.0 and < 4.0.0"
46
+ glendix = ">= 3.0.2 and < 4.0.0"
47
47
  ```
48
48
 
49
49
  Peer dependency (위젯 프로젝트 `package.json`):
50
50
 
51
51
  ```json
52
- { "dependencies": { "react": "^19.0.0", "react-dom": "^19.0.0", "big.js": "^6.0.0" } }
52
+ {
53
+ "dependencies": { "big.js": "^6.0.0" },
54
+ "overrides": { "react": "19.0.0", "react-dom": "19.0.0", "@types/react": "19.0.0", "@types/react-dom": "19.0.0" },
55
+ "resolutions": { "react": "19.0.0", "react-dom": "19.0.0", "@types/react": "19.0.0", "@types/react-dom": "19.0.0" }
56
+ }
53
57
  ```
54
58
 
59
+ > - `react`/`react-dom`은 `dependencies`에 넣지 않는다. `pluggable-widgets-tools`가 자동 제공하며, 직접 선언하면 번들 충돌이 발생한다.
60
+ > - `overrides`/`resolutions`에서 반드시 **캐럿(`^`) 없이 정확한 버전**을 지정한다. `^19.0.0`은 react와 react-dom이 서로 다른 19.x.x로 해석되어 런타임 버전 불일치 에러를 일으킨다.
61
+
55
62
  ```bash
56
63
  gleam run -m glendix/install # 의존성 설치 + 바인딩 생성
57
64
  gleam build # 컴파일 확인
@@ -75,7 +82,7 @@ pub fn widget(props: JsProps) -> Element
75
82
 
76
83
  ---
77
84
 
78
- ## 3. 렌더링 경로 선택
85
+ ## 4. 렌더링 경로 선택
79
86
 
80
87
  glendix는 두 가지 렌더링 경로를 지원합니다. 둘 다 `redraw.Element`를 반환하므로 자유롭게 합성 가능합니다.
81
88
 
@@ -91,9 +98,9 @@ glendix는 두 가지 렌더링 경로를 지원합니다. 둘 다 `redraw.Eleme
91
98
 
92
99
  ---
93
100
 
94
- ## 4. redraw 렌더링 경로 — 레퍼런스
101
+ ## 5. redraw 렌더링 경로 — 레퍼런스
95
102
 
96
- ### 4.1 필수 import 패턴
103
+ ### 5.1 필수 import 패턴
97
104
 
98
105
  ```gleam
99
106
  import glendix/mendix.{type JsProps} // Mendix props 타입
@@ -103,7 +110,7 @@ import redraw/dom/attribute // HTML 속성
103
110
  import redraw/dom/events // 이벤트 핸들러
104
111
  ```
105
112
 
106
- ### 4.2 HTML 엘리먼트 생성
113
+ ### 5.2 HTML 엘리먼트 생성
107
114
 
108
115
  ```gleam
109
116
  // 속성 + 자식
@@ -118,7 +125,7 @@ html.img([attribute.src("image.png"), attribute.alt("설명")])
118
125
  html.br([])
119
126
  ```
120
127
 
121
- ### 4.3 텍스트, 빈 렌더링, Fragment
128
+ ### 5.3 텍스트, 빈 렌더링, Fragment
122
129
 
123
130
  ```gleam
124
131
  html.text("안녕하세요") // 텍스트 노드
@@ -129,7 +136,7 @@ html.none() // 아무것도 렌더링하지 않음 (Rea
129
136
  redraw.fragment([child1, child2]) // Fragment
130
137
  ```
131
138
 
132
- ### 4.4 조건부 렌더링
139
+ ### 5.4 조건부 렌더링
133
140
 
134
141
  v3.0에서는 Gleam `case` 표현식을 직접 사용합니다:
135
142
 
@@ -154,7 +161,7 @@ case mendix.get_status(value) {
154
161
  }
155
162
  ```
156
163
 
157
- ### 4.5 리스트 렌더링
164
+ ### 5.5 리스트 렌더링
158
165
 
159
166
  ```gleam
160
167
  import gleam/list
@@ -168,7 +175,7 @@ html.ul([], list.map(items, fn(item) {
168
175
 
169
176
  > 리스트 렌더링 시 `attribute.key()`를 항상 설정해야 합니다. React reconciliation에 필요합니다.
170
177
 
171
- ### 4.6 속성
178
+ ### 5.6 속성
172
179
 
173
180
  ```gleam
174
181
  import redraw/dom/attribute
@@ -193,7 +200,7 @@ attribute.attribute("data-custom", "value")
193
200
  attribute.ref(my_ref)
194
201
  ```
195
202
 
196
- ### 4.7 이벤트 핸들러
203
+ ### 5.7 이벤트 핸들러
197
204
 
198
205
  ```gleam
199
206
  import redraw/dom/events
@@ -210,7 +217,7 @@ events.on_blur(fn(e) { Nil })
210
217
  events.on_click_capture(fn(e) { Nil })
211
218
  ```
212
219
 
213
- ### 4.8 Hooks
220
+ ### 5.8 Hooks
214
221
 
215
222
  모든 훅은 `redraw` 메인 모듈에 있습니다:
216
223
 
@@ -227,8 +234,11 @@ redraw.use_effect(fn() { Nil }, deps) // 의존성 지정
227
234
  redraw.use_effect_(fn() { fn() { cleanup() } }, deps) // 클린업 포함
228
235
 
229
236
  // Ref
230
- let my_ref = redraw.use_ref() // Option(a)
231
- let my_ref = redraw.use_ref_(initial) // 초기값 지정
237
+ import redraw/ref
238
+ let my_ref = redraw.use_ref() // Ref(Option(a))
239
+ let my_ref = redraw.use_ref_(initial) // Ref(a) — 초기값 지정
240
+ ref.current(my_ref) // 현재 값 읽기
241
+ ref.assign(my_ref, new_value) // 값 쓰기
232
242
 
233
243
  // 메모이제이션
234
244
  let result = redraw.use_memo(fn() { expensive(data) }, data)
@@ -246,7 +256,7 @@ let #(is_pending, start) = redraw.use_transition()
246
256
  let deferred = redraw.use_deferred_value(value)
247
257
  ```
248
258
 
249
- ### 4.9 컴포넌트 정의
259
+ ### 5.9 컴포넌트 정의
250
260
 
251
261
  ```gleam
252
262
  import redraw
@@ -260,7 +270,7 @@ let my_comp = redraw.component_("MyComponent", fn(props) {
260
270
  let memoized = redraw.memoize_(my_comp)
261
271
  ```
262
272
 
263
- ### 4.10 Context API
273
+ ### 5.10 Context API
264
274
 
265
275
  ```gleam
266
276
  import redraw
@@ -274,7 +284,7 @@ redraw.provider(theme_ctx, "dark", [child_elements])
274
284
  let theme = redraw.use_context(theme_ctx)
275
285
  ```
276
286
 
277
- ### 4.11 SVG
287
+ ### 5.11 SVG
278
288
 
279
289
  ```gleam
280
290
  import redraw/dom/svg
@@ -292,9 +302,9 @@ svg.svg([attribute.attribute("viewBox", "0 0 100 100")], [
292
302
 
293
303
  ---
294
304
 
295
- ## 5. lustre 렌더링 경로 — 레퍼런스
305
+ ## 6. lustre 렌더링 경로 — 레퍼런스
296
306
 
297
- ### 5.1 TEA 패턴 (use_tea)
307
+ ### 6.1 TEA 패턴 (use_tea)
298
308
 
299
309
  `update`와 `view`는 표준 lustre 코드와 100% 동일합니다. 진입점만 `glendix/lustre.use_tea()`를 사용합니다.
300
310
 
@@ -341,7 +351,7 @@ pub fn widget(_props: JsProps) -> Element {
341
351
  }
342
352
  ```
343
353
 
344
- ### 5.2 Simple TEA (use_simple) — Effect 없음
354
+ ### 6.2 Simple TEA (use_simple) — Effect 없음
345
355
 
346
356
  ```gleam
347
357
  import glendix/lustre as gl
@@ -358,7 +368,7 @@ fn update_simple(model: Model, msg: Msg) -> Model {
358
368
  }
359
369
  ```
360
370
 
361
- ### 5.3 Lustre Element를 수동으로 변환 (render)
371
+ ### 6.3 Lustre Element를 수동으로 변환 (render)
362
372
 
363
373
  lustre 뷰를 React 트리 안에 삽입할 때 사용합니다:
364
374
 
@@ -368,7 +378,7 @@ import glendix/lustre as gl
368
378
  let react_element = gl.render(lustre_element, dispatch_fn)
369
379
  ```
370
380
 
371
- ### 5.4 redraw Element를 lustre 트리에 삽입 (embed)
381
+ ### 6.4 redraw Element를 lustre 트리에 삽입 (embed)
372
382
 
373
383
  lustre view 안에서 redraw 컴포넌트를 사용할 때 호출합니다:
374
384
 
@@ -395,9 +405,9 @@ fn view(model: Model) {
395
405
 
396
406
  ---
397
407
 
398
- ## 6. 외부 컴포넌트 통합
408
+ ## 7. 외부 컴포넌트 통합
399
409
 
400
- ### 6.1 모듈 선택 가이드
410
+ ### 7.1 모듈 선택 가이드
401
411
 
402
412
  | 컴포넌트 출처 | 사용 모듈 | 예시 |
403
413
  |--------------|----------|------|
@@ -405,7 +415,7 @@ fn view(model: Model) {
405
415
  | `.mpk` Pluggable 위젯 | `glendix/widget` + `glendix/interop` | Switch.mpk, Badge.mpk |
406
416
  | `.mpk` Classic (Dojo) 위젯 | `glendix/classic` | CameraWidget.mpk |
407
417
 
408
- ### 6.2 외부 React 컴포넌트 (binding + interop)
418
+ ### 7.2 외부 React 컴포넌트 (binding + interop)
409
419
 
410
420
  **설정:** `bindings.json` 작성 → `npm install 패키지명` → `gleam run -m glendix/install`
411
421
 
@@ -444,7 +454,7 @@ pub fn tooltip(attrs: List(Attribute)) -> Element {
444
454
  | `interop.component_el_(comp, children)` | 자식만 |
445
455
  | `interop.void_component_el(comp, attrs)` | self-closing (자식 없음) |
446
456
 
447
- ### 6.3 .mpk Pluggable 위젯 (widget + interop)
457
+ ### 7.3 .mpk Pluggable 위젯 (widget + interop)
448
458
 
449
459
  **설정:** `.mpk`를 `widgets/`에 배치 → `gleam run -m glendix/install`
450
460
 
@@ -489,7 +499,7 @@ interop.component_el(comp, [
489
499
 
490
500
  > Mendix에서 받은 prop (JsProps에서 꺼낸 값)은 이미 올바른 형식이므로 `attribute.attribute(key, value)`로 그대로 전달합니다.
491
501
 
492
- ### 6.4 Classic (Dojo) 위젯
502
+ ### 7.4 Classic (Dojo) 위젯
493
503
 
494
504
  ```gleam
495
505
  import gleam/dynamic
@@ -505,9 +515,9 @@ classic.render("CameraWidget.widget.CameraWidget", [
505
515
 
506
516
  ---
507
517
 
508
- ## 7. Mendix API 레퍼런스
518
+ ## 8. Mendix API 레퍼런스
509
519
 
510
- ### 7.1 Props 접근 (`glendix/mendix`)
520
+ ### 8.1 Props 접근 (`glendix/mendix`)
511
521
 
512
522
  `JsProps`는 opaque 타입입니다. 접근자 함수로만 읽습니다.
513
523
 
@@ -527,7 +537,7 @@ mendix.get_string_prop(props, "caption") // String
527
537
  mendix.has_prop(props, "onClick") // Bool
528
538
  ```
529
539
 
530
- ### 7.2 ValueStatus 확인
540
+ ### 8.2 ValueStatus 확인
531
541
 
532
542
  Mendix의 모든 동적 값은 상태를 가집니다:
533
543
 
@@ -541,7 +551,7 @@ case mendix.get_status(some_value) {
541
551
  }
542
552
  ```
543
553
 
544
- ### 7.3 EditableValue (`glendix/mendix/editable_value`)
554
+ ### 8.3 EditableValue (`glendix/mendix/editable_value`)
545
555
 
546
556
  텍스트, 숫자, 날짜 등 편집 가능한 Mendix 속성:
547
557
 
@@ -571,7 +581,7 @@ ev.set_validator(attr, Some(fn(value) {
571
581
  ev.universe(attr) // Option(List(a))
572
582
  ```
573
583
 
574
- ### 7.4 ActionValue (`glendix/mendix/action`)
584
+ ### 8.4 ActionValue (`glendix/mendix/action`)
575
585
 
576
586
  Mendix 마이크로플로우/나노플로우 실행:
577
587
 
@@ -586,7 +596,7 @@ action.can_execute(my_action) // Bool
586
596
  action.is_executing(my_action) // Bool
587
597
  ```
588
598
 
589
- ### 7.5 DynamicValue (`glendix/mendix/dynamic_value`)
599
+ ### 8.5 DynamicValue (`glendix/mendix/dynamic_value`)
590
600
 
591
601
  읽기 전용 표현식 속성:
592
602
 
@@ -598,7 +608,7 @@ dv.status(expr) // String
598
608
  dv.is_available(expr) // Bool
599
609
  ```
600
610
 
601
- ### 7.6 ListValue (`glendix/mendix/list_value`)
611
+ ### 8.6 ListValue (`glendix/mendix/list_value`)
602
612
 
603
613
  Mendix 데이터 소스 리스트:
604
614
 
@@ -631,7 +641,7 @@ lv.set_filter(list_val, None) // 필터 해제
631
641
  lv.reload(list_val)
632
642
  ```
633
643
 
634
- ### 7.7 ListAttribute (`glendix/mendix/list_attribute`)
644
+ ### 8.7 ListAttribute (`glendix/mendix/list_attribute`)
635
645
 
636
646
  리스트의 각 아이템에서 속성/액션/위젯 추출:
637
647
 
@@ -651,7 +661,7 @@ la.attr_type(attr) // "String", "Integer" 등
651
661
  la.attr_formatter(attr) // ValueFormatter
652
662
  ```
653
663
 
654
- ### 7.8 Selection (`glendix/mendix/selection`)
664
+ ### 8.8 Selection (`glendix/mendix/selection`)
655
665
 
656
666
  ```gleam
657
667
  import glendix/mendix/selection
@@ -666,7 +676,7 @@ selection.selections(multi_sel) // List(ObjectItem)
666
676
  selection.set_selections(multi_sel, [item1, item2])
667
677
  ```
668
678
 
669
- ### 7.9 Reference / ReferenceSet
679
+ ### 8.9 Reference / ReferenceSet
670
680
 
671
681
  ```gleam
672
682
  import glendix/mendix/reference as ref
@@ -683,7 +693,7 @@ ref_set.value(my_ref_set) // Option(List(a))
683
693
  ref_set.set_value(my_ref_set, Some([item1, item2]))
684
694
  ```
685
695
 
686
- ### 7.10 Filter (`glendix/mendix/filter`)
696
+ ### 8.10 Filter (`glendix/mendix/filter`)
687
697
 
688
698
  ```gleam
689
699
  import glendix/mendix/filter
@@ -709,7 +719,7 @@ filter.literal(value) // 상수 값
709
719
  filter.empty() // null 비교용
710
720
  ```
711
721
 
712
- ### 7.11 날짜 (`glendix/mendix/date`)
722
+ ### 8.11 날짜 (`glendix/mendix/date`)
713
723
 
714
724
  > Gleam month는 1-based (1~12), JS는 0-based. glendix가 자동 변환합니다.
715
725
 
@@ -731,7 +741,7 @@ date.to_input_value(d) // "2024-03-15" (input[type="date"]용)
731
741
  date.from_input_value(s) // Option(JsDate)
732
742
  ```
733
743
 
734
- ### 7.12 Big (`glendix/mendix/big`)
744
+ ### 8.12 Big (`glendix/mendix/big`)
735
745
 
736
746
  Big.js 래퍼. Mendix Decimal 타입 처리용:
737
747
 
@@ -752,7 +762,7 @@ big.to_string(a) big.to_float(a)
752
762
  big.to_int(a) big.to_fixed(a, 2)
753
763
  ```
754
764
 
755
- ### 7.13 File, Icon, Formatter
765
+ ### 8.13 File, Icon, Formatter
756
766
 
757
767
  ```gleam
758
768
  // FileValue / WebImage
@@ -776,7 +786,7 @@ formatter.parse(fmt, "123.45") // Result(Option(a), Nil)
776
786
 
777
787
  ---
778
788
 
779
- ## 8. Editor Configuration (`glendix/editor_config`)
789
+ ## 9. Editor Configuration (`glendix/editor_config`)
780
790
 
781
791
  Studio Pro의 editorConfig 로직을 Gleam으로 작성합니다.
782
792
 
@@ -823,7 +833,7 @@ pub fn get_properties(
823
833
 
824
834
  ---
825
835
 
826
- ## 9. JS Interop Escape Hatch (`glendix/js/*`)
836
+ ## 10. JS Interop Escape Hatch (`glendix/js/*`)
827
837
 
828
838
  외부 JS 라이브러리(SpreadJS, Chart.js 등)와 직접 상호작용할 때 사용합니다. 모든 값은 `Dynamic` 타입. 가능하면 `glendix/binding`을 먼저 고려하세요.
829
839
 
@@ -835,14 +845,14 @@ array.to_list(js_arr) // JS Array → Gleam List
835
845
 
836
846
  // 객체
837
847
  import glendix/js/object
838
- object.object([#("width", dynamic.from(800))])
848
+ object.object([#("width", dynamic.int(800))])
839
849
  object.get(obj, "key")
840
- object.set(obj, "key", dynamic.from(val))
850
+ object.set(obj, "key", dynamic.string(val))
841
851
  object.call_method(obj, "method", [arg1, arg2])
842
852
 
843
853
  // JSON
844
854
  import glendix/js/json
845
- json.stringify(dynamic.from(data)) // String
855
+ json.stringify(data) // String
846
856
  json.parse("{\"k\":\"v\"}") // Result(Dynamic, String)
847
857
 
848
858
  // Promise
@@ -870,7 +880,7 @@ timer.clear_interval(id)
870
880
 
871
881
  ---
872
882
 
873
- ## 10. 빌드 & 도구
883
+ ## 11. 빌드 & 도구
874
884
 
875
885
  | 명령어 | 설명 |
876
886
  |--------|------|
@@ -889,9 +899,9 @@ timer.clear_interval(id)
889
899
 
890
900
  ---
891
901
 
892
- ## 11. 실전 패턴
902
+ ## 12. 실전 패턴
893
903
 
894
- ### 11.1 폼 입력 위젯
904
+ ### 12.1 폼 입력 위젯
895
905
 
896
906
  ```gleam
897
907
  import gleam/option.{None, Some}
@@ -942,7 +952,7 @@ pub fn text_input_widget(props: JsProps) -> Element {
942
952
  }
943
953
  ```
944
954
 
945
- ### 11.2 데이터 테이블 위젯
955
+ ### 12.2 데이터 테이블 위젯
946
956
 
947
957
  ```gleam
948
958
  import gleam/list
@@ -976,7 +986,7 @@ pub fn data_table(props: JsProps) -> Element {
976
986
  }
977
987
  ```
978
988
 
979
- ### 11.3 검색 가능한 리스트
989
+ ### 12.3 검색 가능한 리스트
980
990
 
981
991
  ```gleam
982
992
  import gleam/option.{None, Some}
@@ -1018,11 +1028,12 @@ pub fn searchable_list(props: JsProps) -> Element {
1018
1028
 
1019
1029
  ---
1020
1030
 
1021
- ## 12. 절대 하지 말 것
1031
+ ## 13. 절대 하지 말 것
1022
1032
 
1023
1033
  | 실수 | 올바른 방법 |
1024
1034
  |------|------------|
1025
1035
  | `import glendix/react` | **삭제됨.** `import redraw` 사용 |
1036
+ | `react`/`react-dom`을 `dependencies`에 추가 | `pluggable-widgets-tools`가 제공. 직접 넣으면 버전 충돌 |
1026
1037
  | 조건 안에서 Hook 호출 | Hook은 항상 함수 최상위에서 호출 |
1027
1038
  | `html.text("")`로 빈 렌더링 | `html.none()` 사용 |
1028
1039
  | `binding.resolve(m(), "pie_chart")` | JS 원본 이름 유지: `"PieChart"` |
@@ -1034,7 +1045,7 @@ pub fn searchable_list(props: JsProps) -> Element {
1034
1045
 
1035
1046
  ---
1036
1047
 
1037
- ## 13. 트러블슈팅
1048
+ ## 14. 트러블슈팅
1038
1049
 
1039
1050
  | 문제 | 원인 | 해결 |
1040
1051
  |------|------|------|
@@ -8,7 +8,7 @@ runtime = "node"
8
8
 
9
9
  [dependencies]
10
10
  gleam_stdlib = ">= 0.44.0 and < 2.0.0"
11
- glendix = ">= 3.0.0 and < 4.0.0"
11
+ glendix = ">= 3.0.2 and < 4.0.0"
12
12
  redraw = ">= 19.2.2 and < 20.0.0"
13
13
  redraw_dom = ">= 19.2.2 and < 20.0.0"
14
14
  lustre = ">= 5.6.0 and < 6.0.0"
@@ -24,16 +24,18 @@
24
24
  },
25
25
  "dependencies": {
26
26
  "classnames": "^2.5.1",
27
- "react": "^19.0.0",
28
- "react-dom": "^19.0.0",
29
27
  "big.js": "^6.0.0"
30
28
  },
31
29
  "resolutions": {
32
- "react": "^19.0.0",
33
- "react-dom": "^19.0.0"
30
+ "react": "19.0.0",
31
+ "react-dom": "19.0.0",
32
+ "@types/react": "19.0.0",
33
+ "@types/react-dom": "19.0.0"
34
34
  },
35
35
  "overrides": {
36
- "react": "^19.0.0",
37
- "react-dom": "^19.0.0"
36
+ "react": "19.0.0",
37
+ "react-dom": "19.0.0",
38
+ "@types/react": "19.0.0",
39
+ "@types/react-dom": "19.0.0"
38
40
  }
39
41
  }