ember-tribe 2.6.7 → 2.6.9
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 +544 -274
- package/blueprints/ember-tribe/files/app/components/storylang/arc-diagram.hbs +242 -0
- package/blueprints/ember-tribe/files/app/components/storylang/arc-diagram.js +432 -0
- package/blueprints/ember-tribe/files/app/components/storylang/index.hbs +457 -0
- package/blueprints/ember-tribe/files/app/components/storylang/index.js +177 -0
- package/blueprints/ember-tribe/files/app/components/storylang/node-detail.hbs +265 -0
- package/blueprints/ember-tribe/files/app/components/storylang/node-detail.js +91 -0
- package/blueprints/ember-tribe/files/app/index.html +2 -2
- package/blueprints/ember-tribe/files/app/templates/index.hbs +4 -4
- package/blueprints/ember-tribe/files/storylang +492 -75
- package/package.json +1 -1
- package/blueprints/ember-tribe/files/app/components/welcome-flame.hbs +0 -5
- package/blueprints/ember-tribe/files/public/assets/css/custom.css +0 -0
- package/blueprints/ember-tribe/files/public/assets/js/custom.js +0 -0
package/README.md
CHANGED
|
@@ -4,6 +4,46 @@ An addon that connects EmberJS to Tribe API, bridging the gap between backend da
|
|
|
4
4
|
|
|
5
5
|
Tribe is a project management framework built for ease of collaboration - https://github.com/tribe-framework/tribe
|
|
6
6
|
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## Table of Contents
|
|
10
|
+
|
|
11
|
+
- [Installation and Setup](#installation-and-setup)
|
|
12
|
+
- [Folder Structure](#folder-structure)
|
|
13
|
+
- [Best Practices for AI Generated Code](#best-practices-for-ai-generated-code)
|
|
14
|
+
- [Code Rules](#code-rules)
|
|
15
|
+
- [Storylang Rules](#storylang-rules)
|
|
16
|
+
- [Types](#types)
|
|
17
|
+
- [Routes](#routes)
|
|
18
|
+
- [Controllers](#controllers)
|
|
19
|
+
- [Helpers](#helpers)
|
|
20
|
+
- [Modifiers](#modifiers)
|
|
21
|
+
- [Services](#services)
|
|
22
|
+
- [Components](#components)
|
|
23
|
+
- [Storylang](#storylang)
|
|
24
|
+
- [Storylang CLI](#storylang-cli)
|
|
25
|
+
- [Storylang.json Documentation](#storylangjson-documentation)
|
|
26
|
+
- [Types](#1-types)
|
|
27
|
+
- [Routes](#2-routes)
|
|
28
|
+
- [Helpers](#3-helpers)
|
|
29
|
+
- [Modifiers](#4-modifiers)
|
|
30
|
+
- [Services](#5-services)
|
|
31
|
+
- [Components](#6-components)
|
|
32
|
+
- [Data Types Reference](#data-types-reference)
|
|
33
|
+
- [Integration with Other Files](#integration-with-other-files)
|
|
34
|
+
- [EmberJS](#emberjs)
|
|
35
|
+
- [Ember-Tribe Development Guide](#ember-tribe-development-guide)
|
|
36
|
+
- [EmberData Integration](#emberdata-integration)
|
|
37
|
+
- [Route Generation](#route-generation)
|
|
38
|
+
- [Helper System](#helper-system)
|
|
39
|
+
- [Modifier System](#modifier-system)
|
|
40
|
+
- [Services Integration](#services-integration)
|
|
41
|
+
- [Component Architecture](#component-architecture)
|
|
42
|
+
- [Forms and Input Fields](#forms-and-input-fields)
|
|
43
|
+
- [Deploying to Junction (Self-Hosted)](#deploying-to-junction-self-hosted)
|
|
44
|
+
|
|
45
|
+
---
|
|
46
|
+
|
|
7
47
|
## Installation and Setup
|
|
8
48
|
|
|
9
49
|
### Prerequisites
|
|
@@ -26,11 +66,22 @@ The addon automatically configures following essential packages:
|
|
|
26
66
|
|
|
27
67
|
**Ember Addons:** `ember-cli-dotenv`, `ember-cli-sass`, `ember-modifier`, `ember-composable-helpers`, `ember-truth-helpers`, `ember-file-upload` , `ember-power-select`
|
|
28
68
|
|
|
29
|
-
**NPM Packages:** `bootstrap`, `@popperjs/core`, `animate.css`, `video.js`, `swiper`, `howler`
|
|
69
|
+
**NPM Packages:** `bootstrap`, `@popperjs/core`, `animate.css`, `video.js`, `swiper`, `howler`, `sortablejs`, `papaparse`
|
|
70
|
+
|
|
71
|
+
**Built-in features that can be used in routes and components**:
|
|
72
|
+
|
|
73
|
+
- **Layout**: `table`, `figure`, `accordion`, `card`, `list-group`, `navbar`, `nav`, `tab`, `breadcrumb`
|
|
74
|
+
- **Interactive**: `button`, `button-group`, `dropdown`, `modal`, `collapse`, `offcanvas`, `pagination`, `popover`, `tooltip`, `swiper-carousel`, `videojs-player`, `howlerjs-player`, `input-field`, `input-group`, `textarea`, `checkbox`, `radio`, `range`, `select`, `multi-select`, `date`, `file-uploader`, `alert`, `badge`, `toast`, `placeholder`, `progress`, `spinner`, `scrollspy`
|
|
75
|
+
|
|
76
|
+
**Preinstalled services in ember-tribe**:
|
|
77
|
+
|
|
78
|
+
- `store`: Ember Data store for CRUD operations
|
|
79
|
+
- `router`: Ember router service for navigation
|
|
80
|
+
- `types`: Automatic model generation from backend tracks
|
|
30
81
|
|
|
31
82
|
---
|
|
32
83
|
|
|
33
|
-
##
|
|
84
|
+
## Folder Structure
|
|
34
85
|
|
|
35
86
|
```
|
|
36
87
|
app/
|
|
@@ -50,11 +101,125 @@ installer.sh
|
|
|
50
101
|
|
|
51
102
|
---
|
|
52
103
|
|
|
53
|
-
##
|
|
104
|
+
## Best Practices for AI generated code
|
|
105
|
+
|
|
106
|
+
These rules are **mandatory** for all Tribe-compatible code. Follow them strictly and do not deviate unless explicitly instructed.
|
|
107
|
+
|
|
108
|
+
### Code Rules
|
|
109
|
+
|
|
110
|
+
1. **EmberJS 6.x Compatibility — Strictly Required**
|
|
111
|
+
All generated code must be strictly compatible with EmberJS 6.x.
|
|
112
|
+
|
|
113
|
+
2. **Bootstrap 5.x — Required Foundation**
|
|
114
|
+
Use Bootstrap 5.x as the sole design system for all layout, spacing, and responsive behaviour. Do not introduce custom CSS frameworks or utility libraries that conflict with Bootstrap. Follow Bootstrap conventions strictly.
|
|
115
|
+
|
|
116
|
+
3. **Backend Field Access**
|
|
117
|
+
Always access backend fields through the `modules` object — e.g. `object.modules.field_name`. Never access backend fields directly.
|
|
118
|
+
|
|
119
|
+
4. **npm Packages over Ember Addons**
|
|
120
|
+
When an npm package and an Ember addon offer equivalent functionality, always prefer the npm package for better long-term compatibility.
|
|
121
|
+
|
|
122
|
+
5. **Icons — FontAwesome 6.x Only**
|
|
123
|
+
Use FontAwesome 6.x for all icons. Do not use any other icon library unless the project description explicitly specifies one.
|
|
124
|
+
|
|
125
|
+
6. **Animations — Subtle and Purposeful**
|
|
126
|
+
If animations are needed, use `animate.css`. Keep animations subtle — prefer fades and minimal slides. Avoid anything that feels flashy or distracting.
|
|
127
|
+
|
|
128
|
+
7. **EmberData Caching**
|
|
129
|
+
When data has already been loaded into the store, retrieve it with `peekRecord` instead of making a new network request.
|
|
130
|
+
|
|
131
|
+
8. **Backend Filtering over Frontend Filtering**
|
|
132
|
+
For sorting and filtering data, always use `this.store.query` with backend query parameters. Do not filter or sort arrays on the frontend when the backend can do it.
|
|
133
|
+
|
|
134
|
+
---
|
|
135
|
+
|
|
136
|
+
### Storylang Rules
|
|
137
|
+
|
|
138
|
+
Follow this strict order of thinking when designing any feature:
|
|
139
|
+
|
|
140
|
+
> **Understand Types → Routes → Controllers → Helpers → Modifiers → Services → Components**
|
|
141
|
+
|
|
142
|
+
Always begin by understanding your data types, then define the routes that load that data, then wire up controllers to handle user actions, then extract reusable template logic into helpers, then isolate DOM behaviour into modifiers, then move app-wide logic into services, and finally — only when the project's scale warrants it — extract repeatable UI into components.
|
|
143
|
+
|
|
144
|
+
---
|
|
145
|
+
|
|
146
|
+
**Types**
|
|
147
|
+
|
|
148
|
+
8. **Start by Understanding Your Data**
|
|
149
|
+
Before writing any code, read the project description and `types.json` to understand the data model. Every architectural decision that follows — which routes to create, which services to build, whether components are even needed — depends on a clear understanding of the underlying types.
|
|
150
|
+
|
|
151
|
+
---
|
|
152
|
+
|
|
153
|
+
**Routes**
|
|
154
|
+
|
|
155
|
+
9. **Route Naming**
|
|
156
|
+
Match route names to user mental models. Use consistent, predictable naming conventions so that routes are self-documenting.
|
|
157
|
+
|
|
158
|
+
10. **Routes Are for Fetching, Not Logic**
|
|
159
|
+
Routes should primarily perform read/fetch operations and pass data down to components or services. Keep JavaScript in routes to a minimum — business logic belongs in components and services, not routes.
|
|
160
|
+
|
|
161
|
+
11. **Route Parameters**
|
|
162
|
+
Keep `get_vars` minimal and meaningful. Load only the data types that each specific route actually needs — avoid over-fetching.
|
|
163
|
+
|
|
164
|
+
---
|
|
165
|
+
|
|
166
|
+
**Controllers**
|
|
167
|
+
|
|
168
|
+
12. **Controllers Bridge Routes and Templates**
|
|
169
|
+
Controllers sit between routes and templates, handling query parameters, user actions, and transient UI state that belongs to a specific route. Keep controllers focused — they are not a place for business logic or data fetching.
|
|
170
|
+
|
|
171
|
+
13. **Keep Controllers Thin**
|
|
172
|
+
Delegate complex logic to services. A controller should primarily expose tracked properties and actions that the corresponding template needs directly.
|
|
173
|
+
|
|
174
|
+
---
|
|
175
|
+
|
|
176
|
+
**Helpers**
|
|
177
|
+
|
|
178
|
+
14. **Helpers Must Be Pure and Stateless**
|
|
179
|
+
A helper receives input and returns output — nothing else. Helpers must have no side effects and must not interact with the store, services, or DOM.
|
|
180
|
+
|
|
181
|
+
---
|
|
182
|
+
|
|
183
|
+
**Modifiers**
|
|
184
|
+
|
|
185
|
+
15. **Modifiers Own All DOM Interaction**
|
|
186
|
+
Any direct DOM manipulation or third-party library initialisation must live in a modifier.
|
|
187
|
+
|
|
188
|
+
---
|
|
189
|
+
|
|
190
|
+
**Services**
|
|
191
|
+
|
|
192
|
+
16. **Services Are the Core Logic Layer**
|
|
193
|
+
Services hold the primary business logic of the application. They interact with both routes and components and are the single source of truth for app-wide behaviour.
|
|
194
|
+
|
|
195
|
+
17. **Keep Services Stateless When Possible**
|
|
196
|
+
Avoid storing transient state in services. Where services must depend on one another, use dependency injection.
|
|
197
|
+
|
|
198
|
+
18. **All Backend Calls Must Live Inside `@action` Functions**
|
|
199
|
+
Any call to a `/custom/*.php` script must be made from within an `@action` function. This rule makes backend interactions explicit, traceable, and user-initiated by design.
|
|
200
|
+
|
|
201
|
+
19. **Plain Methods Belong in `functions`, Not `actions`**
|
|
202
|
+
Internal methods, which carry no special Ember semantics, should be traced automatically under the `functions` key in `storylang.json`. Do not decorate them with `@action`. Keeping `actions` reserved for user-facing interactions makes the intent of each method immediately clear to anyone reading the spec.
|
|
203
|
+
|
|
204
|
+
20. **Getters Are Derived Properties — Not Actions or Functions**
|
|
205
|
+
Native JavaScript `get` accessors (e.g. `get trustScoreColor()`) derive a display value, CSS class, or conditional flag from `this.args` or other state. They carry no side effects and require no `@action` decorator. Storylang captures them under the `getters` key, separate from `actions` and `functions`, so the distinction between user-facing interactions, internal logic, and pure derived state is always explicit in the spec.
|
|
206
|
+
|
|
207
|
+
---
|
|
208
|
+
|
|
209
|
+
**Components**
|
|
210
|
+
|
|
211
|
+
21. **Components are not always required**
|
|
212
|
+
Before creating components, assess the scale of the project from its description. On small projects, fewer files means higher code readability — collapsing template logic directly into route templates is often the right call. On larger projects, the opposite is true: extracting repeatable UI into named components improves clarity, maintainability, and testability. Make this decision deliberately at the start, not as an afterthought.
|
|
213
|
+
|
|
214
|
+
---
|
|
215
|
+
|
|
216
|
+
## Storylang
|
|
217
|
+
|
|
218
|
+
### Storylang CLI
|
|
54
219
|
|
|
55
220
|
ember-tribe ships with a command-line tool called `storylang` that synchronises the `config/storylang.json` specification with the actual Ember project files.
|
|
56
221
|
|
|
57
|
-
|
|
222
|
+
#### Usage
|
|
58
223
|
|
|
59
224
|
```bash
|
|
60
225
|
node storylang
|
|
@@ -71,58 +236,44 @@ node storylang
|
|
|
71
236
|
# => config/storylang.json updated from project files
|
|
72
237
|
```
|
|
73
238
|
|
|
74
|
-
|
|
239
|
+
#### Typical Workflow
|
|
75
240
|
|
|
76
241
|
Run `node storylang` periodically to keep `config/storylang.json` in sync as the project evolves.
|
|
77
242
|
|
|
78
243
|
---
|
|
79
244
|
|
|
80
|
-
|
|
245
|
+
### Storylang.json Documentation
|
|
81
246
|
|
|
82
|
-
|
|
247
|
+
#### Overview
|
|
83
248
|
|
|
84
249
|
Storylang.json is a structured configuration file used in the ember-tribe ecosystem to define the frontend implementation of your application. It is found at `config/storylang.json`. It works in conjunction with your `types.json` (which defines your data types) to create a complete frontend specification.
|
|
85
250
|
|
|
86
|
-
|
|
251
|
+
#### Purpose
|
|
87
252
|
|
|
88
253
|
The storylang.json file serves as a blueprint for frontend developers to understand:
|
|
89
254
|
|
|
90
255
|
- What routes, components, services, helpers, modifiers and types are required
|
|
91
256
|
- How data flows through the application
|
|
92
257
|
|
|
93
|
-
|
|
258
|
+
#### File Structure
|
|
94
259
|
|
|
95
260
|
The storylang.json file contains seven main sections:
|
|
96
261
|
|
|
97
262
|
```json
|
|
98
263
|
{
|
|
99
|
-
"implementation_approach": "...",
|
|
100
264
|
"types": [...],
|
|
101
|
-
"components": [...],
|
|
102
265
|
"routes": [...],
|
|
103
|
-
"services": [...],
|
|
104
266
|
"helpers": [...],
|
|
105
|
-
"modifiers": [...]
|
|
267
|
+
"modifiers": [...],
|
|
268
|
+
"services": [...],
|
|
269
|
+
"components": [...]
|
|
106
270
|
}
|
|
107
271
|
```
|
|
108
272
|
|
|
109
|
-
### Section Definitions
|
|
110
273
|
|
|
111
|
-
|
|
274
|
+
#### Section Definitions
|
|
112
275
|
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
**Format**:
|
|
116
|
-
|
|
117
|
-
```json
|
|
118
|
-
{
|
|
119
|
-
"implementation_approach": "Two-paragraph description explaining technical approach and key functionality."
|
|
120
|
-
}
|
|
121
|
-
```
|
|
122
|
-
|
|
123
|
-
---
|
|
124
|
-
|
|
125
|
-
### 2. Types
|
|
276
|
+
#### 1. Types
|
|
126
277
|
|
|
127
278
|
**Purpose**: Declares which data types from `types.json` and maps them to the components, routes, services, helpers and modifiers that consume them. This creates a traceable link between your data layer and your UI implementation.
|
|
128
279
|
|
|
@@ -147,77 +298,72 @@ The storylang.json file contains seven main sections:
|
|
|
147
298
|
|
|
148
299
|
---
|
|
149
300
|
|
|
150
|
-
|
|
301
|
+
#### 2. Routes
|
|
151
302
|
|
|
152
|
-
**Purpose**: Defines
|
|
303
|
+
**Purpose**: Defines the application's routes and their requirements.
|
|
304
|
+
|
|
305
|
+
> **Note on controllers**: In `storylang.json`, controllers are not listed as a separate top-level section. Instead, each controller is considered part of its corresponding route — just as a component's backing JavaScript class is part of its component entry. Controller actions, tracked variables, and query parameters should be specified within the route definition they belong to.
|
|
153
306
|
|
|
154
307
|
**Format**:
|
|
155
308
|
|
|
156
309
|
```json
|
|
157
310
|
{
|
|
158
|
-
"
|
|
311
|
+
"routes": [
|
|
159
312
|
{
|
|
160
|
-
"name": "
|
|
161
|
-
"type": "component-type",
|
|
313
|
+
"name": "route-name", //should match Ember router.js
|
|
162
314
|
"tracked_vars": [{ "<variableName>": "<dataType>" }],
|
|
163
|
-
"
|
|
164
|
-
"
|
|
165
|
-
"
|
|
166
|
-
"
|
|
167
|
-
"
|
|
315
|
+
"get_vars": [{ "<paramName>": "<dataType>" }],
|
|
316
|
+
"getters": ["derived-property-name"],
|
|
317
|
+
"actions": ["frontend-action", { "backend-action": ["custom/path/file.php"] }],
|
|
318
|
+
"functions": ["helper-method", "compute-something"],
|
|
319
|
+
"helpers": ["helper1"],
|
|
320
|
+
"services": ["service1"],
|
|
321
|
+
"components": ["component1", "component2"],
|
|
322
|
+
"types": ["type1", "type2"]
|
|
168
323
|
}
|
|
169
324
|
]
|
|
170
325
|
}
|
|
171
326
|
```
|
|
172
327
|
|
|
173
|
-
**
|
|
328
|
+
> **Actions format**: `actions` is a mixed array. Pure frontend actions are plain strings. Actions that call one or more `/custom/*.php` scripts are objects whose key is the action name and whose value is the list of PHP paths called — e.g. `{ "save-record": ["custom/records/save.php"] }`.
|
|
174
329
|
|
|
175
|
-
|
|
176
|
-
- **Interactive**: `button`, `button-group`, `dropdown`, `modal`, `collapse`, `offcanvas`, `pagination`, `popover`, `tooltip`, `swiper-carousel`, `videojs-player`, `howlerjs-player`, `input-field`, `input-group`, `textarea`, `checkbox`, `radio`, `range`, `select`, `multi-select`, `date`, `file-uploader`, `alert`, `badge`, `toast`, `placeholder`, `progress`, `spinner`, `scrollspy`
|
|
330
|
+
---
|
|
177
331
|
|
|
178
|
-
|
|
332
|
+
#### 3. Helpers
|
|
333
|
+
|
|
334
|
+
**Purpose**: Defines custom template helpers — pure functions used in templates to format, compute or transform data for display.
|
|
335
|
+
|
|
336
|
+
**Format**:
|
|
179
337
|
|
|
180
338
|
```json
|
|
181
339
|
{
|
|
182
|
-
"
|
|
340
|
+
"helpers": [
|
|
183
341
|
{
|
|
184
|
-
"name": "
|
|
185
|
-
"
|
|
186
|
-
"
|
|
187
|
-
"
|
|
188
|
-
{ "file": "var" },
|
|
189
|
-
{ "onEdit": "action" },
|
|
190
|
-
{ "onDelete": "action" }
|
|
191
|
-
],
|
|
192
|
-
"actions": ["toggleSelection", "expandDetails", "editFile", "deleteFile"],
|
|
193
|
-
"helpers": ["formatDate", "truncateText"],
|
|
194
|
-
"modifiers": ["tooltip"],
|
|
195
|
-
"services": ["store", "router"]
|
|
342
|
+
"name": "helper-name",
|
|
343
|
+
"description": "What this helper does",
|
|
344
|
+
"input_args": [{ "<argumentName>": "<dataType>" }],
|
|
345
|
+
"return": "<dataType>"
|
|
196
346
|
}
|
|
197
347
|
]
|
|
198
348
|
}
|
|
199
349
|
```
|
|
200
350
|
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
### 4. Routes
|
|
204
|
-
|
|
205
|
-
**Purpose**: Defines the application's routes and their requirements.
|
|
206
|
-
|
|
207
|
-
**Format**:
|
|
351
|
+
**Example**:
|
|
208
352
|
|
|
209
353
|
```json
|
|
210
354
|
{
|
|
211
|
-
"
|
|
355
|
+
"helpers": [
|
|
212
356
|
{
|
|
213
|
-
"name": "
|
|
214
|
-
"
|
|
215
|
-
"
|
|
216
|
-
"
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
"
|
|
220
|
-
"
|
|
357
|
+
"name": "format-date",
|
|
358
|
+
"description": "Formats a raw ISO date string into a human-readable date",
|
|
359
|
+
"input_args": [{ "isoString": "string" }, { "format": "string" }],
|
|
360
|
+
"return": "string"
|
|
361
|
+
},
|
|
362
|
+
{
|
|
363
|
+
"name": "truncate-text",
|
|
364
|
+
"description": "Truncates a string to a given character limit and appends an ellipsis",
|
|
365
|
+
"input_args": [{ "text": "string" }, { "limit": "int" }],
|
|
366
|
+
"return": "string"
|
|
221
367
|
}
|
|
222
368
|
]
|
|
223
369
|
}
|
|
@@ -225,56 +371,41 @@ The storylang.json file contains seven main sections:
|
|
|
225
371
|
|
|
226
372
|
---
|
|
227
373
|
|
|
228
|
-
|
|
374
|
+
#### 4. Modifiers
|
|
229
375
|
|
|
230
|
-
**Purpose**: Defines custom Ember
|
|
376
|
+
**Purpose**: Defines custom Ember modifiers — functions that directly interact with DOM elements to attach behaviour, third-party libraries or event listeners.
|
|
231
377
|
|
|
232
378
|
**Format**:
|
|
233
379
|
|
|
234
380
|
```json
|
|
235
381
|
{
|
|
236
|
-
"
|
|
382
|
+
"modifiers": [
|
|
237
383
|
{
|
|
238
|
-
"name": "
|
|
239
|
-
"
|
|
240
|
-
"
|
|
241
|
-
"
|
|
242
|
-
"services": ["dependency1", "dependency2"]
|
|
384
|
+
"name": "modifier-name",
|
|
385
|
+
"description": "What DOM behaviour this modifier applies",
|
|
386
|
+
"input_args": [{ "<argumentName>": "<dataType>" }],
|
|
387
|
+
"services": ["service1"]
|
|
243
388
|
}
|
|
244
389
|
]
|
|
245
390
|
}
|
|
246
391
|
```
|
|
247
392
|
|
|
248
|
-
**Built-in Services in ember-tribe**:
|
|
249
|
-
|
|
250
|
-
- `store`: Ember Data store for CRUD operations
|
|
251
|
-
- `router`: Ember router service for navigation
|
|
252
|
-
- `types`: Automatic model generation from backend tracks
|
|
253
|
-
- `bootstrap`: Bootstrap CSS framework with Popper.js
|
|
254
|
-
- `papaparse`: CSV parsing library
|
|
255
|
-
- `sortablejs`: Drag-and-drop sorting
|
|
256
|
-
- `swiperjs`: Touch slider/carousel
|
|
257
|
-
- `videojs`: Video player
|
|
258
|
-
- `howlerjs`: Audio player
|
|
259
|
-
|
|
260
393
|
**Example**:
|
|
261
394
|
|
|
262
395
|
```json
|
|
263
396
|
{
|
|
264
|
-
"
|
|
397
|
+
"modifiers": [
|
|
265
398
|
{
|
|
266
|
-
"name": "
|
|
267
|
-
"
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
]
|
|
276
|
-
"helpers": ["validateConfig", "generatePreview"],
|
|
277
|
-
"services": ["store", "router"]
|
|
399
|
+
"name": "tooltip",
|
|
400
|
+
"description": "Initialises a Bootstrap tooltip on the target element using the provided label",
|
|
401
|
+
"input_args": [{ "label": "string" }, { "placement": "string" }],
|
|
402
|
+
"services": []
|
|
403
|
+
},
|
|
404
|
+
{
|
|
405
|
+
"name": "autofocus",
|
|
406
|
+
"description": "Sets focus on the target element when it is inserted into the DOM",
|
|
407
|
+
"input_args": [],
|
|
408
|
+
"services": []
|
|
278
409
|
}
|
|
279
410
|
]
|
|
280
411
|
}
|
|
@@ -282,48 +413,47 @@ The storylang.json file contains seven main sections:
|
|
|
282
413
|
|
|
283
414
|
---
|
|
284
415
|
|
|
285
|
-
|
|
416
|
+
#### 5. Services
|
|
286
417
|
|
|
287
|
-
**Purpose**: Defines custom
|
|
418
|
+
**Purpose**: Defines custom Ember services needed by the application.
|
|
288
419
|
|
|
289
420
|
**Format**:
|
|
290
421
|
|
|
291
422
|
```json
|
|
292
423
|
{
|
|
293
|
-
"
|
|
424
|
+
"services": [
|
|
294
425
|
{
|
|
295
|
-
"name": "
|
|
296
|
-
"
|
|
297
|
-
"
|
|
298
|
-
"
|
|
426
|
+
"name": "service-name",
|
|
427
|
+
"tracked_vars": [{ "<variableName>": "<dataType>" }],
|
|
428
|
+
"getters": ["derived-property-name"],
|
|
429
|
+
"actions": ["frontend-action", { "backend-action": ["custom/path/file.php"] }],
|
|
430
|
+
"functions": ["internal-method"],
|
|
431
|
+
"helpers": ["helper1"],
|
|
432
|
+
"services": ["dependency1", "dependency2"]
|
|
299
433
|
}
|
|
300
434
|
]
|
|
301
435
|
}
|
|
302
436
|
```
|
|
303
437
|
|
|
304
|
-
**Helper Properties**:
|
|
305
|
-
|
|
306
|
-
- `name`: Kebab-case name of the helper
|
|
307
|
-
- `description`: Brief description of what the helper computes or transforms
|
|
308
|
-
- `args`: Input arguments the helper accepts
|
|
309
|
-
- `return`: The data type the helper outputs
|
|
310
|
-
|
|
311
438
|
**Example**:
|
|
312
439
|
|
|
313
440
|
```json
|
|
314
441
|
{
|
|
315
|
-
"
|
|
316
|
-
{
|
|
317
|
-
"name": "format-date",
|
|
318
|
-
"description": "Formats a raw ISO date string into a human-readable date",
|
|
319
|
-
"args": [{ "isoString": "string" }, { "format": "string" }],
|
|
320
|
-
"return": "string"
|
|
321
|
-
},
|
|
442
|
+
"services": [
|
|
322
443
|
{
|
|
323
|
-
"name": "
|
|
324
|
-
"
|
|
325
|
-
|
|
326
|
-
|
|
444
|
+
"name": "visualization-builder",
|
|
445
|
+
"tracked_vars": [
|
|
446
|
+
{ "dataMatrix": "object" },
|
|
447
|
+
{ "availableTypes": "array" }
|
|
448
|
+
],
|
|
449
|
+
"functions": ["build-visualization"],
|
|
450
|
+
"actions": [
|
|
451
|
+
"reset-state",
|
|
452
|
+
{ "assemble-data-for-visualization": ["custom/visualizations/assemble-data.php"] },
|
|
453
|
+
"updateVisualization"
|
|
454
|
+
],
|
|
455
|
+
"helpers": ["validateConfig", "generatePreview"],
|
|
456
|
+
"services": ["store", "router"]
|
|
327
457
|
}
|
|
328
458
|
]
|
|
329
459
|
}
|
|
@@ -331,48 +461,51 @@ The storylang.json file contains seven main sections:
|
|
|
331
461
|
|
|
332
462
|
---
|
|
333
463
|
|
|
334
|
-
|
|
464
|
+
#### 6. Components
|
|
335
465
|
|
|
336
|
-
**Purpose**: Defines
|
|
466
|
+
**Purpose**: Defines reusable UI components that will be built for the application.
|
|
337
467
|
|
|
338
468
|
**Format**:
|
|
339
469
|
|
|
340
470
|
```json
|
|
341
471
|
{
|
|
342
|
-
"
|
|
472
|
+
"components": [
|
|
343
473
|
{
|
|
344
|
-
"name": "
|
|
345
|
-
"
|
|
346
|
-
"
|
|
347
|
-
"
|
|
474
|
+
"name": "component-name",
|
|
475
|
+
"type": "component-type",
|
|
476
|
+
"tracked_vars": [{ "<variableName>": "<dataType>" }],
|
|
477
|
+
"inherited_args": [{ "<argumentName>": "<argType>" }],
|
|
478
|
+
"getters": ["derived-property-name"],
|
|
479
|
+
"actions": ["frontend-action", { "backend-action": ["custom/path/file.php"] }],
|
|
480
|
+
"functions": ["internal-method"],
|
|
481
|
+
"helpers": ["helper1", "helper2"],
|
|
482
|
+
"modifiers": ["modifier1"],
|
|
483
|
+
"services": ["service1", "service2"]
|
|
348
484
|
}
|
|
349
485
|
]
|
|
350
486
|
}
|
|
351
487
|
```
|
|
352
488
|
|
|
353
|
-
**Modifier Properties**:
|
|
354
|
-
|
|
355
|
-
- `name`: Kebab-case name of the modifier
|
|
356
|
-
- `description`: Brief description of the DOM behaviour it attaches
|
|
357
|
-
- `args`: Arguments passed into the modifier from the template
|
|
358
|
-
- `services`: Ember services injected if needed
|
|
359
489
|
|
|
360
490
|
**Example**:
|
|
361
491
|
|
|
362
492
|
```json
|
|
363
493
|
{
|
|
364
|
-
"
|
|
365
|
-
{
|
|
366
|
-
"name": "tooltip",
|
|
367
|
-
"description": "Initialises a Bootstrap tooltip on the target element using the provided label",
|
|
368
|
-
"args": [{ "label": "string" }, { "placement": "string" }],
|
|
369
|
-
"services": []
|
|
370
|
-
},
|
|
494
|
+
"components": [
|
|
371
495
|
{
|
|
372
|
-
"name": "
|
|
373
|
-
"
|
|
374
|
-
"
|
|
375
|
-
"
|
|
496
|
+
"name": "file-summary-card",
|
|
497
|
+
"type": "card",
|
|
498
|
+
"tracked_vars": [{ "isSelected": "bool" }, { "isExpanded": "bool" }],
|
|
499
|
+
"inherited_args": [
|
|
500
|
+
{ "file": "var" },
|
|
501
|
+
{ "onEdit": "action" },
|
|
502
|
+
{ "onDelete": "action" }
|
|
503
|
+
],
|
|
504
|
+
"getters": ["trust-score-color", "display-label"],
|
|
505
|
+
"actions": ["toggle-selection", "expand-details", { "save-file": ["custom/files/save.php"] }, { "delete-file": ["custom/files/delete.php"] }],
|
|
506
|
+
"helpers": ["formatDate", "truncateText"],
|
|
507
|
+
"modifiers": ["tooltip"],
|
|
508
|
+
"services": ["store", "router"]
|
|
376
509
|
}
|
|
377
510
|
]
|
|
378
511
|
}
|
|
@@ -380,9 +513,9 @@ The storylang.json file contains seven main sections:
|
|
|
380
513
|
|
|
381
514
|
---
|
|
382
515
|
|
|
383
|
-
|
|
516
|
+
#### Data Types Reference
|
|
384
517
|
|
|
385
|
-
|
|
518
|
+
#### Data Types (dataType)
|
|
386
519
|
|
|
387
520
|
- `string`: Text values
|
|
388
521
|
- `int`: Integer numbers
|
|
@@ -390,51 +523,35 @@ The storylang.json file contains seven main sections:
|
|
|
390
523
|
- `array`: List of items
|
|
391
524
|
- `object`: Complex data structure
|
|
392
525
|
|
|
393
|
-
|
|
526
|
+
#### Argument Types (argType)
|
|
394
527
|
|
|
395
528
|
- `var`: Passed data/state
|
|
396
|
-
- `
|
|
529
|
+
- `function`: Callback function
|
|
397
530
|
- `get`: Get function
|
|
398
531
|
- `action`: User interaction handler
|
|
399
532
|
|
|
400
533
|
---
|
|
401
534
|
|
|
402
|
-
|
|
535
|
+
#### Integration with Other Files
|
|
403
536
|
|
|
404
|
-
|
|
537
|
+
#### With types.json
|
|
405
538
|
|
|
406
539
|
- Type names used in routes should match type names from `types.json`
|
|
407
540
|
- The `types` section in storylang.json is the explicit bridge between your data types and your UI — always keep it in sync with `types.json`
|
|
408
541
|
- Types are the gateway to persistent storage on the backend
|
|
542
|
+
- For a full reference on the `types.json` format and its field definitions, see the official documentation at [https://github.com/tribe-framework/types.json](https://github.com/tribe-framework/types.json)
|
|
409
543
|
|
|
410
544
|
---
|
|
411
545
|
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
**Always begin your thought process with routes → then move repeatable template logic into components → then move repeatable app-wide logic from components and routes to services → then extract reusable template and component functions into helpers → then extract template and component DOM behaviour into modifiers**
|
|
415
|
-
|
|
416
|
-
1. **Start with Routes**: Match route names to user mental models and use consistent naming conventions
|
|
417
|
-
2. **Minimize Route Logic**: Preferably fetch (read) type data in routes and then pass that data to components or services. Other than fetching type data, minimize the use of javascript in routes — Javascript is meant to be in components and services more than in routes
|
|
418
|
-
3. **Route Parameters**: Keep get_vars minimal and meaningful, and load only necessary types for each route
|
|
419
|
-
4. **Component Focus**: Keep components focused on single responsibilities and use descriptive, kebab-case names. Names should indicate which built-in ember-tribe component to use.
|
|
420
|
-
5. **Data Flow**: Receive backend data down from routes (via inherited_args) rather than fetching (reading) in components
|
|
421
|
-
6. **Component Actions**: Non-read functions — create, update, delete — can all happen well at component-level
|
|
422
|
-
7. **Service Integration**: Use services directly in both components and routes for app-wide logic
|
|
423
|
-
8. **Service Architecture**: Keep services stateless when possible and use dependency injection for service composition
|
|
424
|
-
9. **Service Role**: Services interact with both routes and components and store the core logic of the application
|
|
425
|
-
10. **Helpers**: Keep helpers pure and stateless — they should only receive input and return output with no side effects
|
|
426
|
-
11. **Modifiers**: Use modifiers to isolate all direct DOM manipulation and third-party library initialisation from component logic
|
|
427
|
-
12. **Types Mapping**: Populate the `types` section as you define your routes and components — it is your traceability layer between data types and UI
|
|
428
|
-
|
|
429
|
-
---
|
|
546
|
+
## EmberJS
|
|
430
547
|
|
|
431
|
-
|
|
548
|
+
### Ember-Tribe Development Guide
|
|
432
549
|
|
|
433
|
-
|
|
550
|
+
#### Required File Outputs
|
|
434
551
|
|
|
435
552
|
Make separate, complete code files for each category:
|
|
436
553
|
|
|
437
|
-
|
|
554
|
+
#### Example installer.sh
|
|
438
555
|
|
|
439
556
|
```bash
|
|
440
557
|
npm i chart.js
|
|
@@ -448,7 +565,7 @@ ember g modifier tooltip
|
|
|
448
565
|
ember g service visualization-builder
|
|
449
566
|
```
|
|
450
567
|
|
|
451
|
-
|
|
568
|
+
#### Default styling
|
|
452
569
|
|
|
453
570
|
Following is the default style that comes with tribe. Use the app.scss file for all style code. Change this based on your design styling requirements.
|
|
454
571
|
|
|
@@ -490,7 +607,7 @@ $spacers: (
|
|
|
490
607
|
@import 'node_modules/animate.css/animate';
|
|
491
608
|
```
|
|
492
609
|
|
|
493
|
-
|
|
610
|
+
#### Default application structure
|
|
494
611
|
|
|
495
612
|
```app/templates/application.hbs
|
|
496
613
|
{{page-title 'Your Application Name'}}
|
|
@@ -528,11 +645,11 @@ export default class ApplicationRoute extends Route {
|
|
|
528
645
|
|
|
529
646
|
---
|
|
530
647
|
|
|
531
|
-
|
|
648
|
+
### EmberData Integration
|
|
532
649
|
|
|
533
650
|
ember-tribe automatically generates models from backend track definitions through the `types` service:
|
|
534
651
|
|
|
535
|
-
|
|
652
|
+
#### Data Access Patterns
|
|
536
653
|
|
|
537
654
|
EmberData operations always use a "modules" key for field access, except for `.id` and `.slug` properties. All field names from backend storage use underscore notation: `modules.any_field`.
|
|
538
655
|
|
|
@@ -545,7 +662,7 @@ console.log(post.modules.title); // Field access
|
|
|
545
662
|
console.log(post.modules.content_privacy); // Universal field
|
|
546
663
|
```
|
|
547
664
|
|
|
548
|
-
|
|
665
|
+
#### Query Operations
|
|
549
666
|
|
|
550
667
|
```javascript
|
|
551
668
|
// Complex queries
|
|
@@ -558,6 +675,25 @@ this.store.query('post', {
|
|
|
558
675
|
});
|
|
559
676
|
```
|
|
560
677
|
|
|
678
|
+
#### `modules` vs `filter`: AND vs OR Queries
|
|
679
|
+
|
|
680
|
+
When querying records, `modules` and `filter` serve distinct purposes that map directly to how the backend constructs its SQL or query logic.
|
|
681
|
+
|
|
682
|
+
**`modules`** applies **AND** logic: every key-value pair in the object must match for a record to be included. Use this when you want to narrow results to records that simultaneously satisfy all of the given conditions — for example, posts that are both `published` and belong to a specific `author_id`.
|
|
683
|
+
|
|
684
|
+
**`filter`** applies **OR** logic: a record is included if it matches *any* of the key-value pairs. Use this when you want to broaden results across multiple values of a field — for example, items whose `category` is either `tech` or `design`.
|
|
685
|
+
|
|
686
|
+
The two can be combined in the same query. For instance, to find all published posts that are tagged as either `news` or `feature`:
|
|
687
|
+
|
|
688
|
+
```javascript
|
|
689
|
+
this.store.query('post', {
|
|
690
|
+
modules: { status: 'published' }, // must be published
|
|
691
|
+
filter: { tag: 'news', section: 'feature' }, // tagged news OR in feature section
|
|
692
|
+
});
|
|
693
|
+
```
|
|
694
|
+
|
|
695
|
+
Always prefer expressing these constraints via `modules` and `filter` over post-processing results in JavaScript — the backend handles this far more efficiently.
|
|
696
|
+
|
|
561
697
|
Smart use of EmberData can significantly reduce size of the codebase. Make sure you take advantage of that.
|
|
562
698
|
|
|
563
699
|
**Universal Default Module:**
|
|
@@ -584,9 +720,9 @@ this.store.findRecord('post', 1).then((post) => {
|
|
|
584
720
|
```javascript
|
|
585
721
|
this.store
|
|
586
722
|
.query('person', {
|
|
587
|
-
modules: { name: 'Peter', location: 'delhi' }, //
|
|
723
|
+
modules: { name: 'Peter', location: 'delhi' }, //AND: both conditions must match
|
|
588
724
|
/*
|
|
589
|
-
filter: { name: 'Peter', location: 'delhi' } //
|
|
725
|
+
filter: { name: 'Peter', location: 'delhi' } //OR: either condition can match
|
|
590
726
|
sort: "location,-age,name", //minus for descending order of that field, default is -id
|
|
591
727
|
page: { offset:0, limit:-1 }, //for pagination or smart uses, -1 means everything
|
|
592
728
|
ignore_ids: [10,14] //excludes these IDs from results
|
|
@@ -615,69 +751,9 @@ post.destroyRecord(); // => DELETE request
|
|
|
615
751
|
|
|
616
752
|
---
|
|
617
753
|
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
### Component Structure
|
|
621
|
-
|
|
622
|
-
```javascript
|
|
623
|
-
// Component class
|
|
624
|
-
import Component from '@glimmer/component';
|
|
625
|
-
import { tracked } from '@glimmer/tracking';
|
|
626
|
-
import { action } from '@ember/object';
|
|
627
|
-
import { service } from '@ember/service';
|
|
628
|
-
|
|
629
|
-
export default class FileCardComponent extends Component {
|
|
630
|
-
@service store;
|
|
631
|
-
@tracked isSelected = false;
|
|
632
|
-
|
|
633
|
-
@action
|
|
634
|
-
toggleSelection() {
|
|
635
|
-
this.isSelected = !this.isSelected;
|
|
636
|
-
}
|
|
637
|
-
}
|
|
638
|
-
```
|
|
639
|
-
|
|
640
|
-
### Template Patterns
|
|
641
|
-
|
|
642
|
-
```handlebars
|
|
643
|
-
<div
|
|
644
|
-
class='card {{if this.isSelected "border-primary"}}'
|
|
645
|
-
{{on 'click' this.toggleSelection}}
|
|
646
|
-
>
|
|
647
|
-
<div class='card-body'>
|
|
648
|
-
<h5 class='card-title'>{{@file.modules.title}}</h5>
|
|
649
|
-
<p class='card-text'>{{@file.modules.description}}</p>
|
|
650
|
-
</div>
|
|
651
|
-
</div>
|
|
652
|
-
```
|
|
653
|
-
|
|
654
|
-
---
|
|
655
|
-
|
|
656
|
-
## Services Integration
|
|
657
|
-
|
|
658
|
-
Make services based on storylang.json service definitions:
|
|
659
|
-
|
|
660
|
-
```javascript
|
|
661
|
-
// app/services/visualization-builder.js
|
|
662
|
-
import Service from '@ember/service';
|
|
663
|
-
import { tracked } from '@glimmer/tracking';
|
|
664
|
-
import { service } from '@ember/service';
|
|
665
|
-
|
|
666
|
-
export default class VisualizationBuilderService extends Service {
|
|
667
|
-
@service store;
|
|
668
|
-
@tracked supportedTypes = ['network', 'tree', 'sankey'];
|
|
669
|
-
|
|
670
|
-
buildVisualization(files, type, config) {
|
|
671
|
-
// Service logic implementation
|
|
672
|
-
}
|
|
673
|
-
}
|
|
674
|
-
```
|
|
675
|
-
|
|
676
|
-
---
|
|
677
|
-
|
|
678
|
-
## Route Generation
|
|
754
|
+
### Route Generation
|
|
679
755
|
|
|
680
|
-
|
|
756
|
+
#### Route Creation
|
|
681
757
|
|
|
682
758
|
Make routes based on storylang.json route definitions:
|
|
683
759
|
|
|
@@ -704,9 +780,9 @@ export default class FilesRoute extends Route {
|
|
|
704
780
|
|
|
705
781
|
---
|
|
706
782
|
|
|
707
|
-
|
|
783
|
+
### Helper System
|
|
708
784
|
|
|
709
|
-
|
|
785
|
+
#### Global Helpers
|
|
710
786
|
|
|
711
787
|
Make helpers based on storylang.json helper requirements:
|
|
712
788
|
|
|
@@ -721,7 +797,7 @@ export default helper(function formatDate([date, format = 'short']) {
|
|
|
721
797
|
});
|
|
722
798
|
```
|
|
723
799
|
|
|
724
|
-
|
|
800
|
+
#### Template Usage
|
|
725
801
|
|
|
726
802
|
```handlebars
|
|
727
803
|
<span class='text-muted'>
|
|
@@ -731,9 +807,9 @@ export default helper(function formatDate([date, format = 'short']) {
|
|
|
731
807
|
|
|
732
808
|
---
|
|
733
809
|
|
|
734
|
-
|
|
810
|
+
### Modifier System
|
|
735
811
|
|
|
736
|
-
|
|
812
|
+
#### DOM Interaction Modifiers
|
|
737
813
|
|
|
738
814
|
Make modifiers for specific DOM manipulation needs:
|
|
739
815
|
|
|
@@ -751,7 +827,7 @@ export default modifier((element, [content]) => {
|
|
|
751
827
|
});
|
|
752
828
|
```
|
|
753
829
|
|
|
754
|
-
|
|
830
|
+
#### Writing Helpers
|
|
755
831
|
|
|
756
832
|
Helper functions are JavaScript functions callable from Ember templates that perform computations or operations beyond basic template syntax, keeping templates clean while adding dynamic functionality.
|
|
757
833
|
|
|
@@ -806,7 +882,7 @@ export default function formatCurrency(amount, currency = 'USD') {
|
|
|
806
882
|
- Local: Component-specific logic, simple transformations
|
|
807
883
|
- Global: Reusable functionality across multiple components (formatting, calculations)
|
|
808
884
|
|
|
809
|
-
|
|
885
|
+
#### Component Architecture & Principle of Substitution
|
|
810
886
|
|
|
811
887
|
Ember components should be thought of as templates that re-execute from scratch whenever data changes. Write templates that produce correct output for any given input; Ember efficiently updates only what has changed.
|
|
812
888
|
|
|
@@ -852,7 +928,7 @@ export default class CounterComponent extends Component {
|
|
|
852
928
|
}
|
|
853
929
|
```
|
|
854
930
|
|
|
855
|
-
|
|
931
|
+
#### Component Communication & Modifiers
|
|
856
932
|
|
|
857
933
|
**Design Pattern:**
|
|
858
934
|
|
|
@@ -900,7 +976,7 @@ Modifiers applied to components pass through via `...attributes`:
|
|
|
900
976
|
<div ...attributes>...</div>
|
|
901
977
|
```
|
|
902
978
|
|
|
903
|
-
|
|
979
|
+
#### Services
|
|
904
980
|
|
|
905
981
|
Services are Ember objects that persist for the entire application duration, providing shared state or persistent connections across different parts of your app.
|
|
906
982
|
|
|
@@ -936,9 +1012,69 @@ export default class CartContentsComponent extends Component {
|
|
|
936
1012
|
|
|
937
1013
|
---
|
|
938
1014
|
|
|
939
|
-
|
|
1015
|
+
### Services Integration
|
|
1016
|
+
|
|
1017
|
+
Make services based on storylang.json service definitions:
|
|
1018
|
+
|
|
1019
|
+
```javascript
|
|
1020
|
+
// app/services/visualization-builder.js
|
|
1021
|
+
import Service from '@ember/service';
|
|
1022
|
+
import { tracked } from '@glimmer/tracking';
|
|
1023
|
+
import { service } from '@ember/service';
|
|
1024
|
+
|
|
1025
|
+
export default class VisualizationBuilderService extends Service {
|
|
1026
|
+
@service store;
|
|
1027
|
+
@tracked supportedTypes = ['network', 'tree', 'sankey'];
|
|
1028
|
+
|
|
1029
|
+
buildVisualization(files, type, config) {
|
|
1030
|
+
// Service logic implementation
|
|
1031
|
+
}
|
|
1032
|
+
}
|
|
1033
|
+
```
|
|
1034
|
+
|
|
1035
|
+
---
|
|
1036
|
+
|
|
1037
|
+
### Component Architecture
|
|
1038
|
+
|
|
1039
|
+
#### Component Structure
|
|
1040
|
+
|
|
1041
|
+
```javascript
|
|
1042
|
+
// Component class
|
|
1043
|
+
import Component from '@glimmer/component';
|
|
1044
|
+
import { tracked } from '@glimmer/tracking';
|
|
1045
|
+
import { action } from '@ember/object';
|
|
1046
|
+
import { service } from '@ember/service';
|
|
1047
|
+
|
|
1048
|
+
export default class FileCardComponent extends Component {
|
|
1049
|
+
@service store;
|
|
1050
|
+
@tracked isSelected = false;
|
|
1051
|
+
|
|
1052
|
+
@action
|
|
1053
|
+
toggleSelection() {
|
|
1054
|
+
this.isSelected = !this.isSelected;
|
|
1055
|
+
}
|
|
1056
|
+
}
|
|
1057
|
+
```
|
|
1058
|
+
|
|
1059
|
+
#### Template Patterns
|
|
1060
|
+
|
|
1061
|
+
```handlebars
|
|
1062
|
+
<div
|
|
1063
|
+
class='card {{if this.isSelected "border-primary"}}'
|
|
1064
|
+
{{on 'click' this.toggleSelection}}
|
|
1065
|
+
>
|
|
1066
|
+
<div class='card-body'>
|
|
1067
|
+
<h5 class='card-title'>{{@file.modules.title}}</h5>
|
|
1068
|
+
<p class='card-text'>{{@file.modules.description}}</p>
|
|
1069
|
+
</div>
|
|
1070
|
+
</div>
|
|
1071
|
+
```
|
|
1072
|
+
|
|
1073
|
+
---
|
|
1074
|
+
|
|
1075
|
+
### Forms and Input Fields
|
|
940
1076
|
|
|
941
|
-
|
|
1077
|
+
#### File upload javascript example
|
|
942
1078
|
|
|
943
1079
|
```javascript
|
|
944
1080
|
import ENV from '<your-application-name>/config/environment';
|
|
@@ -968,9 +1104,156 @@ async uploadFile(file) {
|
|
|
968
1104
|
}
|
|
969
1105
|
```
|
|
970
1106
|
|
|
1107
|
+
#### Input and Textarea fields
|
|
1108
|
+
|
|
1109
|
+
Use Ember's built-in `<Input>` component instead of a raw `<input>` tag — it automatically updates bound state via `@value`.
|
|
1110
|
+
|
|
1111
|
+
```handlebars
|
|
1112
|
+
<div class="mb-3">
|
|
1113
|
+
<label for="input-name" class="form-label">Name:</label>
|
|
1114
|
+
<Input
|
|
1115
|
+
id="input-name"
|
|
1116
|
+
class="form-control"
|
|
1117
|
+
@type="text"
|
|
1118
|
+
@value={{this.name}}
|
|
1119
|
+
disabled={{this.isReadOnly}}
|
|
1120
|
+
maxlength="50"
|
|
1121
|
+
placeholder="Enter your name"
|
|
1122
|
+
/>
|
|
1123
|
+
</div>
|
|
1124
|
+
```
|
|
1125
|
+
|
|
1126
|
+
```javascript
|
|
1127
|
+
import Component from '@glimmer/component';
|
|
1128
|
+
import { tracked } from '@glimmer/tracking';
|
|
1129
|
+
|
|
1130
|
+
export default class ExampleComponent extends Component {
|
|
1131
|
+
@tracked name = '';
|
|
1132
|
+
@tracked isReadOnly = false;
|
|
1133
|
+
}
|
|
1134
|
+
```
|
|
1135
|
+
|
|
1136
|
+
```handlebars
|
|
1137
|
+
<div class="form-check mb-3">
|
|
1138
|
+
<Input
|
|
1139
|
+
id="admin-checkbox"
|
|
1140
|
+
class="form-check-input"
|
|
1141
|
+
@type="checkbox"
|
|
1142
|
+
@checked={{this.isAdmin}}
|
|
1143
|
+
{{on "input" this.validateRole}}
|
|
1144
|
+
/>
|
|
1145
|
+
<label for="admin-checkbox" class="form-check-label">Is Admin?</label>
|
|
1146
|
+
</div>
|
|
1147
|
+
```
|
|
1148
|
+
|
|
1149
|
+
```handlebars
|
|
1150
|
+
<div class="mb-3">
|
|
1151
|
+
<label for="user-comment" class="form-label">Comment:</label>
|
|
1152
|
+
<Textarea
|
|
1153
|
+
id="user-comment"
|
|
1154
|
+
class="form-control"
|
|
1155
|
+
@value={{this.userComment}}
|
|
1156
|
+
rows="6"
|
|
1157
|
+
/>
|
|
1158
|
+
</div>
|
|
1159
|
+
```
|
|
1160
|
+
|
|
1161
|
+
**Key rules for `<Input>` and `<Textarea>`:**
|
|
1162
|
+
- `@value`, `@type`, and `@checked` must be passed as **arguments** (with `@`).
|
|
1163
|
+
- Use the `{{on}}` modifier for event handling (e.g. `{{on "input" this.handler}}`).
|
|
1164
|
+
- Bootstrap styles `form-control` correctly when `disabled` is present
|
|
1165
|
+
|
|
1166
|
+
#### ember-power-select example
|
|
1167
|
+
|
|
1168
|
+
`ember-power-select` is the recommended way to implement searchable, single, and multi-select dropdowns in ember-tribe. It is pre-installed and works alongside Bootstrap 5.x. Use it wherever a native `<select>` would be insufficient — e.g. when you need search/filter, async options, or multi-select.
|
|
1169
|
+
|
|
1170
|
+
**Single select (Bootstrap-compatible wrapper):**
|
|
1171
|
+
|
|
1172
|
+
```handlebars
|
|
1173
|
+
<div class="mb-3">
|
|
1174
|
+
<label class="form-label">Assign Category:</label>
|
|
1175
|
+
<div class="form-control p-0 border-0">
|
|
1176
|
+
<PowerSelect
|
|
1177
|
+
@options={{this.categories}}
|
|
1178
|
+
@selected={{this.selectedCategory}}
|
|
1179
|
+
@onChange={{this.handleCategoryChange}}
|
|
1180
|
+
@placeholder="Select a category"
|
|
1181
|
+
as |category|
|
|
1182
|
+
>
|
|
1183
|
+
{{category.name}}
|
|
1184
|
+
</PowerSelect>
|
|
1185
|
+
</div>
|
|
1186
|
+
</div>
|
|
1187
|
+
```
|
|
1188
|
+
|
|
1189
|
+
```javascript
|
|
1190
|
+
import Component from '@glimmer/component';
|
|
1191
|
+
import { tracked } from '@glimmer/tracking';
|
|
1192
|
+
import { action } from '@ember/object';
|
|
1193
|
+
|
|
1194
|
+
export default class ExampleComponent extends Component {
|
|
1195
|
+
@tracked selectedCategory = null;
|
|
1196
|
+
|
|
1197
|
+
categories = [
|
|
1198
|
+
{ id: 1, name: 'Design' },
|
|
1199
|
+
{ id: 2, name: 'Engineering' },
|
|
1200
|
+
{ id: 3, name: 'Marketing' },
|
|
1201
|
+
];
|
|
1202
|
+
|
|
1203
|
+
@action
|
|
1204
|
+
handleCategoryChange(category) {
|
|
1205
|
+
this.selectedCategory = category;
|
|
1206
|
+
}
|
|
1207
|
+
}
|
|
1208
|
+
```
|
|
1209
|
+
|
|
1210
|
+
**Multi-select variant:**
|
|
1211
|
+
|
|
1212
|
+
```handlebars
|
|
1213
|
+
<div class="mb-3">
|
|
1214
|
+
<label class="form-label">Assign Tags:</label>
|
|
1215
|
+
<div class="form-control p-0 border-0">
|
|
1216
|
+
<PowerSelectMultiple
|
|
1217
|
+
@options={{this.availableTags}}
|
|
1218
|
+
@selected={{this.selectedTags}}
|
|
1219
|
+
@onChange={{this.handleTagsChange}}
|
|
1220
|
+
@placeholder="Select tags"
|
|
1221
|
+
as |tag|
|
|
1222
|
+
>
|
|
1223
|
+
{{tag.label}}
|
|
1224
|
+
</PowerSelectMultiple>
|
|
1225
|
+
</div>
|
|
1226
|
+
</div>
|
|
1227
|
+
```
|
|
1228
|
+
|
|
1229
|
+
**Async options loaded from the store:**
|
|
1230
|
+
|
|
1231
|
+
```handlebars
|
|
1232
|
+
<div class="mb-3">
|
|
1233
|
+
<label class="form-label">Select Project:</label>
|
|
1234
|
+
<div class="form-control p-0 border-0">
|
|
1235
|
+
<PowerSelect
|
|
1236
|
+
@options={{this.projects}}
|
|
1237
|
+
@selected={{this.selectedProject}}
|
|
1238
|
+
@onChange={{this.handleProjectChange}}
|
|
1239
|
+
@searchField="name"
|
|
1240
|
+
@placeholder="Search projects..."
|
|
1241
|
+
as |project|
|
|
1242
|
+
>
|
|
1243
|
+
{{project.modules.name}}
|
|
1244
|
+
</PowerSelect>
|
|
1245
|
+
</div>
|
|
1246
|
+
</div>
|
|
1247
|
+
```
|
|
1248
|
+
|
|
1249
|
+
**Key rules for `<PowerSelect>`:**
|
|
1250
|
+
- `@options`, `@selected`, and `@onChange` are always required arguments.
|
|
1251
|
+
- Use `@searchField` to specify which object property drives the built-in search filter.
|
|
1252
|
+
- For multi-select, use `<PowerSelectMultiple>` — the `@onChange` callback receives the full updated array, so assign it directly to your tracked property.
|
|
1253
|
+
|
|
971
1254
|
---
|
|
972
1255
|
|
|
973
|
-
|
|
1256
|
+
### Deploying to Junction (Self-Hosted)
|
|
974
1257
|
|
|
975
1258
|
After building your Ember app, run `php-dist` to prepare the `dist/` folder for PHP middleware:
|
|
976
1259
|
|
|
@@ -985,19 +1268,6 @@ You can then upload the `dist/` folder to [Junction (open source)](http://localh
|
|
|
985
1268
|
|
|
986
1269
|
---
|
|
987
1270
|
|
|
988
|
-
## Best Practices
|
|
989
|
-
|
|
990
|
-
1. **Module Access**: Remember to use `modules.field_name` for backend fields
|
|
991
|
-
2. **npm packages vs ember addons**: Prioritize npm packages over ember addons for better compatibility, when both are offering similar functionality
|
|
992
|
-
3. **Minimal Controllers**: Logic should ideally reside in components and services
|
|
993
|
-
4. **Bootstrap 5.x Foundation**: Responsive, accessible design system
|
|
994
|
-
5. **Use FontAwesome 6.x**: Comprehensive icon library
|
|
995
|
-
6. **Animations**: If required, use animate.css for enhanced user experience. Prefer subtle animations (fadeIn, slideIn). Avoid overwhelming users with excessive animation
|
|
996
|
-
7. **Access Cache**: Leverage EmberData caching with `peekRecord`
|
|
997
|
-
8. **Avoid array manipulations of backend data**: Use backend filtering over frontend array manipulation
|
|
998
|
-
|
|
999
|
-
---
|
|
1000
|
-
|
|
1001
1271
|
# License
|
|
1002
1272
|
|
|
1003
1273
|
This project is licensed under the [GNU GPL v3 License](LICENSE.md).
|