create-mendix-widget-gleam 1.0.0 → 1.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.
@@ -0,0 +1,156 @@
1
+ // HTML 태그 편의 함수 - 순수 Gleam, FFI 없음
2
+ // react.el / react.el_ / react.void 래퍼
3
+
4
+ import widget/react.{type Props, type ReactElement}
5
+
6
+ // === 컨테이너 ===
7
+
8
+ pub fn div(props: Props, children: List(ReactElement)) -> ReactElement {
9
+ react.el("div", props, children)
10
+ }
11
+
12
+ pub fn div_(children: List(ReactElement)) -> ReactElement {
13
+ react.el_("div", children)
14
+ }
15
+
16
+ pub fn span(props: Props, children: List(ReactElement)) -> ReactElement {
17
+ react.el("span", props, children)
18
+ }
19
+
20
+ pub fn span_(children: List(ReactElement)) -> ReactElement {
21
+ react.el_("span", children)
22
+ }
23
+
24
+ pub fn section(props: Props, children: List(ReactElement)) -> ReactElement {
25
+ react.el("section", props, children)
26
+ }
27
+
28
+ pub fn main(props: Props, children: List(ReactElement)) -> ReactElement {
29
+ react.el("main", props, children)
30
+ }
31
+
32
+ // === 텍스트 ===
33
+
34
+ pub fn p(props: Props, children: List(ReactElement)) -> ReactElement {
35
+ react.el("p", props, children)
36
+ }
37
+
38
+ pub fn p_(children: List(ReactElement)) -> ReactElement {
39
+ react.el_("p", children)
40
+ }
41
+
42
+ pub fn h1(props: Props, children: List(ReactElement)) -> ReactElement {
43
+ react.el("h1", props, children)
44
+ }
45
+
46
+ pub fn h2(props: Props, children: List(ReactElement)) -> ReactElement {
47
+ react.el("h2", props, children)
48
+ }
49
+
50
+ pub fn h3(props: Props, children: List(ReactElement)) -> ReactElement {
51
+ react.el("h3", props, children)
52
+ }
53
+
54
+ pub fn h4(props: Props, children: List(ReactElement)) -> ReactElement {
55
+ react.el("h4", props, children)
56
+ }
57
+
58
+ pub fn h5(props: Props, children: List(ReactElement)) -> ReactElement {
59
+ react.el("h5", props, children)
60
+ }
61
+
62
+ pub fn h6(props: Props, children: List(ReactElement)) -> ReactElement {
63
+ react.el("h6", props, children)
64
+ }
65
+
66
+ // === 리스트 ===
67
+
68
+ pub fn ul(props: Props, children: List(ReactElement)) -> ReactElement {
69
+ react.el("ul", props, children)
70
+ }
71
+
72
+ pub fn ol(props: Props, children: List(ReactElement)) -> ReactElement {
73
+ react.el("ol", props, children)
74
+ }
75
+
76
+ pub fn li(props: Props, children: List(ReactElement)) -> ReactElement {
77
+ react.el("li", props, children)
78
+ }
79
+
80
+ // === 폼 ===
81
+
82
+ pub fn form(props: Props, children: List(ReactElement)) -> ReactElement {
83
+ react.el("form", props, children)
84
+ }
85
+
86
+ pub fn button(props: Props, children: List(ReactElement)) -> ReactElement {
87
+ react.el("button", props, children)
88
+ }
89
+
90
+ pub fn label(props: Props, children: List(ReactElement)) -> ReactElement {
91
+ react.el("label", props, children)
92
+ }
93
+
94
+ pub fn input(props: Props) -> ReactElement {
95
+ react.void("input", props)
96
+ }
97
+
98
+ pub fn textarea(props: Props, children: List(ReactElement)) -> ReactElement {
99
+ react.el("textarea", props, children)
100
+ }
101
+
102
+ pub fn select(props: Props, children: List(ReactElement)) -> ReactElement {
103
+ react.el("select", props, children)
104
+ }
105
+
106
+ pub fn option(props: Props, children: List(ReactElement)) -> ReactElement {
107
+ react.el("option", props, children)
108
+ }
109
+
110
+ // === 테이블 ===
111
+
112
+ pub fn table(props: Props, children: List(ReactElement)) -> ReactElement {
113
+ react.el("table", props, children)
114
+ }
115
+
116
+ pub fn thead(props: Props, children: List(ReactElement)) -> ReactElement {
117
+ react.el("thead", props, children)
118
+ }
119
+
120
+ pub fn tbody(props: Props, children: List(ReactElement)) -> ReactElement {
121
+ react.el("tbody", props, children)
122
+ }
123
+
124
+ pub fn tr(props: Props, children: List(ReactElement)) -> ReactElement {
125
+ react.el("tr", props, children)
126
+ }
127
+
128
+ pub fn td(props: Props, children: List(ReactElement)) -> ReactElement {
129
+ react.el("td", props, children)
130
+ }
131
+
132
+ pub fn th(props: Props, children: List(ReactElement)) -> ReactElement {
133
+ react.el("th", props, children)
134
+ }
135
+
136
+ // === 기타 ===
137
+
138
+ pub fn a(props: Props, children: List(ReactElement)) -> ReactElement {
139
+ react.el("a", props, children)
140
+ }
141
+
142
+ pub fn img(props: Props) -> ReactElement {
143
+ react.void("img", props)
144
+ }
145
+
146
+ pub fn br() -> ReactElement {
147
+ react.void("br", empty_props())
148
+ }
149
+
150
+ pub fn hr(props: Props) -> ReactElement {
151
+ react.void("hr", props)
152
+ }
153
+
154
+ // br에서 사용할 빈 props (순환 의존 방지용 내부 FFI)
155
+ @external(javascript, "../react_ffi.mjs", "empty_props")
156
+ fn empty_props() -> Props
@@ -0,0 +1,106 @@
1
+ // Props 빌더 - 파이프라인 API로 React props 구성
2
+
3
+ import widget/react.{type Props, type Ref}
4
+
5
+ // === Style 타입 ===
6
+
7
+ /// CSS 스타일 객체
8
+ pub type Style
9
+
10
+ // === Props 생성 ===
11
+
12
+ /// 빈 props 객체 생성
13
+ @external(javascript, "../react_ffi.mjs", "empty_props")
14
+ pub fn new() -> Props
15
+
16
+ // === 속성 설정 (모두 Props -> Props, 파이프라인 가능) ===
17
+
18
+ /// 문자열 속성
19
+ @external(javascript, "../react_ffi.mjs", "set_prop_string")
20
+ pub fn string(props: Props, key: String, value: String) -> Props
21
+
22
+ /// 정수 속성
23
+ @external(javascript, "../react_ffi.mjs", "set_prop_int")
24
+ pub fn int(props: Props, key: String, value: Int) -> Props
25
+
26
+ /// 실수 속성
27
+ @external(javascript, "../react_ffi.mjs", "set_prop_float")
28
+ pub fn float(props: Props, key: String, value: Float) -> Props
29
+
30
+ /// 불리언 속성
31
+ @external(javascript, "../react_ffi.mjs", "set_prop_bool")
32
+ pub fn bool(props: Props, key: String, value: Bool) -> Props
33
+
34
+ /// 임의 타입 속성
35
+ @external(javascript, "../react_ffi.mjs", "set_prop_any")
36
+ pub fn any(props: Props, key: String, value: a) -> Props
37
+
38
+ // === CSS 클래스 ===
39
+
40
+ /// className 설정
41
+ @external(javascript, "../react_ffi.mjs", "set_class_name")
42
+ pub fn class(props: Props, class_name: String) -> Props
43
+
44
+ /// 여러 클래스명을 공백으로 결합
45
+ @external(javascript, "../react_ffi.mjs", "set_class_names")
46
+ pub fn classes(props: Props, class_names: List(String)) -> Props
47
+
48
+ // === 특수 속성 ===
49
+
50
+ /// key 설정
51
+ @external(javascript, "../react_ffi.mjs", "set_key")
52
+ pub fn key(props: Props, key: String) -> Props
53
+
54
+ /// ref 설정
55
+ @external(javascript, "../react_ffi.mjs", "set_ref")
56
+ pub fn ref(props: Props, ref: Ref(a)) -> Props
57
+
58
+ /// style 객체 설정
59
+ @external(javascript, "../react_ffi.mjs", "set_style")
60
+ pub fn style(props: Props, style: Style) -> Props
61
+
62
+ // === 이벤트 핸들러 ===
63
+
64
+ /// 범용 이벤트 핸들러
65
+ @external(javascript, "../react_ffi.mjs", "set_prop_handler")
66
+ pub fn on(props: Props, event_name: String, handler: fn(e) -> Nil) -> Props
67
+
68
+ /// onClick
69
+ pub fn on_click(props: Props, handler: fn(e) -> Nil) -> Props {
70
+ on(props, "onClick", handler)
71
+ }
72
+
73
+ /// onChange
74
+ pub fn on_change(props: Props, handler: fn(e) -> Nil) -> Props {
75
+ on(props, "onChange", handler)
76
+ }
77
+
78
+ /// onSubmit
79
+ pub fn on_submit(props: Props, handler: fn(e) -> Nil) -> Props {
80
+ on(props, "onSubmit", handler)
81
+ }
82
+
83
+ /// onKeyDown
84
+ pub fn on_key_down(props: Props, handler: fn(e) -> Nil) -> Props {
85
+ on(props, "onKeyDown", handler)
86
+ }
87
+
88
+ /// onFocus
89
+ pub fn on_focus(props: Props, handler: fn(e) -> Nil) -> Props {
90
+ on(props, "onFocus", handler)
91
+ }
92
+
93
+ /// onBlur
94
+ pub fn on_blur(props: Props, handler: fn(e) -> Nil) -> Props {
95
+ on(props, "onBlur", handler)
96
+ }
97
+
98
+ // === Style 빌더 ===
99
+
100
+ /// 빈 스타일 객체 생성
101
+ @external(javascript, "../react_ffi.mjs", "empty_style")
102
+ pub fn new_style() -> Style
103
+
104
+ /// 스타일 속성 설정
105
+ @external(javascript, "../react_ffi.mjs", "set_style_prop")
106
+ pub fn set(style: Style, key: String, value: String) -> Style
@@ -0,0 +1,85 @@
1
+ // React 핵심 타입 + createElement + fragment/text/none
2
+
3
+ import gleam/option.{type Option, None, Some}
4
+
5
+ // === Opaque 타입 ===
6
+
7
+ /// React가 렌더링하는 요소
8
+ pub type ReactElement
9
+
10
+ /// Mendix가 전달하는 props 객체
11
+ pub type JsProps
12
+
13
+ /// React 컴포넌트 참조
14
+ pub type Component
15
+
16
+ /// React ref 객체
17
+ pub type Ref(a)
18
+
19
+ // Props 타입 (react/prop 모듈에서 빌더 제공)
20
+ pub type Props
21
+
22
+ // === 요소 생성 FFI 바인딩 ===
23
+
24
+ /// 범용 HTML 요소 생성
25
+ @external(javascript, "./react_ffi.mjs", "create_element")
26
+ pub fn el(
27
+ tag: String,
28
+ props: Props,
29
+ children: List(ReactElement),
30
+ ) -> ReactElement
31
+
32
+ /// props 없이 자식만으로 요소 생성
33
+ @external(javascript, "./react_ffi.mjs", "create_element_no_props")
34
+ pub fn el_(tag: String, children: List(ReactElement)) -> ReactElement
35
+
36
+ /// self-closing 요소 (input, img, br 등)
37
+ @external(javascript, "./react_ffi.mjs", "create_void_element")
38
+ pub fn void(tag: String, props: Props) -> ReactElement
39
+
40
+ /// React 컴포넌트 합성
41
+ @external(javascript, "./react_ffi.mjs", "create_component")
42
+ pub fn component(
43
+ comp: Component,
44
+ props: Props,
45
+ children: List(ReactElement),
46
+ ) -> ReactElement
47
+
48
+ // === Fragment / null / text ===
49
+
50
+ /// Fragment로 여러 자식을 감싸기
51
+ @external(javascript, "./react_ffi.mjs", "fragment")
52
+ pub fn fragment(children: List(ReactElement)) -> ReactElement
53
+
54
+ /// key가 있는 Fragment
55
+ @external(javascript, "./react_ffi.mjs", "keyed_fragment")
56
+ pub fn keyed_fragment(key: String, children: List(ReactElement)) -> ReactElement
57
+
58
+ /// null 렌더링 (아무것도 표시하지 않음)
59
+ @external(javascript, "./react_ffi.mjs", "null_element")
60
+ pub fn none() -> ReactElement
61
+
62
+ /// 텍스트 노드
63
+ @external(javascript, "./react_ffi.mjs", "text")
64
+ pub fn text(content: String) -> ReactElement
65
+
66
+ // === 순수 Gleam 헬퍼 ===
67
+
68
+ /// Bool 기반 조건부 렌더링
69
+ pub fn when(condition: Bool, element_fn: fn() -> ReactElement) -> ReactElement {
70
+ case condition {
71
+ True -> element_fn()
72
+ False -> none()
73
+ }
74
+ }
75
+
76
+ /// Option 기반 조건부 렌더링
77
+ pub fn when_some(
78
+ option: Option(a),
79
+ render_fn: fn(a) -> ReactElement,
80
+ ) -> ReactElement {
81
+ case option {
82
+ Some(value) -> render_fn(value)
83
+ None -> none()
84
+ }
85
+ }
@@ -0,0 +1,184 @@
1
+ // React FFI 어댑터 - 모든 React 원시 함수를 Gleam에 노출
2
+ import * as React from "react";
3
+ import { toList } from "../gleam.mjs";
4
+
5
+ // === 요소 생성 ===
6
+
7
+ // 범용 요소 생성: tag + props + children(Gleam List)
8
+ export function create_element(tag, props, children) {
9
+ return React.createElement(tag, props, ...children.toArray());
10
+ }
11
+
12
+ // props 없이 자식만
13
+ export function create_element_no_props(tag, children) {
14
+ return React.createElement(tag, null, ...children.toArray());
15
+ }
16
+
17
+ // self-closing 요소 (input, img, br 등)
18
+ export function create_void_element(tag, props) {
19
+ return React.createElement(tag, props);
20
+ }
21
+
22
+ // React 컴포넌트 합성
23
+ export function create_component(component, props, children) {
24
+ return React.createElement(component, props, ...children.toArray());
25
+ }
26
+
27
+ // === Fragment / null / text ===
28
+
29
+ export function fragment(children) {
30
+ return React.createElement(React.Fragment, null, ...children.toArray());
31
+ }
32
+
33
+ export function keyed_fragment(key, children) {
34
+ return React.createElement(React.Fragment, { key }, ...children.toArray());
35
+ }
36
+
37
+ export function null_element() {
38
+ return null;
39
+ }
40
+
41
+ export function text(content) {
42
+ return content;
43
+ }
44
+
45
+ // === Props 빌더 ===
46
+
47
+ export function empty_props() {
48
+ return {};
49
+ }
50
+
51
+ export function set_prop_string(props, key, value) {
52
+ return { ...props, [key]: value };
53
+ }
54
+
55
+ export function set_prop_int(props, key, value) {
56
+ return { ...props, [key]: value };
57
+ }
58
+
59
+ export function set_prop_float(props, key, value) {
60
+ return { ...props, [key]: value };
61
+ }
62
+
63
+ export function set_prop_bool(props, key, value) {
64
+ return { ...props, [key]: value };
65
+ }
66
+
67
+ export function set_prop_handler(props, key, handler) {
68
+ return { ...props, [key]: handler };
69
+ }
70
+
71
+ export function set_prop_any(props, key, value) {
72
+ return { ...props, [key]: value };
73
+ }
74
+
75
+ export function set_class_name(props, class_name) {
76
+ return { ...props, className: class_name };
77
+ }
78
+
79
+ export function set_class_names(props, class_names) {
80
+ return { ...props, className: class_names.toArray().join(" ") };
81
+ }
82
+
83
+ export function set_key(props, key) {
84
+ return { ...props, key };
85
+ }
86
+
87
+ export function set_ref(props, ref) {
88
+ return { ...props, ref };
89
+ }
90
+
91
+ export function set_style(props, style_obj) {
92
+ return { ...props, style: style_obj };
93
+ }
94
+
95
+ // === Style 빌더 ===
96
+
97
+ export function empty_style() {
98
+ return {};
99
+ }
100
+
101
+ export function set_style_prop(style, key, value) {
102
+ return { ...style, [key]: value };
103
+ }
104
+
105
+ // === Props 읽기 (Mendix props에서 값 추출) ===
106
+
107
+ export function get_string_prop(props, key) {
108
+ const value = props[key];
109
+ return value !== undefined && value !== null ? String(value) : "";
110
+ }
111
+
112
+ export function get_prop(props, key) {
113
+ return props[key];
114
+ }
115
+
116
+ export function has_prop(props, key) {
117
+ return key in props && props[key] !== undefined && props[key] !== null;
118
+ }
119
+
120
+ // === React Hooks ===
121
+
122
+ export function use_state(initial) {
123
+ return React.useState(initial);
124
+ }
125
+
126
+ export function use_effect(effect_fn, deps) {
127
+ React.useEffect(effect_fn, deps.toArray());
128
+ }
129
+
130
+ export function use_effect_always(effect_fn) {
131
+ React.useEffect(effect_fn);
132
+ }
133
+
134
+ export function use_effect_once(effect_fn) {
135
+ React.useEffect(effect_fn, []);
136
+ }
137
+
138
+ export function use_memo(compute_fn, deps) {
139
+ return React.useMemo(compute_fn, deps.toArray());
140
+ }
141
+
142
+ export function use_callback(callback, deps) {
143
+ return React.useCallback(callback, deps.toArray());
144
+ }
145
+
146
+ export function use_ref(initial) {
147
+ return React.useRef(initial);
148
+ }
149
+
150
+ export function get_ref_current(ref) {
151
+ return ref.current;
152
+ }
153
+
154
+ export function set_ref_current(ref, value) {
155
+ ref.current = value;
156
+ }
157
+
158
+ // === 이벤트 ===
159
+
160
+ export function get_target_value(event) {
161
+ return event.target.value ?? "";
162
+ }
163
+
164
+ export function prevent_default(event) {
165
+ event.preventDefault();
166
+ }
167
+
168
+ export function stop_propagation(event) {
169
+ event.stopPropagation();
170
+ }
171
+
172
+ export function get_event_key(event) {
173
+ return event.key;
174
+ }
175
+
176
+ // === 유틸리티 ===
177
+
178
+ export function list_to_array(gleam_list) {
179
+ return gleam_list.toArray();
180
+ }
181
+
182
+ export function array_to_list(js_array) {
183
+ return toList(js_array);
184
+ }
@@ -1,13 +0,0 @@
1
- // React FFI 어댑터 - React 원시 함수만 노출
2
- import * as React from "react";
3
-
4
- // div 요소에 텍스트 자식을 렌더링
5
- export function create_div(class_name, text_content) {
6
- return React.createElement("div", { className: class_name }, text_content);
7
- }
8
-
9
- // props 객체에서 문자열 속성값 추출
10
- export function get_string_prop(props, key) {
11
- const value = props[key];
12
- return value !== undefined && value !== null ? String(value) : "";
13
- }