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.
- package/package.json +1 -1
- package/src/index.mjs +36 -10
- package/template/src/widget/__widget_name__.gleam +8 -13
- package/template/src/widget/mendix/action.gleam +41 -0
- package/template/src/widget/mendix/dynamic_value.gleam +30 -0
- package/template/src/widget/mendix/editable_value.gleam +73 -0
- package/template/src/widget/mendix/file.gleam +34 -0
- package/template/src/widget/mendix/filter.gleam +109 -0
- package/template/src/widget/mendix/formatter.gleam +18 -0
- package/template/src/widget/mendix/icon.gleam +34 -0
- package/template/src/widget/mendix/list_attribute.gleam +57 -0
- package/template/src/widget/mendix/list_value.gleam +106 -0
- package/template/src/widget/mendix/reference.gleam +47 -0
- package/template/src/widget/mendix/selection.gleam +31 -0
- package/template/src/widget/mendix.gleam +65 -0
- package/template/src/widget/mendix_ffi.mjs +373 -0
- package/template/src/widget/react/event.gleam +33 -0
- package/template/src/widget/react/hook.gleam +47 -0
- package/template/src/widget/react/html.gleam +156 -0
- package/template/src/widget/react/prop.gleam +106 -0
- package/template/src/widget/react.gleam +85 -0
- package/template/src/widget/react_ffi.mjs +184 -0
- package/template/src/widget/__widget_name___ffi.mjs +0 -13
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
// Mendix Reference 타입 — 연관 관계 (Association) 값
|
|
2
|
+
// ReferenceValue: 단일 참조, ReferenceSetValue: 다중 참조
|
|
3
|
+
// 둘 다 ModifiableValue 패턴을 따름
|
|
4
|
+
|
|
5
|
+
import gleam/option.{type Option}
|
|
6
|
+
|
|
7
|
+
// === 타입 ===
|
|
8
|
+
|
|
9
|
+
pub type ReferenceValue
|
|
10
|
+
|
|
11
|
+
pub type ReferenceSetValue
|
|
12
|
+
|
|
13
|
+
// === ReferenceValue (단일 참조) ===
|
|
14
|
+
|
|
15
|
+
/// 참조 값 (없으면 None)
|
|
16
|
+
@external(javascript, "../mendix_ffi.mjs", "get_modifiable_value")
|
|
17
|
+
pub fn value(ref: ReferenceValue) -> Option(a)
|
|
18
|
+
|
|
19
|
+
/// 참조 설정 (None → 참조 해제)
|
|
20
|
+
@external(javascript, "../mendix_ffi.mjs", "modifiable_set_value")
|
|
21
|
+
pub fn set_value(ref: ReferenceValue, value: Option(a)) -> Nil
|
|
22
|
+
|
|
23
|
+
/// 읽기 전용 여부
|
|
24
|
+
@external(javascript, "../mendix_ffi.mjs", "get_modifiable_read_only")
|
|
25
|
+
pub fn read_only(ref: ReferenceValue) -> Bool
|
|
26
|
+
|
|
27
|
+
/// 유효성 검사 메시지 (없으면 None)
|
|
28
|
+
@external(javascript, "../mendix_ffi.mjs", "get_modifiable_validation")
|
|
29
|
+
pub fn validation(ref: ReferenceValue) -> Option(String)
|
|
30
|
+
|
|
31
|
+
// === ReferenceSetValue (다중 참조) ===
|
|
32
|
+
|
|
33
|
+
/// 참조 목록 (없으면 None)
|
|
34
|
+
@external(javascript, "../mendix_ffi.mjs", "get_modifiable_value")
|
|
35
|
+
pub fn multi_value(rset: ReferenceSetValue) -> Option(List(a))
|
|
36
|
+
|
|
37
|
+
/// 참조 목록 설정 (None → 전체 해제)
|
|
38
|
+
@external(javascript, "../mendix_ffi.mjs", "modifiable_set_value")
|
|
39
|
+
pub fn set_multi_value(rset: ReferenceSetValue, value: Option(List(a))) -> Nil
|
|
40
|
+
|
|
41
|
+
/// 읽기 전용 여부
|
|
42
|
+
@external(javascript, "../mendix_ffi.mjs", "get_modifiable_read_only")
|
|
43
|
+
pub fn multi_read_only(rset: ReferenceSetValue) -> Bool
|
|
44
|
+
|
|
45
|
+
/// 유효성 검사 메시지 (없으면 None)
|
|
46
|
+
@external(javascript, "../mendix_ffi.mjs", "get_modifiable_validation")
|
|
47
|
+
pub fn multi_validation(rset: ReferenceSetValue) -> Option(String)
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
// Mendix Selection 타입 — 단일/다중 선택
|
|
2
|
+
// 사용: 데이터 그리드 선택, 리스트 선택 등
|
|
3
|
+
|
|
4
|
+
import gleam/option.{type Option}
|
|
5
|
+
import widget/mendix.{type ObjectItem}
|
|
6
|
+
|
|
7
|
+
// === 타입 ===
|
|
8
|
+
|
|
9
|
+
pub type SelectionSingleValue
|
|
10
|
+
|
|
11
|
+
pub type SelectionMultiValue
|
|
12
|
+
|
|
13
|
+
// === 단일 선택 ===
|
|
14
|
+
|
|
15
|
+
/// 현재 선택된 아이템 (없으면 None)
|
|
16
|
+
@external(javascript, "../mendix_ffi.mjs", "get_selection_single")
|
|
17
|
+
pub fn selection(sel: SelectionSingleValue) -> Option(ObjectItem)
|
|
18
|
+
|
|
19
|
+
/// 선택 설정 (None → 선택 해제)
|
|
20
|
+
@external(javascript, "../mendix_ffi.mjs", "set_selection_single")
|
|
21
|
+
pub fn set_selection(sel: SelectionSingleValue, item: Option(ObjectItem)) -> Nil
|
|
22
|
+
|
|
23
|
+
// === 다중 선택 ===
|
|
24
|
+
|
|
25
|
+
/// 현재 선택된 아이템 목록
|
|
26
|
+
@external(javascript, "../mendix_ffi.mjs", "get_selection_multi")
|
|
27
|
+
pub fn selections(sel: SelectionMultiValue) -> List(ObjectItem)
|
|
28
|
+
|
|
29
|
+
/// 선택 목록 설정
|
|
30
|
+
@external(javascript, "../mendix_ffi.mjs", "set_selection_multi")
|
|
31
|
+
pub fn set_selections(sel: SelectionMultiValue, items: List(ObjectItem)) -> Nil
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
// Mendix Pluggable Widget API - 핵심 타입 + JsProps 접근자
|
|
2
|
+
// ValueStatus, ObjectItem 타입과 props 접근 유틸리티
|
|
3
|
+
|
|
4
|
+
import gleam/option.{type Option}
|
|
5
|
+
import widget/react.{type JsProps}
|
|
6
|
+
|
|
7
|
+
// === ValueStatus ===
|
|
8
|
+
|
|
9
|
+
pub type ValueStatus {
|
|
10
|
+
Available
|
|
11
|
+
Unavailable
|
|
12
|
+
Loading
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
pub fn to_value_status(status: String) -> ValueStatus {
|
|
16
|
+
case status {
|
|
17
|
+
"available" -> Available
|
|
18
|
+
"loading" -> Loading
|
|
19
|
+
_ -> Unavailable
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
// === ObjectItem ===
|
|
24
|
+
|
|
25
|
+
pub type ObjectItem
|
|
26
|
+
|
|
27
|
+
@external(javascript, "./mendix_ffi.mjs", "get_object_id")
|
|
28
|
+
pub fn object_id(item: ObjectItem) -> String
|
|
29
|
+
|
|
30
|
+
// === JsProps 접근자 ===
|
|
31
|
+
|
|
32
|
+
/// Mendix props에서 값 추출 (undefined → None)
|
|
33
|
+
@external(javascript, "./mendix_ffi.mjs", "get_mendix_prop")
|
|
34
|
+
pub fn get_prop(props: JsProps, key: String) -> Option(a)
|
|
35
|
+
|
|
36
|
+
/// Mendix props에서 항상 존재하는 값 추출
|
|
37
|
+
@external(javascript, "./mendix_ffi.mjs", "get_mendix_prop_required")
|
|
38
|
+
pub fn get_prop_required(props: JsProps, key: String) -> a
|
|
39
|
+
|
|
40
|
+
/// Mendix props에서 문자열 속성값 추출 (undefined → "")
|
|
41
|
+
@external(javascript, "./react_ffi.mjs", "get_string_prop")
|
|
42
|
+
pub fn get_string_prop(props: JsProps, key: String) -> String
|
|
43
|
+
|
|
44
|
+
/// Mendix props에서 키 존재 확인
|
|
45
|
+
@external(javascript, "./react_ffi.mjs", "has_prop")
|
|
46
|
+
pub fn has_prop(props: JsProps, key: String) -> Bool
|
|
47
|
+
|
|
48
|
+
// === Status 접근 (status 속성을 가진 모든 Mendix 객체) ===
|
|
49
|
+
|
|
50
|
+
@external(javascript, "./mendix_ffi.mjs", "get_status")
|
|
51
|
+
fn get_status_raw(obj: a) -> String
|
|
52
|
+
|
|
53
|
+
pub fn get_status(obj: a) -> ValueStatus {
|
|
54
|
+
to_value_status(get_status_raw(obj))
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
// === Option 변환 유틸리티 (FFI 경계 처리) ===
|
|
58
|
+
|
|
59
|
+
/// JS 값을 Gleam Option으로 변환 (undefined/null → None)
|
|
60
|
+
@external(javascript, "./mendix_ffi.mjs", "to_option")
|
|
61
|
+
pub fn to_option(value: a) -> Option(a)
|
|
62
|
+
|
|
63
|
+
/// Gleam Option을 JS 값으로 변환 (None → undefined)
|
|
64
|
+
@external(javascript, "./mendix_ffi.mjs", "from_option")
|
|
65
|
+
pub fn from_option(option: Option(a)) -> a
|
|
@@ -0,0 +1,373 @@
|
|
|
1
|
+
// Mendix Pluggable Widget API FFI 어댑터
|
|
2
|
+
// Mendix 런타임 타입의 속성 접근과 메서드 호출을 Gleam에 노출
|
|
3
|
+
import { Some, None } from "../../gleam_stdlib/gleam/option.mjs";
|
|
4
|
+
import { Ok, Error as GleamError, toList } from "../gleam.mjs";
|
|
5
|
+
|
|
6
|
+
// === JS undefined ↔ Gleam Option 변환 ===
|
|
7
|
+
|
|
8
|
+
export function to_option(value) {
|
|
9
|
+
return value !== undefined && value !== null ? new Some(value) : new None();
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export function from_option(option) {
|
|
13
|
+
return option instanceof Some ? option[0] : undefined;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
// === JsProps 접근 ===
|
|
17
|
+
|
|
18
|
+
export function get_mendix_prop(props, key) {
|
|
19
|
+
return to_option(props[key]);
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export function get_mendix_prop_required(props, key) {
|
|
23
|
+
return props[key];
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
// === ValueStatus / ObjectItem ===
|
|
27
|
+
|
|
28
|
+
export function get_status(obj) {
|
|
29
|
+
return obj.status;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export function get_object_id(item) {
|
|
33
|
+
return item.id;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
// === EditableValue ===
|
|
37
|
+
|
|
38
|
+
export function get_editable_value(obj) {
|
|
39
|
+
return to_option(obj.value);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export function get_editable_read_only(obj) {
|
|
43
|
+
return obj.readOnly;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export function get_editable_validation(obj) {
|
|
47
|
+
return to_option(obj.validation);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
export function get_editable_display_value(obj) {
|
|
51
|
+
return obj.displayValue;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
export function get_editable_formatter(obj) {
|
|
55
|
+
return obj.formatter;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
export function get_editable_universe(obj) {
|
|
59
|
+
return to_option(obj.universe ? toList(obj.universe) : undefined);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
export function editable_set_value(obj, option) {
|
|
63
|
+
obj.setValue(from_option(option));
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
export function editable_set_text_value(obj, text) {
|
|
67
|
+
obj.setTextValue(text);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
export function editable_set_validator(obj, option_fn) {
|
|
71
|
+
if (option_fn instanceof Some) {
|
|
72
|
+
const gleam_fn = option_fn[0];
|
|
73
|
+
obj.setValidator((value) => {
|
|
74
|
+
const result = gleam_fn(to_option(value));
|
|
75
|
+
return from_option(result);
|
|
76
|
+
});
|
|
77
|
+
} else {
|
|
78
|
+
obj.setValidator(undefined);
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
// === ActionValue ===
|
|
83
|
+
|
|
84
|
+
export function get_action_can_execute(a) {
|
|
85
|
+
return a.canExecute;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
export function get_action_is_executing(a) {
|
|
89
|
+
return a.isExecuting;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
export function action_execute(a) {
|
|
93
|
+
a.execute();
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
// === DynamicValue ===
|
|
97
|
+
|
|
98
|
+
export function get_dynamic_value(obj) {
|
|
99
|
+
return to_option(obj.value);
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
// === ListValue ===
|
|
103
|
+
|
|
104
|
+
export function get_list_items(lv) {
|
|
105
|
+
return to_option(lv.items ? toList(lv.items) : undefined);
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
export function get_list_offset(lv) {
|
|
109
|
+
return lv.offset;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
export function get_list_limit(lv) {
|
|
113
|
+
return lv.limit;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
export function get_list_has_more_items(lv) {
|
|
117
|
+
return to_option(lv.hasMoreItems);
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
export function get_list_total_count(lv) {
|
|
121
|
+
return to_option(lv.totalCount);
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
export function get_list_sort_order(lv) {
|
|
125
|
+
return toList(lv.sortOrder || []);
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
export function get_list_filter(lv) {
|
|
129
|
+
return to_option(lv.filter);
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
export function list_set_offset(lv, offset) {
|
|
133
|
+
lv.setOffset(offset);
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
export function list_set_limit(lv, limit) {
|
|
137
|
+
lv.setLimit(limit);
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
export function list_set_filter(lv, option) {
|
|
141
|
+
lv.setFilter(from_option(option));
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
export function list_set_sort_order(lv, gleam_list) {
|
|
145
|
+
lv.setSortOrder(gleam_list.toArray());
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
export function list_reload(lv) {
|
|
149
|
+
lv.reload();
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
export function list_request_total_count(lv, need) {
|
|
153
|
+
lv.requestTotalCount(need);
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
// === List-linked 타입 (공용) ===
|
|
157
|
+
|
|
158
|
+
export function list_type_get(list_type, item) {
|
|
159
|
+
return list_type.get(item);
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
// === ListAttributeValue 메타데이터 ===
|
|
163
|
+
|
|
164
|
+
export function get_list_attr_id(attr) {
|
|
165
|
+
return attr.id;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
export function get_list_attr_sortable(attr) {
|
|
169
|
+
return attr.sortable;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
export function get_list_attr_filterable(attr) {
|
|
173
|
+
return attr.filterable;
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
export function get_list_attr_type(attr) {
|
|
177
|
+
return attr.type;
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
export function get_list_attr_formatter(attr) {
|
|
181
|
+
return attr.formatter;
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
// === Selection ===
|
|
185
|
+
|
|
186
|
+
export function get_selection_single(sel) {
|
|
187
|
+
return to_option(sel.selection);
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
export function get_selection_multi(sel) {
|
|
191
|
+
return toList(sel.selection || []);
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
export function set_selection_single(sel, option) {
|
|
195
|
+
sel.setSelection(from_option(option));
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
export function set_selection_multi(sel, gleam_list) {
|
|
199
|
+
sel.setSelection(gleam_list.toArray());
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
// === ModifiableValue / Reference ===
|
|
203
|
+
|
|
204
|
+
export function get_modifiable_value(obj) {
|
|
205
|
+
return to_option(obj.value);
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
export function modifiable_set_value(obj, option) {
|
|
209
|
+
obj.setValue(from_option(option));
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
export function get_modifiable_read_only(obj) {
|
|
213
|
+
return obj.readOnly;
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
export function get_modifiable_validation(obj) {
|
|
217
|
+
return to_option(obj.validation);
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
// === FileValue / WebImage ===
|
|
221
|
+
|
|
222
|
+
export function get_file_uri(f) {
|
|
223
|
+
return f.uri;
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
export function get_file_name(f) {
|
|
227
|
+
return to_option(f.name);
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
export function get_image_alt_text(img) {
|
|
231
|
+
return to_option(img.altText);
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
// === WebIcon ===
|
|
235
|
+
|
|
236
|
+
export function get_icon_type(icon) {
|
|
237
|
+
return icon.type;
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
export function get_icon_class(icon) {
|
|
241
|
+
return icon.iconClass || "";
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
export function get_icon_url(icon) {
|
|
245
|
+
return icon.iconUrl || "";
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
// === Formatter ===
|
|
249
|
+
|
|
250
|
+
export function formatter_format(fmt, option_value) {
|
|
251
|
+
return fmt.format(from_option(option_value));
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
export function formatter_parse(fmt, text) {
|
|
255
|
+
const result = fmt.parse(text);
|
|
256
|
+
if (result.valid) {
|
|
257
|
+
return new Ok(to_option(result.value));
|
|
258
|
+
} else {
|
|
259
|
+
return new GleamError(undefined);
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
// === SortInstruction ===
|
|
264
|
+
|
|
265
|
+
export function make_sort_instruction(id, asc) {
|
|
266
|
+
return { id, asc };
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
export function get_sort_id(instr) {
|
|
270
|
+
return instr.id;
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
export function get_sort_asc(instr) {
|
|
274
|
+
return instr.asc;
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
// === Filter 빌더 (mendix/filters/builders 래핑) ===
|
|
278
|
+
// Mendix 런타임에서 제공하는 외부 모듈 (Rollup에서 external 처리)
|
|
279
|
+
import * as filters from "mendix/filters/builders";
|
|
280
|
+
|
|
281
|
+
// Boolean 조합
|
|
282
|
+
export function filter_and(conditions) {
|
|
283
|
+
return filters.and(...conditions.toArray());
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
export function filter_or(conditions) {
|
|
287
|
+
return filters.or(...conditions.toArray());
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
export function filter_not(condition) {
|
|
291
|
+
return filters.not(condition);
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
// 동등 비교
|
|
295
|
+
export function filter_equals(a, b) {
|
|
296
|
+
return filters.equals(a, b);
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
export function filter_not_equal(a, b) {
|
|
300
|
+
return filters.notEqual(a, b);
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
// 크기 비교
|
|
304
|
+
export function filter_greater_than(a, b) {
|
|
305
|
+
return filters.greaterThan(a, b);
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
export function filter_greater_than_or_equal(a, b) {
|
|
309
|
+
return filters.greaterThanOrEqual(a, b);
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
export function filter_less_than(a, b) {
|
|
313
|
+
return filters.lessThan(a, b);
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
export function filter_less_than_or_equal(a, b) {
|
|
317
|
+
return filters.lessThanOrEqual(a, b);
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
// 문자열 검색
|
|
321
|
+
export function filter_contains(a, b) {
|
|
322
|
+
return filters.contains(a, b);
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
export function filter_starts_with(a, b) {
|
|
326
|
+
return filters.startsWith(a, b);
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
export function filter_ends_with(a, b) {
|
|
330
|
+
return filters.endsWith(a, b);
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
// 날짜 비교
|
|
334
|
+
export function filter_day_equals(a, b) {
|
|
335
|
+
return filters.dayEquals(a, b);
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
export function filter_day_not_equal(a, b) {
|
|
339
|
+
return filters.dayNotEqual(a, b);
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
export function filter_day_greater_than(a, b) {
|
|
343
|
+
return filters.dayGreaterThan(a, b);
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
export function filter_day_greater_than_or_equal(a, b) {
|
|
347
|
+
return filters.dayGreaterThanOrEqual(a, b);
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
export function filter_day_less_than(a, b) {
|
|
351
|
+
return filters.dayLessThan(a, b);
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
export function filter_day_less_than_or_equal(a, b) {
|
|
355
|
+
return filters.dayLessThanOrEqual(a, b);
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
// 표현식 생성
|
|
359
|
+
export function filter_attribute(id) {
|
|
360
|
+
return filters.attribute(id);
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
export function filter_association(id) {
|
|
364
|
+
return filters.association(id);
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
export function filter_literal(value) {
|
|
368
|
+
return filters.literal(value);
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
export function filter_empty() {
|
|
372
|
+
return filters.empty();
|
|
373
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
// React 이벤트 타입 + 값 추출 함수
|
|
2
|
+
|
|
3
|
+
// === 이벤트 타입 (opaque) ===
|
|
4
|
+
|
|
5
|
+
pub type Event
|
|
6
|
+
|
|
7
|
+
pub type MouseEvent
|
|
8
|
+
|
|
9
|
+
pub type ChangeEvent
|
|
10
|
+
|
|
11
|
+
pub type KeyboardEvent
|
|
12
|
+
|
|
13
|
+
pub type FormEvent
|
|
14
|
+
|
|
15
|
+
pub type FocusEvent
|
|
16
|
+
|
|
17
|
+
// === 값 추출 ===
|
|
18
|
+
|
|
19
|
+
/// input/textarea의 현재 값 추출
|
|
20
|
+
@external(javascript, "../react_ffi.mjs", "get_target_value")
|
|
21
|
+
pub fn target_value(event: event) -> String
|
|
22
|
+
|
|
23
|
+
/// 기본 동작 방지
|
|
24
|
+
@external(javascript, "../react_ffi.mjs", "prevent_default")
|
|
25
|
+
pub fn prevent_default(event: event) -> Nil
|
|
26
|
+
|
|
27
|
+
/// 이벤트 전파 중지
|
|
28
|
+
@external(javascript, "../react_ffi.mjs", "stop_propagation")
|
|
29
|
+
pub fn stop_propagation(event: event) -> Nil
|
|
30
|
+
|
|
31
|
+
/// 키보드 이벤트의 키 값
|
|
32
|
+
@external(javascript, "../react_ffi.mjs", "get_event_key")
|
|
33
|
+
pub fn key(event: event) -> String
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
// React Hooks - useState, useEffect 등
|
|
2
|
+
|
|
3
|
+
import widget/react.{type Ref}
|
|
4
|
+
|
|
5
|
+
// === useState ===
|
|
6
|
+
|
|
7
|
+
/// 상태 훅. #(현재값, 세터함수) 튜플 반환
|
|
8
|
+
@external(javascript, "../react_ffi.mjs", "use_state")
|
|
9
|
+
pub fn use_state(initial: a) -> #(a, fn(a) -> Nil)
|
|
10
|
+
|
|
11
|
+
// === useEffect ===
|
|
12
|
+
|
|
13
|
+
/// 의존성 배열과 함께 실행
|
|
14
|
+
@external(javascript, "../react_ffi.mjs", "use_effect")
|
|
15
|
+
pub fn use_effect(effect_fn: fn() -> Nil, deps: List(a)) -> Nil
|
|
16
|
+
|
|
17
|
+
/// 매 렌더링마다 실행 (deps 없음)
|
|
18
|
+
@external(javascript, "../react_ffi.mjs", "use_effect_always")
|
|
19
|
+
pub fn use_effect_always(effect_fn: fn() -> Nil) -> Nil
|
|
20
|
+
|
|
21
|
+
/// 마운트 시 한 번만 실행 (deps = [])
|
|
22
|
+
@external(javascript, "../react_ffi.mjs", "use_effect_once")
|
|
23
|
+
pub fn use_effect_once(effect_fn: fn() -> Nil) -> Nil
|
|
24
|
+
|
|
25
|
+
// === useMemo / useCallback ===
|
|
26
|
+
|
|
27
|
+
/// 메모이제이션된 값 계산
|
|
28
|
+
@external(javascript, "../react_ffi.mjs", "use_memo")
|
|
29
|
+
pub fn use_memo(compute: fn() -> a, deps: List(b)) -> a
|
|
30
|
+
|
|
31
|
+
/// 메모이제이션된 콜백
|
|
32
|
+
@external(javascript, "../react_ffi.mjs", "use_callback")
|
|
33
|
+
pub fn use_callback(callback: fn(a) -> b, deps: List(c)) -> fn(a) -> b
|
|
34
|
+
|
|
35
|
+
// === useRef ===
|
|
36
|
+
|
|
37
|
+
/// ref 생성
|
|
38
|
+
@external(javascript, "../react_ffi.mjs", "use_ref")
|
|
39
|
+
pub fn use_ref(initial: a) -> Ref(a)
|
|
40
|
+
|
|
41
|
+
/// ref 현재 값 읽기
|
|
42
|
+
@external(javascript, "../react_ffi.mjs", "get_ref_current")
|
|
43
|
+
pub fn get_ref(ref: Ref(a)) -> a
|
|
44
|
+
|
|
45
|
+
/// ref 현재 값 설정
|
|
46
|
+
@external(javascript, "../react_ffi.mjs", "set_ref_current")
|
|
47
|
+
pub fn set_ref(ref: Ref(a), value: a) -> Nil
|