Current section
Files
Jump to
Current section
Files
mendix_widget_gleam
README.md
README.md
# MendixWidgetGleam
**Gleam μΈμ΄λ‘ Mendix Pluggable Widgetμ κ°λ°νλ νλ‘μ νΈ.**
JSXλ₯Ό μ¬μ©νμ§ μκ³ , Gleam μ½λλ§μΌλ‘ React μ»΄ν¬λνΈλ₯Ό μμ±νμ¬ Mendix Studio Proμμ λμνλ μμ ―μ λ§λ λ€. Reactμ Mendix API λ°μΈλ©μ [glendix](https://hexdocs.pm/glendix/) ν¨ν€μ§κ° μ 곡νλ€.
## μ GleamμΈκ°?
- **μ μ νμ
μμ μ±** β Gleamμ κ°λ ₯ν νμ
μμ€ν
μΌλ‘ λ°νμ μλ¬λ₯Ό μ»΄νμΌ νμμ λ°©μ§
- **λΆλ³ λ°μ΄ν°** β μμΈ‘ κ°λ₯ν μν κ΄λ¦¬
- **JavaScript νκ² μ§μ** β `gleam build --target javascript`λ‘ ES λͺ¨λ μΆλ ₯
- **glendix ν¨ν€μ§** β React + Mendix APIμ νμ
μμ ν Gleam λ°μΈλ©. `EditableValue`, `ActionValue`, `ListValue` λ± λͺ¨λ Mendix Pluggable Widget APIλ₯Ό μ§μ
## μν€ν
μ²
```
src/
mendix_widget_gleam.gleam # μμ ― λ©μΈ λͺ¨λ
editor_config.gleam # Studio Pro μμ± ν¨λ μ€μ
editor_preview.gleam # Studio Pro λμμΈ λ·° 미리보기
components/
hello_world.gleam # Hello World 곡μ μ»΄ν¬λνΈ
MendixWidget.xml # μμ ― μμ± μ μ
package.xml # Mendix ν¨ν€μ§ λ§€λνμ€νΈ
widgets/ # .mpk μμ ― νμΌ (glendix/widgetλ‘ λ°μΈλ©)
bindings.json # μΈλΆ React μ»΄ν¬λνΈ λ°μΈλ© μ€μ
package.json # npm μμ‘΄μ± (React, μΈλΆ λΌμ΄λΈλ¬λ¦¬ λ±)
gleam.toml # glendix >= 2.0.4 μμ‘΄μ± ν¬ν¨
```
React/Mendix FFI λ°μΈλ©μ μ΄ νλ‘μ νΈμ ν¬ν¨λμ§ μμΌλ©°, [glendix](https://hexdocs.pm/glendix/) Hex ν¨ν€μ§λ‘ μ 곡λλ€.
### λΉλ νμ΄νλΌμΈ
```
μμ ― μ½λ (.gleam) + glendix ν¨ν€μ§ (Hex)
β gleam run -m glendix/build (Gleam μ»΄νμΌ μλ μν)
ES λͺ¨λ (.mjs) β build/dev/javascript/...
β λΈλ¦Ώμ§ JS (μλ μμ±)κ° import
β Rollup (pluggable-widgets-tools)
.mpk μμ ― ν¨ν€μ§ β dist/
```
### ν΅μ¬ μ리
Gleam ν¨μ `fn(JsProps) -> ReactElement`λ React ν¨μν μ»΄ν¬λνΈμ λμΌν μκ·Έλμ²λ€. glendixκ° React μμ ν¨μμ Mendix λ°νμ νμ
μ κ·Όμλ₯Ό νμ
μμ νκ² μ 곡νλ―λ‘, μμ ― νλ‘μ νΈμμλ λΉμ¦λμ€ λ‘μ§μλ§ μ§μ€νλ©΄ λλ€.
```gleam
// src/mendix_widget_gleam.gleam
import glendix/mendix
import glendix/react.{type JsProps, type ReactElement}
import glendix/react/attribute
import glendix/react/html
pub fn widget(props: JsProps) -> ReactElement {
let sample_text = mendix.get_string_prop(props, "sampleText")
html.div([attribute.class("widget-hello-world")], [
react.text("Hello " <> sample_text),
])
}
```
Mendix λ³΅ν© νμ
λ Gleamμμ νμ
μμ νκ² μ¬μ©ν μ μλ€:
```gleam
import glendix/mendix
import glendix/mendix/editable_value
import glendix/mendix/action
pub fn widget(props: JsProps) -> ReactElement {
// EditableValue μ κ·Ό
let name_attr: EditableValue = mendix.get_prop_required(props, "name")
let display = editable_value.display_value(name_attr)
// ActionValue μ€ν
let on_save: Option(ActionValue) = mendix.get_prop(props, "onSave")
action.execute_action(on_save)
// ...
}
```
## μμνκΈ°
### μ¬μ μꡬμ¬ν
- [Gleam](https://gleam.run/getting-started/installing/) (v1.0+)
- [Node.js](https://nodejs.org/) (v16+)
- [Mendix Studio Pro](https://marketplace.mendix.com/link/studiopro/) (μμ ― ν
μ€νΈμ©)
### μ€μΉ
```bash
gleam run -m glendix/install # Gleam μμ‘΄μ± μλ λ€μ΄λ‘λ + npm μμ‘΄μ± μ€μΉ + λ°μΈλ© μ½λ μμ± (μΈλΆ React ν¨ν€μ§λ μ¬μ μ μλ μ€μΉ νμ)
```
### λΉλ
```bash
gleam run -m glendix/build # Gleam μ»΄νμΌ + μμ ― λΉλ (.mpk μμ±)
```
λΉλ κ²°κ³Όλ¬Όμ `dist/` λλ ν 리μ `.mpk` νμΌλ‘ μμ±λλ€.
### κ°λ°
```bash
gleam run -m glendix/dev # Gleam μ»΄νμΌ + κ°λ° μλ² (HMR, port 3000)
gleam run -m glendix/start # Mendix ν
μ€νΈ νλ‘μ νΈμ μ°λ κ°λ°
```
## λͺ
λ Ήμ΄ λͺ¨μ
λͺ¨λ λͺ
λ Ήμ΄λ `gleam`μΌλ‘ ν΅μΌ. `gleam run -m`μ Gleam μ»΄νμΌμ μλ μνν λ€ μ€ν¬λ¦½νΈλ₯Ό μ€ννλ€.
| λͺ
λ Ήμ΄ | μ€λͺ
|
|--------|------|
| `gleam run -m glendix/install` | μμ‘΄μ± μ€μΉ (Gleam + npm) + λ°μΈλ© μ½λ μμ± |
| `gleam run -m glendix/build` | νλ‘λμ
λΉλ (.mpk μμ±) |
| `gleam run -m glendix/dev` | κ°λ° μλ² (HMR, port 3000) |
| `gleam run -m glendix/start` | Mendix ν
μ€νΈ νλ‘μ νΈ μ°λ |
| `gleam run -m glendix/lint` | ESLint μ€ν |
| `gleam run -m glendix/lint_fix` | ESLint μλ μμ |
| `gleam run -m glendix/release` | λ¦΄λ¦¬μ¦ λΉλ |
| `gleam run -m glendix/marketplace` | Mendix Marketplace μμ ― κ²μ/λ€μ΄λ‘λ |
| `gleam build --target javascript` | Gleam β JS μ»΄νμΌλ§ |
| `gleam test` | Gleam ν
μ€νΈ μ€ν |
| `gleam format` | Gleam μ½λ ν¬λ§·ν
|
## μΈλΆ React μ»΄ν¬λνΈ μ¬μ©
npm ν¨ν€μ§λ‘ μ 곡λλ React μ»΄ν¬λνΈ λΌμ΄λΈλ¬λ¦¬λ₯Ό `.mjs` FFI νμΌ μμ± μμ΄ μμ Gleamμμ μ¬μ©ν μ μλ€.
### 1λ¨κ³: npm ν¨ν€μ§ μ€μΉ
```bash
npm install recharts
```
### 2λ¨κ³: `bindings.json` μμ±
νλ‘μ νΈ λ£¨νΈμ `bindings.json`μ μμ±νκ³ , μ¬μ©ν μ»΄ν¬λνΈλ₯Ό λ±λ‘νλ€:
```json
{
"recharts": {
"components": ["PieChart", "Pie", "Cell", "Tooltip", "ResponsiveContainer"]
}
}
```
### 3λ¨κ³: λ°μΈλ© μμ±
```bash
gleam run -m glendix/install
```
`binding_ffi.mjs`κ° μλ μμ±λλ€. μ΄ν `gleam run -m glendix/build` λ± λΉλ μμλ μλ κ°±μ λλ€.
### 4λ¨κ³: Gleamμμ μ¬μ©
```gleam
import glendix/binding
import glendix/react.{type ReactElement}
import glendix/react/attribute.{type Attribute}
fn m() { binding.module("recharts") }
pub fn pie_chart(attrs: List(Attribute), children: List(ReactElement)) -> ReactElement {
react.component_el(binding.resolve(m(), "PieChart"), attrs, children)
}
pub fn tooltip(attrs: List(Attribute)) -> ReactElement {
react.void_component_el(binding.resolve(m(), "Tooltip"), attrs)
}
```
`html.div`μ λμΌν νΈμΆ ν¨ν΄μΌλ‘ μΈλΆ React μ»΄ν¬λνΈλ₯Ό μ¬μ©ν μ μλ€.
## Mendix Marketplace μμ ― λ€μ΄λ‘λ
Mendix Marketplaceμμ μμ ―(.mpk)μ μΈν°λν°λΈνκ² κ²μνκ³ λ€μ΄λ‘λν μ μλ€. λ€μ΄λ‘λ μλ£ ν λ°μΈλ© `.gleam` νμΌμ΄ μλ μμ±λμ΄ λ°λ‘ μ¬μ© κ°λ₯νλ€.
### μ¬μ μ€λΉ
`.env` νμΌμ Mendix Personal Access Tokenμ μ€μ νλ€:
```
MENDIX_PAT=your_personal_access_token
```
> PATλ [Mendix Developer Settings](https://user-settings.mendix.com/link/developersettings)μμ **Personal Access Tokens** μΉμ
μ **New Token**μ ν΄λ¦νμ¬ λ°κΈ. νμν scope: `mx:marketplace-content:read`
### μ€ν
```bash
gleam run -m glendix/marketplace
```
μΈν°λν°λΈ TUIμμ μμ ―μ κ²μ/μ ννλ©΄ `widgets/` λλ ν 리μ `.mpk`κ° λ€μ΄λ‘λλκ³ , `src/widgets/`μ λ°μΈλ© `.gleam` νμΌμ΄ μλ μμ±λλ€.
## .mpk μμ ― μ»΄ν¬λνΈ μ¬μ©
`widgets/` λλ ν 리μ `.mpk` νμΌ(Mendix μμ ― λΉλ κ²°κ³Όλ¬Ό)μ λ°°μΉνλ©΄, λ€λ₯Έ μμ ― μμμ κΈ°μ‘΄ Mendix μμ ―μ React μ»΄ν¬λνΈλ‘ λ λλ§ν μ μλ€.
### 1λ¨κ³: `.mpk` νμΌ λ°°μΉ
```
νλ‘μ νΈ λ£¨νΈ/
βββ widgets/
β βββ Switch.mpk
β βββ Badge.mpk
βββ src/
βββ gleam.toml
```
### 2λ¨κ³: λ°μΈλ© μμ±
```bash
gleam run -m glendix/install
```
μ€ν μ λ€μμ΄ μλ μ²λ¦¬λλ€:
- `.mpk`μμ `.mjs`/`.css`λ₯Ό μΆμΆνκ³ `widget_ffi.mjs`κ° μμ±λλ€
- `.mpk` XMLμ `<property>` μ μλ₯Ό νμ±νμ¬ `src/widgets/`μ λ°μΈλ© `.gleam` νμΌμ΄ μλ μμ±λλ€ (μ΄λ―Έ μ‘΄μ¬νλ©΄ 건λλ)
### 3λ¨κ³: μλ μμ±λ `src/widgets/*.gleam` νμΌ νμΈ
```gleam
// src/widgets/switch.gleam (μλ μμ±)
import glendix/mendix
import glendix/react.{type JsProps, type ReactElement}
import glendix/react/attribute
import glendix/widget
/// Switch μμ ― λ λλ§ - propsμμ μμ±μ μ½μ΄ μμ ―μ μ λ¬
pub fn render(props: JsProps) -> ReactElement {
let boolean_attribute = mendix.get_prop_required(props, "booleanAttribute")
let action = mendix.get_prop_required(props, "action")
let comp = widget.component("Switch")
react.component_el(
comp,
[
attribute.attribute("booleanAttribute", boolean_attribute),
attribute.attribute("action", action),
],
[],
)
}
```
required/optional μμ±μ΄ μλ ꡬλΆλλ©°, νμμ λ°λΌ μμ±λ νμΌμ μμ λ‘κ² μμ ν μ μλ€.
### 4λ¨κ³: μμ ―μμ μ¬μ©
```gleam
import widgets/switch
// μ»΄ν¬λνΈ λ΄λΆμμ
switch.render(props)
```
μμ ― μ΄λ¦μ `.mpk` λ΄λΆ XMLμ `<name>` κ°μ, property keyλ `.mpk` XMLμ μλ³Έ keyλ₯Ό κ·Έλλ‘ μ¬μ©νλ€.
## κΈ°μ μ€ν
- **Gleam** β μμ ― λ‘μ§, UI (JavaScript νκ² μ»΄νμΌ)
- **[glendix](https://hexdocs.pm/glendix/)** β React + Mendix API Gleam λ°μΈλ© (Hex ν¨ν€μ§)
- **React 19** β Mendix Pluggable Widget λ°νμ
- **Rollup** β `@mendix/pluggable-widgets-tools` κΈ°λ° λ²λ€λ§
## μ μ½μ¬ν
- Gleam β JS β Mendix Widget νμ΄νλΌμΈμ 곡μ μ§μλμ§ μλ μ‘°ν©μ΄λ―λ‘ λΉλ μ€μ 컀μ€ν°λ§μ΄μ§μ΄ νμν μ μλ€
- JSX νμΌμ μ¬μ©νμ§ μλλ€ β λͺ¨λ React λ‘μ§μ Gleam + glendixλ‘ κ΅¬ν
- Redraw λ± μΈλΆ Gleam React λΌμ΄λΈλ¬λ¦¬λ μ¬μ©νμ§ μλλ€
- μμ ― νλ‘μ νΈμ FFI νμΌμ μ§μ μμ±νμ§ μλλ€ β React/Mendix FFIλ glendixκ° μ 곡
## λΌμ΄μ μ€
Apache License 2.0 β [LICENSE](./LICENSE) μ°Έμ‘°