ember-tribe 2.5.1 → 2.5.3
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 +587 -391
- package/blueprints/ember-tribe/files/app/components/welcome-flame.gjs +11 -0
- package/blueprints/ember-tribe/files/app/templates/application.gjs +6 -0
- package/blueprints/ember-tribe/files/app/templates/index.gjs +11 -0
- package/blueprints/ember-tribe/index.js +3 -4
- package/package.json +5 -2
- package/blueprints/ember-tribe/files/app/components/welcome-flame.hbs +0 -5
- package/blueprints/ember-tribe/files/app/templates/application.hbs +0 -2
- package/blueprints/ember-tribe/files/app/templates/index.hbs +0 -7
package/README.md
CHANGED
|
@@ -1,135 +1,475 @@
|
|
|
1
1
|
# ember-tribe
|
|
2
2
|
|
|
3
|
-
An addon that connects EmberJS to Tribe API.
|
|
4
|
-
Tribe is a collaborative project management framework - https://github.com/tribe-framework/tribe
|
|
3
|
+
An addon that connects EmberJS to Tribe API, bridging the gap between backend data structures and frontend application development. It helps you make an Ember app based on Junction's Blueprint file, also called `types.json`.
|
|
5
4
|
|
|
6
|
-
|
|
5
|
+
Tribe is a project management framework built for ease of collaboration - https://github.com/tribe-framework/tribe
|
|
7
6
|
|
|
8
|
-
##
|
|
7
|
+
## Installation and Setup
|
|
9
8
|
|
|
10
|
-
|
|
11
|
-
- Ember CLI v6.0 or above
|
|
9
|
+
### Prerequisites
|
|
12
10
|
|
|
13
|
-
|
|
11
|
+
- Ember CLI v6.0 – v6.4
|
|
12
|
+
- Node.js (latest LTS)
|
|
13
|
+
- Junction (optional) - via https://junction.express (cloud version) or https://tribe-framework.org (open source version)
|
|
14
14
|
|
|
15
|
-
|
|
15
|
+
### Installation
|
|
16
16
|
|
|
17
|
-
```
|
|
17
|
+
```bash
|
|
18
18
|
ember install ember-tribe
|
|
19
19
|
```
|
|
20
20
|
|
|
21
|
-
|
|
21
|
+
- Enter `TRIBE_API_URL` and `TRIBE_API_KEY` in `.env` file (copy of `.env.sample`). You can find these values in the Junction dashboard at [junction.express](https://junction.express) (cloud) or your self-hosted Tribe admin panel.
|
|
22
22
|
|
|
23
|
-
|
|
23
|
+
---
|
|
24
24
|
|
|
25
|
-
|
|
25
|
+
The addon automatically configures following essential packages:
|
|
26
26
|
|
|
27
|
-
|
|
27
|
+
**Ember Addons:** `ember-cli-dotenv`, `ember-cli-sass`, `ember-modifier`, `ember-composable-helpers`, `ember-truth-helpers`, `ember-file-upload` , `ember-power-select`
|
|
28
28
|
|
|
29
|
-
|
|
29
|
+
**NPM Packages:** `bootstrap`, `@popperjs/core`, `animate.css`, `video.js`, `swiper`, `howler`
|
|
30
30
|
|
|
31
|
-
|
|
31
|
+
---
|
|
32
32
|
|
|
33
|
-
|
|
33
|
+
## Core File Structure
|
|
34
34
|
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
35
|
+
```
|
|
36
|
+
app/
|
|
37
|
+
├── routes/
|
|
38
|
+
├── templates/
|
|
39
|
+
├── controllers/
|
|
40
|
+
├── components/
|
|
41
|
+
├── helpers/
|
|
42
|
+
├── modifiers/
|
|
43
|
+
├── services/
|
|
44
|
+
├── styles/app.scss
|
|
45
|
+
└── router.js
|
|
46
|
+
config/
|
|
47
|
+
└── storylang.json
|
|
48
|
+
installer.sh
|
|
49
|
+
```
|
|
39
50
|
|
|
40
|
-
|
|
51
|
+
---
|
|
41
52
|
|
|
42
|
-
|
|
53
|
+
## Storylang CLI
|
|
43
54
|
|
|
44
|
-
-
|
|
45
|
-
- Node.js (latest LTS)
|
|
46
|
-
- Junction backend
|
|
55
|
+
ember-tribe ships with a command-line tool called `storylang` that synchronises the `config/storylang.json` specification with the actual Ember project files.
|
|
47
56
|
|
|
48
|
-
###
|
|
57
|
+
### Usage
|
|
49
58
|
|
|
50
59
|
```bash
|
|
51
|
-
|
|
60
|
+
storylang <command> [options]
|
|
52
61
|
```
|
|
53
62
|
|
|
54
|
-
|
|
63
|
+
**Commands:**
|
|
64
|
+
|
|
65
|
+
| Command | Description |
|
|
66
|
+
|---|---|
|
|
67
|
+
| `storylang pull` | Updates `config/storylang.json` from current project files |
|
|
68
|
+
| `storylang push` | Generates any missing routes, components, services, helpers, modifiers, and controllers |
|
|
69
|
+
| `storylang push -o` | Regenerates all artefacts in `storylang.json`, overwriting existing files |
|
|
55
70
|
|
|
56
|
-
**
|
|
71
|
+
**Example:**
|
|
57
72
|
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
- `ember-modifier` - DOM manipulation helpers
|
|
61
|
-
- `ember-composable-helpers` - Template utilities
|
|
62
|
-
- `ember-truth-helpers` - Boolean logic helpers
|
|
63
|
-
- `ember-file-upload` - File handling
|
|
64
|
-
- `ember-power-select` - Advanced select components
|
|
65
|
-
- `ember-table` - Data tables
|
|
66
|
-
- `ember-animated` - Smooth animations
|
|
73
|
+
```bash
|
|
74
|
+
cd /path/to/ember/app
|
|
67
75
|
|
|
68
|
-
|
|
76
|
+
storylang pull
|
|
77
|
+
# => config/storylang.json updated from project files
|
|
69
78
|
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
79
|
+
storylang push
|
|
80
|
+
# => missing routes, components, services, helpers, modifiers, and controllers are generated
|
|
81
|
+
|
|
82
|
+
storylang push -o
|
|
83
|
+
# => all artefacts in storylang.json are (re)generated, overwriting existing files
|
|
84
|
+
```
|
|
76
85
|
|
|
77
|
-
|
|
86
|
+
### Typical Workflow
|
|
78
87
|
|
|
79
|
-
|
|
88
|
+
1. Define your `config/storylang.json` spec (manually or with the help of an AI tool), then run `storylang push` to generate the project files.
|
|
89
|
+
2. Run `storylang pull` periodically to keep the spec in sync as the project evolves.
|
|
80
90
|
|
|
81
|
-
|
|
91
|
+
---
|
|
82
92
|
|
|
83
|
-
|
|
84
|
-
2. **app/routes** - Route handlers and data loading
|
|
85
|
-
3. **app/templates & controllers** - UI templates and minimal controllers
|
|
86
|
-
4. **app/components** - Reusable UI components with component specific logic
|
|
87
|
-
5. **app/services** - Application-wide logic and state
|
|
93
|
+
## Storylang.json Documentation
|
|
88
94
|
|
|
89
|
-
|
|
95
|
+
### Overview
|
|
90
96
|
|
|
91
|
-
|
|
97
|
+
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.
|
|
92
98
|
|
|
93
|
-
|
|
99
|
+
### Purpose
|
|
100
|
+
|
|
101
|
+
The storylang.json file serves as a blueprint for frontend developers to understand:
|
|
102
|
+
|
|
103
|
+
- What routes, components, services, helpers, modifiers and types are required
|
|
104
|
+
- How data flows through the application
|
|
94
105
|
|
|
95
106
|
### File Structure
|
|
96
107
|
|
|
108
|
+
The storylang.json file contains seven main sections:
|
|
109
|
+
|
|
110
|
+
```json
|
|
111
|
+
{
|
|
112
|
+
"implementation_approach": "...",
|
|
113
|
+
"types": [...],
|
|
114
|
+
"components": [...],
|
|
115
|
+
"routes": [...],
|
|
116
|
+
"services": [...],
|
|
117
|
+
"helpers": [...],
|
|
118
|
+
"modifiers": [...]
|
|
119
|
+
}
|
|
97
120
|
```
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
121
|
+
|
|
122
|
+
### Section Definitions
|
|
123
|
+
|
|
124
|
+
### 1. Implementation Approach
|
|
125
|
+
|
|
126
|
+
**Purpose**: Provides a high-level technical overview of how the frontend interface would work.
|
|
127
|
+
|
|
128
|
+
**Format**:
|
|
129
|
+
|
|
130
|
+
```json
|
|
131
|
+
{
|
|
132
|
+
"implementation_approach": "Two-paragraph description explaining technical approach and key functionality."
|
|
133
|
+
}
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
---
|
|
137
|
+
|
|
138
|
+
### 2. Types
|
|
139
|
+
|
|
140
|
+
**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.
|
|
141
|
+
|
|
142
|
+
**Format**:
|
|
143
|
+
|
|
144
|
+
```json
|
|
145
|
+
{
|
|
146
|
+
"types": [
|
|
147
|
+
{
|
|
148
|
+
"slug": "type-slug", //type slug as defined in `types.json` blueprint
|
|
149
|
+
"used_in": { //where this type is used
|
|
150
|
+
"routes": ["route-name"],
|
|
151
|
+
"components": ["component-name"],
|
|
152
|
+
"services": ["service-name"],
|
|
153
|
+
"helpers": ["helper-name"],
|
|
154
|
+
"modifiers": ["modifier-name"]
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
]
|
|
158
|
+
}
|
|
109
159
|
```
|
|
110
160
|
|
|
111
|
-
|
|
161
|
+
---
|
|
162
|
+
|
|
163
|
+
### 3. Components
|
|
164
|
+
|
|
165
|
+
**Purpose**: Defines reusable UI components that will be built for the application.
|
|
166
|
+
|
|
167
|
+
**Format**:
|
|
168
|
+
|
|
169
|
+
```json
|
|
170
|
+
{
|
|
171
|
+
"components": [
|
|
172
|
+
{
|
|
173
|
+
"name": "component-name",
|
|
174
|
+
"type": "component-type",
|
|
175
|
+
"tracked_vars": [{ "<variableName>": "<dataType>" }],
|
|
176
|
+
"inherited_args": [{ "<argumentName>": "<argType>" }],
|
|
177
|
+
"actions": ["action1", "action2"],
|
|
178
|
+
"helpers": ["helper1", "helper2"],
|
|
179
|
+
"modifiers": ["modifier1"],
|
|
180
|
+
"services": ["service1", "service2"]
|
|
181
|
+
}
|
|
182
|
+
]
|
|
183
|
+
}
|
|
184
|
+
```
|
|
185
|
+
|
|
186
|
+
**Built-in Component Types in ember-tribe**:
|
|
187
|
+
|
|
188
|
+
- **Layout**: `table`, `figure`, `accordion`, `card`, `list-group`, `navbar`, `nav`, `tab`, `breadcrumb`
|
|
189
|
+
- **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`
|
|
190
|
+
|
|
191
|
+
**Example**:
|
|
192
|
+
|
|
193
|
+
```json
|
|
194
|
+
{
|
|
195
|
+
"components": [
|
|
196
|
+
{
|
|
197
|
+
"name": "file-summary-card",
|
|
198
|
+
"type": "card",
|
|
199
|
+
"tracked_vars": [{ "isSelected": "bool" }, { "isExpanded": "bool" }],
|
|
200
|
+
"inherited_args": [
|
|
201
|
+
{ "file": "var" },
|
|
202
|
+
{ "onEdit": "action" },
|
|
203
|
+
{ "onDelete": "action" }
|
|
204
|
+
],
|
|
205
|
+
"actions": ["toggleSelection", "expandDetails", "editFile", "deleteFile"],
|
|
206
|
+
"helpers": ["formatDate", "truncateText"],
|
|
207
|
+
"modifiers": ["tooltip"],
|
|
208
|
+
"services": ["store", "router"]
|
|
209
|
+
}
|
|
210
|
+
]
|
|
211
|
+
}
|
|
212
|
+
```
|
|
213
|
+
|
|
214
|
+
---
|
|
215
|
+
|
|
216
|
+
### 4. Routes
|
|
217
|
+
|
|
218
|
+
**Purpose**: Defines the application's routes and their requirements.
|
|
219
|
+
|
|
220
|
+
**Format**:
|
|
221
|
+
|
|
222
|
+
```json
|
|
223
|
+
{
|
|
224
|
+
"routes": [
|
|
225
|
+
{
|
|
226
|
+
"name": "route-name", //should match Ember router.js
|
|
227
|
+
"tracked_vars": [{ "<variableName>": "<dataType>" }],
|
|
228
|
+
"get_vars": [{ "<paramName>": "<dataType>" }],
|
|
229
|
+
"actions": ["action1", "action2"],
|
|
230
|
+
"helpers": ["helper1"],
|
|
231
|
+
"services": ["service1"],
|
|
232
|
+
"components": ["component1", "component2"],
|
|
233
|
+
"types": ["type1", "type2"]
|
|
234
|
+
}
|
|
235
|
+
]
|
|
236
|
+
}
|
|
237
|
+
```
|
|
238
|
+
|
|
239
|
+
---
|
|
240
|
+
|
|
241
|
+
### 5. Services
|
|
242
|
+
|
|
243
|
+
**Purpose**: Defines custom Ember services needed by the application.
|
|
244
|
+
|
|
245
|
+
**Format**:
|
|
246
|
+
|
|
247
|
+
```json
|
|
248
|
+
{
|
|
249
|
+
"services": [
|
|
250
|
+
{
|
|
251
|
+
"name": "service-name",
|
|
252
|
+
"tracked_vars": [{ "<variableName>": "<dataType>" }],
|
|
253
|
+
"actions": ["action1", "action2"],
|
|
254
|
+
"helpers": ["helper1"],
|
|
255
|
+
"services": ["dependency1", "dependency2"]
|
|
256
|
+
}
|
|
257
|
+
]
|
|
258
|
+
}
|
|
259
|
+
```
|
|
260
|
+
|
|
261
|
+
**Built-in Services in ember-tribe**:
|
|
262
|
+
|
|
263
|
+
- `store`: Ember Data store for CRUD operations
|
|
264
|
+
- `router`: Ember router service for navigation
|
|
265
|
+
- `types`: Automatic model generation from backend tracks
|
|
266
|
+
- `bootstrap`: Bootstrap CSS framework with Popper.js
|
|
267
|
+
- `papaparse`: CSV parsing library
|
|
268
|
+
- `sortablejs`: Drag-and-drop sorting
|
|
269
|
+
- `swiperjs`: Touch slider/carousel
|
|
270
|
+
- `videojs`: Video player
|
|
271
|
+
- `howlerjs`: Audio player
|
|
272
|
+
|
|
273
|
+
**Example**:
|
|
274
|
+
|
|
275
|
+
```json
|
|
276
|
+
{
|
|
277
|
+
"services": [
|
|
278
|
+
{
|
|
279
|
+
"name": "visualization-builder",
|
|
280
|
+
"tracked_vars": [
|
|
281
|
+
{ "currentVisualization": "object" },
|
|
282
|
+
{ "availableTypes": "array" }
|
|
283
|
+
],
|
|
284
|
+
"actions": [
|
|
285
|
+
"createVisualization",
|
|
286
|
+
"updateVisualization",
|
|
287
|
+
"deleteVisualization"
|
|
288
|
+
],
|
|
289
|
+
"helpers": ["validateConfig", "generatePreview"],
|
|
290
|
+
"services": ["store", "router"]
|
|
291
|
+
}
|
|
292
|
+
]
|
|
293
|
+
}
|
|
294
|
+
```
|
|
295
|
+
|
|
296
|
+
---
|
|
297
|
+
|
|
298
|
+
### 6. Helpers
|
|
299
|
+
|
|
300
|
+
**Purpose**: Defines custom template helpers — pure functions used in templates to format, compute or transform data for display.
|
|
301
|
+
|
|
302
|
+
**Format**:
|
|
303
|
+
|
|
304
|
+
```json
|
|
305
|
+
{
|
|
306
|
+
"helpers": [
|
|
307
|
+
{
|
|
308
|
+
"name": "helper-name",
|
|
309
|
+
"description": "What this helper does",
|
|
310
|
+
"args": [{ "<argumentName>": "<dataType>" }],
|
|
311
|
+
"return": "<dataType>"
|
|
312
|
+
}
|
|
313
|
+
]
|
|
314
|
+
}
|
|
315
|
+
```
|
|
316
|
+
|
|
317
|
+
**Helper Properties**:
|
|
318
|
+
|
|
319
|
+
- `name`: Kebab-case name of the helper
|
|
320
|
+
- `description`: Brief description of what the helper computes or transforms
|
|
321
|
+
- `args`: Input arguments the helper accepts
|
|
322
|
+
- `return`: The data type the helper outputs
|
|
323
|
+
|
|
324
|
+
**Example**:
|
|
325
|
+
|
|
326
|
+
```json
|
|
327
|
+
{
|
|
328
|
+
"helpers": [
|
|
329
|
+
{
|
|
330
|
+
"name": "format-date",
|
|
331
|
+
"description": "Formats a raw ISO date string into a human-readable date",
|
|
332
|
+
"args": [{ "isoString": "string" }, { "format": "string" }],
|
|
333
|
+
"return": "string"
|
|
334
|
+
},
|
|
335
|
+
{
|
|
336
|
+
"name": "truncate-text",
|
|
337
|
+
"description": "Truncates a string to a given character limit and appends an ellipsis",
|
|
338
|
+
"args": [{ "text": "string" }, { "limit": "int" }],
|
|
339
|
+
"return": "string"
|
|
340
|
+
}
|
|
341
|
+
]
|
|
342
|
+
}
|
|
343
|
+
```
|
|
344
|
+
|
|
345
|
+
---
|
|
346
|
+
|
|
347
|
+
### 7. Modifiers
|
|
348
|
+
|
|
349
|
+
**Purpose**: Defines custom Ember modifiers — functions that directly interact with DOM elements to attach behaviour, third-party libraries or event listeners.
|
|
350
|
+
|
|
351
|
+
**Format**:
|
|
352
|
+
|
|
353
|
+
```json
|
|
354
|
+
{
|
|
355
|
+
"modifiers": [
|
|
356
|
+
{
|
|
357
|
+
"name": "modifier-name",
|
|
358
|
+
"description": "What DOM behaviour this modifier applies",
|
|
359
|
+
"args": [{ "<argumentName>": "<dataType>" }],
|
|
360
|
+
"services": ["service1"]
|
|
361
|
+
}
|
|
362
|
+
]
|
|
363
|
+
}
|
|
364
|
+
```
|
|
365
|
+
|
|
366
|
+
**Modifier Properties**:
|
|
367
|
+
|
|
368
|
+
- `name`: Kebab-case name of the modifier
|
|
369
|
+
- `description`: Brief description of the DOM behaviour it attaches
|
|
370
|
+
- `args`: Arguments passed into the modifier from the template
|
|
371
|
+
- `services`: Ember services injected if needed
|
|
372
|
+
|
|
373
|
+
**Example**:
|
|
374
|
+
|
|
375
|
+
```json
|
|
376
|
+
{
|
|
377
|
+
"modifiers": [
|
|
378
|
+
{
|
|
379
|
+
"name": "tooltip",
|
|
380
|
+
"description": "Initialises a Bootstrap tooltip on the target element using the provided label",
|
|
381
|
+
"args": [{ "label": "string" }, { "placement": "string" }],
|
|
382
|
+
"services": []
|
|
383
|
+
},
|
|
384
|
+
{
|
|
385
|
+
"name": "autofocus",
|
|
386
|
+
"description": "Sets focus on the target element when it is inserted into the DOM",
|
|
387
|
+
"args": [],
|
|
388
|
+
"services": []
|
|
389
|
+
}
|
|
390
|
+
]
|
|
391
|
+
}
|
|
392
|
+
```
|
|
393
|
+
|
|
394
|
+
---
|
|
395
|
+
|
|
396
|
+
### Data Types Reference
|
|
397
|
+
|
|
398
|
+
### Data Types (dataType)
|
|
399
|
+
|
|
400
|
+
- `string`: Text values
|
|
401
|
+
- `int`: Integer numbers
|
|
402
|
+
- `bool`: Boolean true/false
|
|
403
|
+
- `array`: List of items
|
|
404
|
+
- `object`: Complex data structure
|
|
405
|
+
|
|
406
|
+
### Argument Types (argType)
|
|
407
|
+
|
|
408
|
+
- `var`: Passed data/state
|
|
409
|
+
- `fn`: Callback function
|
|
410
|
+
- `get`: Get function
|
|
411
|
+
- `action`: User interaction handler
|
|
412
|
+
|
|
413
|
+
---
|
|
414
|
+
|
|
415
|
+
### Integration with Other Files
|
|
416
|
+
|
|
417
|
+
### With types.json
|
|
418
|
+
|
|
419
|
+
- Type names used in routes should match type names from `types.json`
|
|
420
|
+
- 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`
|
|
421
|
+
- Types are the gateway to persistent storage on the backend
|
|
422
|
+
|
|
423
|
+
---
|
|
424
|
+
|
|
425
|
+
### Storylang Best Practices
|
|
426
|
+
|
|
427
|
+
**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**
|
|
428
|
+
|
|
429
|
+
1. **Start with Routes**: Match route names to user mental models and use consistent naming conventions
|
|
430
|
+
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
|
|
431
|
+
3. **Route Parameters**: Keep get_vars minimal and meaningful, and load only necessary types for each route
|
|
432
|
+
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.
|
|
433
|
+
5. **Data Flow**: Receive backend data down from routes (via inherited_args) rather than fetching (reading) in components
|
|
434
|
+
6. **Component Actions**: Non-read functions — create, update, delete — can all happen well at component-level
|
|
435
|
+
7. **Service Integration**: Use services directly in both components and routes for app-wide logic
|
|
436
|
+
8. **Service Architecture**: Keep services stateless when possible and use dependency injection for service composition
|
|
437
|
+
9. **Service Role**: Services interact with both routes and components and store the core logic of the application
|
|
438
|
+
10. **Helpers**: Keep helpers pure and stateless — they should only receive input and return output with no side effects
|
|
439
|
+
11. **Modifiers**: Use modifiers to isolate all direct DOM manipulation and third-party library initialisation from component logic
|
|
440
|
+
12. **Types Mapping**: Populate the `types` section as you define your routes and components — it is your traceability layer between data types and UI
|
|
441
|
+
|
|
442
|
+
---
|
|
443
|
+
|
|
444
|
+
## Ember-Tribe Development Guide
|
|
445
|
+
|
|
446
|
+
### Required File Outputs
|
|
112
447
|
|
|
113
448
|
Make separate, complete code files for each category:
|
|
114
449
|
|
|
115
|
-
### installer.sh
|
|
450
|
+
### Example installer.sh
|
|
116
451
|
|
|
117
452
|
```bash
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
ember
|
|
121
|
-
ember g
|
|
122
|
-
ember g
|
|
123
|
-
ember g
|
|
453
|
+
npm i chart.js
|
|
454
|
+
npm i lodash
|
|
455
|
+
ember install ember-table
|
|
456
|
+
ember g route files
|
|
457
|
+
ember g controller files
|
|
458
|
+
ember g component file-card -gc
|
|
459
|
+
ember g helper format-date
|
|
460
|
+
ember g modifier tooltip
|
|
461
|
+
ember g service visualization-builder
|
|
124
462
|
```
|
|
125
463
|
|
|
126
|
-
###
|
|
464
|
+
### Default styling
|
|
127
465
|
|
|
128
|
-
|
|
466
|
+
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.
|
|
467
|
+
|
|
468
|
+
```app/styles/app.scss
|
|
129
469
|
@import url('https://fonts.googleapis.com/css2?family=IBM+Plex+Mono:ital,wght@0,100;0,200;0,300;0,400;0,500;0,600;0,700;1,100;1,200;1,300;1,400;1,500;1,600;1,700&display=swap');
|
|
130
470
|
|
|
131
|
-
$font-family-sans-serif: 'IBM Plex Mono',
|
|
132
|
-
$display-font-family: 'IBM Plex Mono',
|
|
471
|
+
$font-family-sans-serif: 'IBM Plex Mono', monospace !default;
|
|
472
|
+
$display-font-family: 'IBM Plex Mono', monospace !default;
|
|
133
473
|
|
|
134
474
|
$primary: #000000 !default;
|
|
135
475
|
$secondary: #cccccc !default;
|
|
@@ -146,59 +486,68 @@ $enable-cssgrid: true !default;
|
|
|
146
486
|
|
|
147
487
|
$spacer: 1rem !default;
|
|
148
488
|
$spacers: (
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
489
|
+
0: 0,
|
|
490
|
+
1: $spacer * 0.25,
|
|
491
|
+
2: $spacer * 0.5,
|
|
492
|
+
3: $spacer,
|
|
493
|
+
4: $spacer * 1.5,
|
|
494
|
+
5: $spacer * 3,
|
|
495
|
+
6: $spacer * 4.5,
|
|
496
|
+
7: $spacer * 6,
|
|
497
|
+
8: $spacer * 7.5,
|
|
498
|
+
9: $spacer * 9,
|
|
499
|
+
10: $spacer * 12,
|
|
160
500
|
) !default;
|
|
161
501
|
|
|
162
502
|
@import 'node_modules/bootstrap/scss/bootstrap';
|
|
163
503
|
@import 'node_modules/animate.css/animate';
|
|
164
504
|
```
|
|
165
505
|
|
|
166
|
-
###
|
|
506
|
+
### Default application structure
|
|
167
507
|
|
|
168
|
-
```
|
|
508
|
+
```app/templates/application.hbs
|
|
169
509
|
{{page-title 'Your Application Name'}}
|
|
170
510
|
{{outlet}}
|
|
171
511
|
<BasicDropdownWormhole />
|
|
172
512
|
```
|
|
173
513
|
|
|
174
|
-
|
|
514
|
+
```app/routes/application.js
|
|
515
|
+
import Route from '@ember/routing/route';
|
|
516
|
+
import * as bootstrap from 'bootstrap';
|
|
517
|
+
import { service } from '@ember/service';
|
|
518
|
+
import { later } from '@ember/runloop';
|
|
519
|
+
import { action } from '@ember/object';
|
|
520
|
+
|
|
521
|
+
export default class ApplicationRoute extends Route {
|
|
522
|
+
@service types;
|
|
523
|
+
|
|
524
|
+
//auto-sync backend types
|
|
525
|
+
async beforeModel() { await this.types.fetchAgain() }
|
|
526
|
+
|
|
527
|
+
@action
|
|
528
|
+
didTransition() { later( this, () => { document.querySelector('#loading').classList.add('d-none') }, 50 ) }
|
|
529
|
+
|
|
530
|
+
@action
|
|
531
|
+
willTransition() { document.querySelector('#loading').classList.remove('d-none') }
|
|
532
|
+
}
|
|
533
|
+
```
|
|
534
|
+
|
|
535
|
+
**Application Extension Guidelines:**
|
|
175
536
|
|
|
176
537
|
- Extend when adding global navigation components
|
|
177
538
|
- Include shared modals or dropdowns
|
|
178
539
|
- Add application-wide notification systems
|
|
179
540
|
- Insert global loading states or overlays
|
|
180
541
|
|
|
181
|
-
|
|
542
|
+
---
|
|
182
543
|
|
|
183
|
-
|
|
544
|
+
## EmberData Integration
|
|
184
545
|
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
```javascript
|
|
188
|
-
// app/routes/application.js
|
|
189
|
-
export default class ApplicationRoute extends Route {
|
|
190
|
-
@service types;
|
|
191
|
-
|
|
192
|
-
async beforeModel() {
|
|
193
|
-
// Auto-generates models from backend
|
|
194
|
-
await this.types.fetchAgain();
|
|
195
|
-
}
|
|
196
|
-
}
|
|
197
|
-
```
|
|
546
|
+
ember-tribe automatically generates models from backend track definitions through the `types` service:
|
|
198
547
|
|
|
199
548
|
### Data Access Patterns
|
|
200
549
|
|
|
201
|
-
|
|
550
|
+
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`.
|
|
202
551
|
|
|
203
552
|
```javascript
|
|
204
553
|
// Accessing track data
|
|
@@ -214,61 +563,72 @@ console.log(post.modules.content_privacy); // Universal field
|
|
|
214
563
|
```javascript
|
|
215
564
|
// Complex queries
|
|
216
565
|
this.store.query('post', {
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
566
|
+
modules: { status: 'published' }, // AND conditions
|
|
567
|
+
filter: { category: 'tech' }, // OR conditions
|
|
568
|
+
sort: 'title,-created_date', // Sort (- for desc)
|
|
569
|
+
page: { offset: 0, limit: 10 }, // Pagination
|
|
570
|
+
show_public_objects_only: false, // Include drafts
|
|
222
571
|
});
|
|
223
572
|
```
|
|
224
573
|
|
|
225
|
-
|
|
574
|
+
Smart use of EmberData can significantly reduce size of the codebase. Make sure you take advantage of that.
|
|
226
575
|
|
|
227
|
-
|
|
576
|
+
**Universal Default Module:**
|
|
577
|
+
All objects include: `"content_privacy": "string | public, private, pending, draft"`
|
|
228
578
|
|
|
229
|
-
|
|
230
|
-
// Typography
|
|
231
|
-
@import url('https://fonts.googleapis.com/css2?family=IBM+Plex+Mono...');
|
|
232
|
-
$font-family-sans-serif: 'IBM Plex Mono', serif !default;
|
|
579
|
+
**Single Record Operations:**
|
|
233
580
|
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
// ... additional colors
|
|
581
|
+
```javascript
|
|
582
|
+
// Find by ID or slug
|
|
583
|
+
this.store.findRecord('track', 30);
|
|
584
|
+
this.store.findRecord('track', 'some-slug-here');
|
|
239
585
|
|
|
240
|
-
//
|
|
241
|
-
|
|
242
|
-
$enable-negative-margins: true !default;
|
|
243
|
-
$enable-cssgrid: true !default;
|
|
586
|
+
// Access without network request (if already in store)
|
|
587
|
+
let post = this.store.peekRecord('post', 1);
|
|
244
588
|
|
|
245
|
-
|
|
246
|
-
|
|
589
|
+
// Usage pattern
|
|
590
|
+
this.store.findRecord('post', 1).then((post) => {
|
|
591
|
+
// Access: post.id, post.slug, post.modules.<field_name>
|
|
592
|
+
});
|
|
247
593
|
```
|
|
248
594
|
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
- **Minimal Controllers**: Logic should reside in components and services
|
|
252
|
-
- **Bootstrap 5.x Foundation**: Responsive, accessible design system
|
|
253
|
-
- **Subtle Animations**: animate.css for enhanced user experience
|
|
254
|
-
- **FontAwesome 6.x**: Comprehensive icon library
|
|
595
|
+
**Multiple Records:**
|
|
255
596
|
|
|
256
|
-
|
|
597
|
+
```javascript
|
|
598
|
+
this.store
|
|
599
|
+
.query('person', {
|
|
600
|
+
modules: { name: 'Peter', location: 'delhi' }, //results with AND
|
|
601
|
+
/*
|
|
602
|
+
filter: { name: 'Peter', location: 'delhi' } //results with OR
|
|
603
|
+
sort: "location,-age,name", //minus for descending order of that field, default is -id
|
|
604
|
+
page: { offset:0, limit:-1 }, //for pagination or smart uses, -1 means everything
|
|
605
|
+
ignore_ids: [10,14] //excludes these IDs from results
|
|
606
|
+
show_public_objects_only: false, //default = true, if set false results include content_privacy = drafts, private or pending
|
|
607
|
+
*/
|
|
608
|
+
})
|
|
609
|
+
.then((results) => {
|
|
610
|
+
// Process results
|
|
611
|
+
});
|
|
612
|
+
```
|
|
257
613
|
|
|
258
|
-
|
|
614
|
+
Prefer using backend (this.store.query) for search, filter and sort, over front-end JS functions to achieve the same thing. Avoid using this.store.findAll altogether, use this.store.query with page: { offset:0, limit:-1 } instead.
|
|
259
615
|
|
|
260
|
-
|
|
616
|
+
**CRUD Operations:**
|
|
261
617
|
|
|
262
|
-
|
|
618
|
+
```javascript
|
|
619
|
+
// Update
|
|
620
|
+
let post = await this.store.findRecord('post', 1);
|
|
621
|
+
post.modules.title = 'A new title';
|
|
622
|
+
await post.save(); // => PATCH request
|
|
263
623
|
|
|
264
|
-
|
|
265
|
-
|
|
624
|
+
// Delete
|
|
625
|
+
let post = this.store.peekRecord('post', 2);
|
|
626
|
+
post.destroyRecord(); // => DELETE request
|
|
627
|
+
```
|
|
266
628
|
|
|
267
|
-
|
|
629
|
+
---
|
|
268
630
|
|
|
269
|
-
|
|
270
|
-
- `input-field`, `select`, `file-uploader`
|
|
271
|
-
- `pagination`, `popover`, `tooltip`
|
|
631
|
+
## Component Architecture
|
|
272
632
|
|
|
273
633
|
### Component Structure
|
|
274
634
|
|
|
@@ -280,13 +640,13 @@ import { action } from '@ember/object';
|
|
|
280
640
|
import { service } from '@ember/service';
|
|
281
641
|
|
|
282
642
|
export default class FileCardComponent extends Component {
|
|
283
|
-
|
|
284
|
-
|
|
643
|
+
@service store;
|
|
644
|
+
@tracked isSelected = false;
|
|
285
645
|
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
646
|
+
@action
|
|
647
|
+
toggleSelection() {
|
|
648
|
+
this.isSelected = !this.isSelected;
|
|
649
|
+
}
|
|
290
650
|
}
|
|
291
651
|
```
|
|
292
652
|
|
|
@@ -294,26 +654,19 @@ export default class FileCardComponent extends Component {
|
|
|
294
654
|
|
|
295
655
|
```handlebars
|
|
296
656
|
<div
|
|
297
|
-
|
|
298
|
-
|
|
657
|
+
class='card {{if this.isSelected "border-primary"}}'
|
|
658
|
+
{{on 'click' this.toggleSelection}}
|
|
299
659
|
>
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
660
|
+
<div class='card-body'>
|
|
661
|
+
<h5 class='card-title'>{{@file.modules.title}}</h5>
|
|
662
|
+
<p class='card-text'>{{@file.modules.description}}</p>
|
|
663
|
+
</div>
|
|
304
664
|
</div>
|
|
305
665
|
```
|
|
306
666
|
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
### Built-in Services
|
|
667
|
+
---
|
|
310
668
|
|
|
311
|
-
|
|
312
|
-
- `router` - Navigation and route management
|
|
313
|
-
- `types` - Automatic model generation
|
|
314
|
-
- `bootstrap` - Bootstrap component initialization
|
|
315
|
-
|
|
316
|
-
### Custom Services
|
|
669
|
+
## Services Integration
|
|
317
670
|
|
|
318
671
|
Make services based on storylang.json service definitions:
|
|
319
672
|
|
|
@@ -324,15 +677,17 @@ import { tracked } from '@glimmer/tracking';
|
|
|
324
677
|
import { service } from '@ember/service';
|
|
325
678
|
|
|
326
679
|
export default class VisualizationBuilderService extends Service {
|
|
327
|
-
|
|
328
|
-
|
|
680
|
+
@service store;
|
|
681
|
+
@tracked supportedTypes = ['network', 'tree', 'sankey'];
|
|
329
682
|
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
683
|
+
buildVisualization(files, type, config) {
|
|
684
|
+
// Service logic implementation
|
|
685
|
+
}
|
|
333
686
|
}
|
|
334
687
|
```
|
|
335
688
|
|
|
689
|
+
---
|
|
690
|
+
|
|
336
691
|
## Route Generation
|
|
337
692
|
|
|
338
693
|
### Route Creation
|
|
@@ -344,22 +699,24 @@ import Route from '@ember/routing/route';
|
|
|
344
699
|
import { service } from '@ember/service';
|
|
345
700
|
|
|
346
701
|
export default class FilesRoute extends Route {
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
702
|
+
@service store;
|
|
703
|
+
|
|
704
|
+
queryParams = {
|
|
705
|
+
page: { refreshModel: true },
|
|
706
|
+
search: { refreshModel: true },
|
|
707
|
+
};
|
|
708
|
+
|
|
709
|
+
async model(params) {
|
|
710
|
+
return await this.store.query('json_file', {
|
|
711
|
+
page: { offset: (params.page - 1) * 10, limit: 10 },
|
|
712
|
+
modules: params.search ? { title: params.search } : {},
|
|
713
|
+
});
|
|
714
|
+
}
|
|
360
715
|
}
|
|
361
716
|
```
|
|
362
717
|
|
|
718
|
+
---
|
|
719
|
+
|
|
363
720
|
## Helper System
|
|
364
721
|
|
|
365
722
|
### Global Helpers
|
|
@@ -371,9 +728,9 @@ Make helpers based on storylang.json helper requirements:
|
|
|
371
728
|
import { helper } from '@ember/component/helper';
|
|
372
729
|
|
|
373
730
|
export default helper(function formatDate([date, format = 'short']) {
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
731
|
+
return new Intl.DateTimeFormat('en-US', {
|
|
732
|
+
dateStyle: format,
|
|
733
|
+
}).format(new Date(date));
|
|
377
734
|
});
|
|
378
735
|
```
|
|
379
736
|
|
|
@@ -381,10 +738,12 @@ export default helper(function formatDate([date, format = 'short']) {
|
|
|
381
738
|
|
|
382
739
|
```handlebars
|
|
383
740
|
<span class='text-muted'>
|
|
384
|
-
|
|
741
|
+
{{format-date @post.modules.created_date 'medium'}}
|
|
385
742
|
</span>
|
|
386
743
|
```
|
|
387
744
|
|
|
745
|
+
---
|
|
746
|
+
|
|
388
747
|
## Modifier System
|
|
389
748
|
|
|
390
749
|
### DOM Interaction Modifiers
|
|
@@ -397,76 +756,15 @@ import { modifier } from 'ember-modifier';
|
|
|
397
756
|
import { Tooltip } from 'bootstrap';
|
|
398
757
|
|
|
399
758
|
export default modifier((element, [content]) => {
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
return () => tooltip.dispose();
|
|
405
|
-
});
|
|
406
|
-
```
|
|
407
|
-
|
|
408
|
-
## Ember.js Reference Guide
|
|
409
|
-
|
|
410
|
-
### EmberData Patterns
|
|
411
|
-
|
|
412
|
-
Smart use of EmberData can significantly reduce size of the codebase. Make sure you take advantage of that.
|
|
413
|
-
|
|
414
|
-
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`.
|
|
415
|
-
|
|
416
|
-
**Universal Default Module:**
|
|
417
|
-
All objects include: `"content_privacy": "string | public, private, pending, draft"`
|
|
418
|
-
|
|
419
|
-
**Single Record Operations:**
|
|
759
|
+
const tooltip = new Tooltip(element, {
|
|
760
|
+
title: content,
|
|
761
|
+
});
|
|
420
762
|
|
|
421
|
-
|
|
422
|
-
// Find by ID or slug
|
|
423
|
-
this.store.findRecord('track', 30);
|
|
424
|
-
this.store.findRecord('track', 'some-slug-here');
|
|
425
|
-
|
|
426
|
-
// Access without network request (if already in store)
|
|
427
|
-
let post = this.store.peekRecord('post', 1);
|
|
428
|
-
|
|
429
|
-
// Usage pattern
|
|
430
|
-
this.store.findRecord('post', 1).then((post) => {
|
|
431
|
-
// Access: post.id, post.slug, post.modules.<field_name>
|
|
763
|
+
return () => tooltip.dispose();
|
|
432
764
|
});
|
|
433
765
|
```
|
|
434
766
|
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
```javascript
|
|
438
|
-
this.store
|
|
439
|
-
.query('person', {
|
|
440
|
-
modules: { name: 'Peter', location: 'delhi' }, //results with AND
|
|
441
|
-
/*
|
|
442
|
-
filter: { name: 'Peter', location: 'delhi' } //results with OR
|
|
443
|
-
sort: "location,-age,name", //minus for descending order of that field, default is -id
|
|
444
|
-
page: { offset:0, limit:-1 }, //for pagination or smart uses, -1 means everything
|
|
445
|
-
ignore_ids: [10,14] //excludes these IDs from results
|
|
446
|
-
show_public_objects_only: false, //default = true, if set false results include content_privacy = drafts, private or pending
|
|
447
|
-
*/
|
|
448
|
-
})
|
|
449
|
-
.then((results) => {
|
|
450
|
-
// Process results
|
|
451
|
-
});
|
|
452
|
-
```
|
|
453
|
-
|
|
454
|
-
Prefer using backend (this.store.query) for search, filter and sort, over front-end JS functions to achieve the same thing. Avoid using this.store.findAll altogether, use this.store.query with page: { offset:0, limit:-1 } instead.
|
|
455
|
-
|
|
456
|
-
**CRUD Operations:**
|
|
457
|
-
|
|
458
|
-
```javascript
|
|
459
|
-
// Update
|
|
460
|
-
let post = await this.store.findRecord('post', 1);
|
|
461
|
-
post.modules.title = 'A new title';
|
|
462
|
-
await post.save(); // => PATCH request
|
|
463
|
-
|
|
464
|
-
// Delete
|
|
465
|
-
let post = this.store.peekRecord('post', 2);
|
|
466
|
-
post.destroyRecord(); // => DELETE request
|
|
467
|
-
```
|
|
468
|
-
|
|
469
|
-
### Helpers
|
|
767
|
+
## Writing Helpers
|
|
470
768
|
|
|
471
769
|
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.
|
|
472
770
|
|
|
@@ -479,8 +777,8 @@ Helper functions are JavaScript functions callable from Ember templates that per
|
|
|
479
777
|
```javascript
|
|
480
778
|
// app/components/user-card.js
|
|
481
779
|
export default class UserCard extends Component {
|
|
482
|
-
|
|
483
|
-
|
|
780
|
+
formatName = (firstName, lastName) =>
|
|
781
|
+
`${firstName} ${lastName}`.toUpperCase();
|
|
484
782
|
}
|
|
485
783
|
```
|
|
486
784
|
|
|
@@ -529,8 +827,8 @@ Ember components should be thought of as templates that re-execute from scratch
|
|
|
529
827
|
|
|
530
828
|
```handlebars
|
|
531
829
|
<article title='{{@article.modules.title}}'>
|
|
532
|
-
|
|
533
|
-
|
|
830
|
+
<header><h1>{{@article.modules.title}}</h1></header>
|
|
831
|
+
<section>{{@article.modules.body}}</section>
|
|
534
832
|
</article>
|
|
535
833
|
```
|
|
536
834
|
|
|
@@ -541,7 +839,7 @@ Ember components should be thought of as templates that re-execute from scratch
|
|
|
541
839
|
|
|
542
840
|
```handlebars
|
|
543
841
|
<div class={{if @user.modules.is_admin 'superuser' 'standard'}}>
|
|
544
|
-
|
|
842
|
+
Welcome to my app.
|
|
545
843
|
</div>
|
|
546
844
|
```
|
|
547
845
|
|
|
@@ -558,12 +856,12 @@ import { action } from '@ember/object';
|
|
|
558
856
|
import { tracked } from '@glimmer/tracking';
|
|
559
857
|
|
|
560
858
|
export default class CounterComponent extends Component {
|
|
561
|
-
|
|
859
|
+
@tracked count = 0;
|
|
562
860
|
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
861
|
+
@action
|
|
862
|
+
increment() {
|
|
863
|
+
this.count++;
|
|
864
|
+
}
|
|
567
865
|
}
|
|
568
866
|
```
|
|
569
867
|
|
|
@@ -598,11 +896,11 @@ play() {
|
|
|
598
896
|
import { modifier } from 'ember-modifier';
|
|
599
897
|
|
|
600
898
|
export default modifier((element, [isPlaying]) => {
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
899
|
+
if (isPlaying) {
|
|
900
|
+
element.play();
|
|
901
|
+
} else {
|
|
902
|
+
element.pause();
|
|
903
|
+
}
|
|
606
904
|
});
|
|
607
905
|
```
|
|
608
906
|
|
|
@@ -627,19 +925,13 @@ import { TrackedArray } from 'tracked-built-ins';
|
|
|
627
925
|
import Service from '@ember/service';
|
|
628
926
|
|
|
629
927
|
export default class ShoppingCartService extends Service {
|
|
630
|
-
|
|
928
|
+
items = new TrackedArray([]);
|
|
631
929
|
|
|
632
|
-
|
|
633
|
-
this.items.push(item);
|
|
634
|
-
}
|
|
930
|
+
add(item) { this.items.push(item) }
|
|
635
931
|
|
|
636
|
-
|
|
637
|
-
this.items.splice(this.items.indexOf(item), 1);
|
|
638
|
-
}
|
|
932
|
+
remove(item) { this.items.splice(this.items.indexOf(item), 1) }
|
|
639
933
|
|
|
640
|
-
|
|
641
|
-
this.items.splice(0, this.items.length);
|
|
642
|
-
}
|
|
934
|
+
empty() { this.items.splice(0, this.items.length) }
|
|
643
935
|
}
|
|
644
936
|
```
|
|
645
937
|
|
|
@@ -650,16 +942,12 @@ import Component from '@glimmer/component';
|
|
|
650
942
|
import { service } from '@ember/service';
|
|
651
943
|
|
|
652
944
|
export default class CartContentsComponent extends Component {
|
|
653
|
-
|
|
654
|
-
|
|
945
|
+
// Loads service from: app/services/shopping-cart.js
|
|
946
|
+
@service shoppingCart;
|
|
655
947
|
}
|
|
656
948
|
```
|
|
657
949
|
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
- Use for application-wide state management
|
|
661
|
-
- Share functionality across multiple routes/components
|
|
662
|
-
- Maintain data that survives route transitions
|
|
950
|
+
---
|
|
663
951
|
|
|
664
952
|
## Code Generation Process
|
|
665
953
|
|
|
@@ -693,113 +981,21 @@ async uploadFile(file) {
|
|
|
693
981
|
}
|
|
694
982
|
```
|
|
695
983
|
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
```bash
|
|
699
|
-
# Write all installer.sh commands
|
|
700
|
-
ember g route files
|
|
701
|
-
ember g controller files
|
|
702
|
-
ember g component file-card -gc
|
|
703
|
-
ember g helper format-date
|
|
704
|
-
ember g modifier tooltip
|
|
705
|
-
ember g service visualization-builder
|
|
706
|
-
```
|
|
707
|
-
|
|
708
|
-
### Pre-approval Process
|
|
709
|
-
|
|
710
|
-
Before code generation, ember-tribe requests approval for additional packages:
|
|
711
|
-
|
|
712
|
-
```bash
|
|
713
|
-
# Example approval request
|
|
714
|
-
npm i chart.js
|
|
715
|
-
npm i lodash
|
|
716
|
-
ember install ember-table
|
|
717
|
-
```
|
|
718
|
-
|
|
719
|
-
## Application Structure
|
|
720
|
-
|
|
721
|
-
### Application Template
|
|
722
|
-
|
|
723
|
-
```handlebars
|
|
724
|
-
{{page-title 'Your Application Name'}}
|
|
725
|
-
{{outlet}}
|
|
726
|
-
<BasicDropdownWormhole />
|
|
727
|
-
```
|
|
984
|
+
---
|
|
728
985
|
|
|
729
986
|
## Best Practices
|
|
730
987
|
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
- Use animate.css for enhanced UX
|
|
740
|
-
- Prefer subtle animations (fadeIn, slideIn)
|
|
741
|
-
- Avoid overwhelming users with excessive animation
|
|
742
|
-
|
|
743
|
-
### Data Flow
|
|
744
|
-
|
|
745
|
-
1. **Routes**: Fetch and prepare data
|
|
746
|
-
2. **Components**: Display and interact with data
|
|
747
|
-
3. **Services**: Handle business logic and state
|
|
748
|
-
4. **Helpers**: Transform data for display
|
|
749
|
-
|
|
750
|
-
### Performance Considerations
|
|
751
|
-
|
|
752
|
-
- Leverage EmberData caching with `peekRecord`
|
|
753
|
-
- Use backend filtering over frontend array manipulation
|
|
754
|
-
- Implement pagination for large datasets
|
|
755
|
-
- Minimize controller file count
|
|
756
|
-
|
|
757
|
-
## Integration Examples
|
|
988
|
+
1. **Module Access**: Remember to use `modules.field_name` for backend fields
|
|
989
|
+
2. **npm packages vs ember addons**: Prioritize npm packages over ember addons for better compatibility, when both are offering similar functionality
|
|
990
|
+
3. **Minimal Controllers**: Logic should ideally reside in components and services
|
|
991
|
+
4. **Bootstrap 5.x Foundation**: Responsive, accessible design system
|
|
992
|
+
5. **Use FontAwesome 6.x**: Comprehensive icon library
|
|
993
|
+
6. **Animations**: If required, use animate.css for enhanced user experience. Prefer subtle animations (fadeIn, slideIn). Avoid overwhelming users with excessive animation
|
|
994
|
+
7. **Access Cache**: Leverage EmberData caching with `peekRecord`
|
|
995
|
+
8. **Avoid array manipulations of backend data**: Use backend filtering over frontend array manipulation
|
|
758
996
|
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
```javascript
|
|
762
|
-
// Automatic sync with Junction tracks
|
|
763
|
-
async beforeModel() {
|
|
764
|
-
await this.types.fetchAgain(); // Syncs with backend types
|
|
765
|
-
}
|
|
766
|
-
```
|
|
767
|
-
|
|
768
|
-
### With External APIs
|
|
769
|
-
|
|
770
|
-
```javascript
|
|
771
|
-
// Service for external integrations
|
|
772
|
-
@service externalApi;
|
|
773
|
-
|
|
774
|
-
async model() {
|
|
775
|
-
const localData = await this.store.query('post', {});
|
|
776
|
-
const externalData = await this.externalApi.fetch('/posts');
|
|
777
|
-
return { local: localData, external: externalData };
|
|
778
|
-
}
|
|
779
|
-
```
|
|
780
|
-
|
|
781
|
-
## Troubleshooting
|
|
782
|
-
|
|
783
|
-
### Common Issues
|
|
784
|
-
|
|
785
|
-
1. **Model Not Found**: Ensure `types.fetchAgain()` completes in application route
|
|
786
|
-
2. **Module Access**: Remember to use `modules.field_name` for backend fields
|
|
787
|
-
3. **Bootstrap Components**: Initialize through modifier or service
|
|
788
|
-
4. **Animation Conflicts**: Check animate.css class conflicts
|
|
789
|
-
|
|
790
|
-
### Debug Commands
|
|
791
|
-
|
|
792
|
-
```javascript
|
|
793
|
-
// Check loaded models
|
|
794
|
-
this.store.peekAll('your-model-name');
|
|
795
|
-
|
|
796
|
-
// Verify service registration
|
|
797
|
-
this.owner.lookup('service:your-service');
|
|
798
|
-
|
|
799
|
-
// Route debugging
|
|
800
|
-
console.log(this.router.currentRouteName);
|
|
801
|
-
```
|
|
997
|
+
---
|
|
802
998
|
|
|
803
999
|
# License
|
|
804
1000
|
|
|
805
|
-
This project is licensed under the [GNU GPL v3 License](LICENSE.md).
|
|
1001
|
+
This project is licensed under the [GNU GPL v3 License](LICENSE.md).
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import Component from '@glimmer/component';
|
|
2
|
+
|
|
3
|
+
export default class WelcomeFlameComponent extends Component {
|
|
4
|
+
<template>
|
|
5
|
+
<section class="flame-bg d-flex align-items-center justify-content-center">
|
|
6
|
+
<div class="py-6 container px-0 text-center text-dark">
|
|
7
|
+
<img src="/assets/img/flame.png" width="200">
|
|
8
|
+
</div>
|
|
9
|
+
</section>
|
|
10
|
+
</template>
|
|
11
|
+
}
|
|
@@ -17,20 +17,19 @@ module.exports = {
|
|
|
17
17
|
{ name: 'ember-math-helpers' },
|
|
18
18
|
{ name: 'ember-cli-string-helpers' },
|
|
19
19
|
{ name: 'ember-promise-helpers' },
|
|
20
|
-
{ name: '@ember/test-waiters'
|
|
20
|
+
{ name: '@ember/test-waiters' },
|
|
21
21
|
{ name: 'ember-tag-input' },
|
|
22
22
|
{ name: 'ember-file-upload' },
|
|
23
23
|
{ name: 'ember-toggle' },
|
|
24
|
-
{ name: 'ember-
|
|
25
|
-
{ name: 'ember-power-select' },
|
|
24
|
+
{ name: 'ember-concurrency' },
|
|
26
25
|
{ name: 'ember-click-outside' },
|
|
27
26
|
{ name: 'ember-web-app' },
|
|
28
27
|
{ name: 'tracked-built-ins' },
|
|
29
28
|
{ name: 'ember-keyboard' },
|
|
30
29
|
{ name: 'ember-router-scroll' },
|
|
31
|
-
{ name: 'ember-concurrency' },
|
|
32
30
|
{ name: 'ember-table' },
|
|
33
31
|
{ name: 'ember-animated' },
|
|
32
|
+
{ name: 'ember-power-select' },
|
|
34
33
|
],
|
|
35
34
|
}).then(() => {
|
|
36
35
|
return this.addPackagesToProject([
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ember-tribe",
|
|
3
|
-
"version": "2.5.
|
|
3
|
+
"version": "2.5.3",
|
|
4
4
|
"description": "The default blueprint for using Tribe API and Junction within EmberJS.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"ember-addon"
|
|
@@ -38,7 +38,7 @@
|
|
|
38
38
|
"babel-eslint": "^10.1.0",
|
|
39
39
|
"broccoli-asset-rev": "^3.0.0",
|
|
40
40
|
"ember-auto-import": "^2.2.4",
|
|
41
|
-
"ember-cli": "^6.
|
|
41
|
+
"ember-cli": "^6.4.0",
|
|
42
42
|
"ember-cli-dependency-checker": "^3.2.0",
|
|
43
43
|
"ember-cli-inject-live-reload": "^2.1.0",
|
|
44
44
|
"ember-cli-sri": "^2.1.1",
|
|
@@ -66,6 +66,9 @@
|
|
|
66
66
|
"qunit-dom": "^2.0.0",
|
|
67
67
|
"webpack": "^5.65.0"
|
|
68
68
|
},
|
|
69
|
+
"peerDependencies": {
|
|
70
|
+
"ember-cli": "<=6.4.0"
|
|
71
|
+
},
|
|
69
72
|
"engines": {
|
|
70
73
|
"node": "12.* || 14.* || >= 16"
|
|
71
74
|
},
|