@startinblox/components-ds4go 2.0.0 → 2.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@startinblox/components-ds4go",
3
- "version": "2.0.0",
3
+ "version": "2.1.1",
4
4
  "description": "Startin'blox DS4GO",
5
5
  "main": "dist/index.js",
6
6
  "type": "module",
@@ -0,0 +1,399 @@
1
+ import {
2
+ filterObjectByValue,
3
+ formatDate,
4
+ OrbitComponent,
5
+ sort,
6
+ } from "@helpers";
7
+ import { msg, str } from "@lit/localize";
8
+ import { Task } from "@lit/task";
9
+ import type { PropertiesPicker, Resource } from "@src/component";
10
+ import FlexHelpers from "@styles/_helpers/flex.scss?inline";
11
+ import { css, html, nothing, unsafeCSS } from "lit";
12
+ import { customElement, property, state } from "lit/decorators.js";
13
+
14
+ @customElement("solid-fact-bundle-creation")
15
+ export class SolidFactBundle extends OrbitComponent {
16
+ static styles = [
17
+ unsafeCSS(FlexHelpers),
18
+ css`
19
+ [slot="content"] {
20
+ height: 100%;
21
+ }
22
+ section {
23
+ gap: var(--scale-400);
24
+ padding: var(--scale-900) 0;
25
+ height: calc(100% - var(--scale-900) * 2);
26
+ }
27
+ section > div {
28
+ height: 100%;
29
+ display: flex;
30
+ flex-direction: column;
31
+ gap: var(--scale-400);
32
+ flex: 1;
33
+ }
34
+ section > div:first-child {
35
+ max-width: 400px;
36
+ }
37
+ .card-grid {
38
+ flex: 1;
39
+ overflow-y: scroll;
40
+ display: flex;
41
+ flex-direction: row;
42
+ flex-wrap: wrap;
43
+ gap: 20px;
44
+ align-content: flex-start;
45
+ }
46
+ .card-grid-vertical {
47
+ justify-content: stretch;
48
+ }
49
+ .card-grid-vertical tems-card-catalog {
50
+ width: 354px;
51
+ height: fit-content;
52
+ }
53
+ tems-card-catalog {
54
+ cursor: pointer;
55
+ }
56
+ tems-card-catalog[selected] {
57
+ --color-border-primary: var(--color-surface-action-solid);
58
+ }
59
+ tems-search-bar {
60
+ --scale-900: 0;
61
+ }
62
+ .gap-400 {
63
+ gap: var(--scale-400);
64
+ }
65
+ `,
66
+ ];
67
+
68
+ @property({ attribute: "header", type: String })
69
+ header?: string = "DS4GO Fact Bundling Creation";
70
+
71
+ @property({ attribute: "facts-src", reflect: true })
72
+ factsSrc?: string;
73
+
74
+ @state()
75
+ selectedFacts: Resource[] = [];
76
+
77
+ @state()
78
+ bundleName = "";
79
+
80
+ @state()
81
+ bundleDescription = "";
82
+
83
+ @state()
84
+ filterFactText = "";
85
+
86
+ @state()
87
+ spliceLength = 0;
88
+
89
+ cherryPickedProperties: PropertiesPicker[] = [
90
+ { key: "created_at", value: "created_at", cast: formatDate },
91
+ { key: "updated_at", value: "updated_at", cast: formatDate },
92
+ { key: "name", value: "name" },
93
+ { key: "description", value: "description" },
94
+ { key: "author", value: "author" },
95
+ {
96
+ key: "categories",
97
+ value: "categories",
98
+ expand: true,
99
+ cast: (c: Resource[]) => sort(c, "name"),
100
+ },
101
+ ];
102
+
103
+ _getResource = new Task(this, {
104
+ task: async ([factsSrc, filterText, selectedFacts]) => {
105
+ if (
106
+ !factsSrc ||
107
+ !this.orbit ||
108
+ (!this.noRouter &&
109
+ this.route &&
110
+ this.currentRoute &&
111
+ !this.route.startsWith(this.currentRoute))
112
+ )
113
+ return;
114
+
115
+ if (!this.hasCachedDatas || this.oldDataSrc !== factsSrc) {
116
+ if (!factsSrc) return;
117
+
118
+ this.facts = (await this._getProxyValue(factsSrc)) as Resource[];
119
+ this.hasCachedDatas = true;
120
+ }
121
+
122
+ if (this.oldDataSrc !== factsSrc) {
123
+ this.oldDataSrc = factsSrc;
124
+ }
125
+
126
+ if (!Array.isArray(this.facts)) {
127
+ this.facts = [];
128
+ }
129
+
130
+ if (filterText) {
131
+ return sort(
132
+ filterObjectByValue(
133
+ this.facts.filter(
134
+ (fact: Resource) => !selectedFacts.includes(fact),
135
+ ),
136
+ filterText,
137
+ ),
138
+ "name",
139
+ "asc",
140
+ );
141
+ }
142
+
143
+ if (selectedFacts.length === 0) {
144
+ this.spliceLength =
145
+ Math.floor((window.innerWidth - 800) / 354) *
146
+ Math.floor((window.innerHeight - 255) / 500);
147
+ }
148
+
149
+ return sort(
150
+ this.facts.filter(
151
+ (fact: Resource) => !this.selectedFacts.includes(fact),
152
+ ),
153
+ "name",
154
+ "asc",
155
+ );
156
+ },
157
+ args: () => [
158
+ this.factsSrc,
159
+ this.filterFactText,
160
+ this.selectedFacts,
161
+ this.caching,
162
+ this.currentRoute,
163
+ ],
164
+ });
165
+
166
+ _showMoreResults() {
167
+ this.spliceLength += Math.floor((window.innerWidth - 800) / 354);
168
+ }
169
+
170
+ _showAllResults() {
171
+ this.spliceLength = this.facts.length;
172
+ }
173
+
174
+ async _createFactBundle() {
175
+ if (!this.bundleName || !(this.selectedFacts.length > 0)) {
176
+ return;
177
+ }
178
+
179
+ const newFactBundle = {
180
+ "@context": [
181
+ "https://cdn.startinblox.com/owl/context.jsonld",
182
+ "https://cdn.startinblox.com/owl/ds4go.jsonld",
183
+ ],
184
+ "@type": ["ldp:Container", "ds4go:FactBundle"],
185
+ name: this.bundleName,
186
+ description: this.bundleDescription,
187
+ facts: this.selectedFacts.map((fact: Resource) => fact.id),
188
+ };
189
+
190
+ const newFactBundleId = await window.sibStore.post(
191
+ newFactBundle,
192
+ this.dataSrc,
193
+ );
194
+
195
+ document.dispatchEvent(
196
+ new CustomEvent("save", {
197
+ detail: { resource: { "@id": newFactBundleId } },
198
+ bubbles: true,
199
+ }),
200
+ );
201
+
202
+ document.dispatchEvent(
203
+ new CustomEvent("save", {
204
+ detail: { resource: { "@id": this.dataSrc } },
205
+ bubbles: true,
206
+ }),
207
+ );
208
+ }
209
+
210
+ _updateBundleName(e: Event) {
211
+ this.bundleName = e.detail.value;
212
+ }
213
+
214
+ _updateBundleDescription(e: Event) {
215
+ this.bundleDescription = e.detail.value;
216
+ }
217
+
218
+ _toggleFactSelection(fact: Resource) {
219
+ if (this.selectedFacts.includes(fact)) {
220
+ this.selectedFacts = this.selectedFacts.filter((f) => f !== fact);
221
+ } else {
222
+ this.selectedFacts = [...this.selectedFacts, fact];
223
+ }
224
+ }
225
+
226
+ _searchFacts(e: Event) {
227
+ this.filterFactText = e.detail;
228
+ }
229
+
230
+ render() {
231
+ return (
232
+ this.gatekeeper() ||
233
+ this._getResource.render({
234
+ pending: () => html`<solid-loader></solid-loader>`,
235
+ error: (e) => {
236
+ console.warn("[solid-fact-bundle-creation] Task error:", e);
237
+ return nothing;
238
+ },
239
+ complete: (datas) => {
240
+ if (!datas) return nothing;
241
+
242
+ const splicedDatas = datas.slice(0, this.spliceLength);
243
+ const selectedFactsCount = this.selectedFacts.length;
244
+ const allowCreation = !!this.bundleName && selectedFactsCount > 0;
245
+
246
+ return html`<tems-viewport>
247
+ <tems-header slot="header" heading=${this.header}>
248
+ <div slot="cta">
249
+ <tems-button
250
+ type="primary"
251
+ disabled=${!allowCreation || nothing}
252
+ label=${msg("Create bundle")}
253
+ @click=${this._createFactBundle}
254
+ ></tems-button>
255
+ </div>
256
+ </tems-header>
257
+ <div slot="content">
258
+ <section class="flex flex-1">
259
+ <div>
260
+ <div>
261
+ <tems-input
262
+ type="text"
263
+ label=${msg("Bundle name")}
264
+ placeholder=${msg("Bundle name")}
265
+ required=""
266
+ .value=${this.bundleName}
267
+ @change=${this._updateBundleName}
268
+ ></tems-input>
269
+ </div>
270
+ <div>
271
+ <tems-textarea
272
+ rows="4"
273
+ label=${msg("Bundle description")}
274
+ placeholder=${msg("Bundle description")}
275
+ .value=${this.bundleDescription}
276
+ @change=${this._updateBundleDescription}
277
+ ></tems-textarea>
278
+ </div>
279
+ ${selectedFactsCount > 0
280
+ ? html`<tems-division type="h4">
281
+ <div>
282
+ ${msg(
283
+ str`${selectedFactsCount} selected fact${selectedFactsCount > 1 ? "s" : ""}`,
284
+ )}
285
+ </div></tems-division
286
+ >
287
+ <div class="card-grid card-grid-vertical">
288
+ ${this.selectedFacts.map((fact: Resource) => {
289
+ const tags = fact.categories.map((c: Resource) => ({
290
+ name: c.name,
291
+ type: "information",
292
+ }));
293
+ if (tags.length > 3) {
294
+ const overflowTags = tags.length - 3;
295
+ tags.splice(3, overflowTags);
296
+ tags.push({
297
+ name: `+${overflowTags} ${msg("more")}`,
298
+ type: "information",
299
+ });
300
+ }
301
+ return html`<tems-card-catalog
302
+ .object=${import.meta.env.DEV ? fact : nothing}
303
+ .header=${fact.name || nothing}
304
+ date=${fact.updated_at || nothing}
305
+ @click=${this._toggleFactSelection.bind(
306
+ this,
307
+ fact,
308
+ )}
309
+ selected=${true}
310
+ ></tems-card-catalog>`;
311
+ })}
312
+ </div>`
313
+ : nothing}
314
+ </div>
315
+ ${splicedDatas.length > 0 || this.filterFactText
316
+ ? html`<div>
317
+ <div>
318
+ <tems-search-bar
319
+ .displayFilters=${false}
320
+ .displaySavedSearch=${false}
321
+ .displayViews=${false}
322
+ .dropdownCardElement=${false}
323
+ .dropdownListElement=${false}
324
+ .dropdownTableElement=${false}
325
+ .dropdownMapElement=${false}
326
+ .displayActiveTags=${false}
327
+ .value=${this.filterFactText}
328
+ @search=${this._searchFacts}
329
+ ></tems-search-bar>
330
+ </div>
331
+ ${splicedDatas.length > 0
332
+ ? html` <div class="card-grid card-grid-vertical">
333
+ ${splicedDatas.map((fact: Resource) => {
334
+ const tags = fact.categories.map(
335
+ (c: Resource) => ({
336
+ name: c.name,
337
+ type: "information",
338
+ }),
339
+ );
340
+ if (tags.length > 3) {
341
+ const overflowTags = tags.length - 3;
342
+ tags.splice(3, overflowTags);
343
+ tags.push({
344
+ name: `+${overflowTags} ${msg("more")}`,
345
+ type: "information",
346
+ });
347
+ }
348
+
349
+ return html`<tems-card-catalog
350
+ .object=${import.meta.env.DEV
351
+ ? fact
352
+ : nothing}
353
+ .tags=${tags}
354
+ .header=${fact.name || nothing}
355
+ .content=${fact.description || nothing}
356
+ date=${fact.updated_at || nothing}
357
+ @click=${this._toggleFactSelection.bind(
358
+ this,
359
+ fact,
360
+ )}
361
+ ></tems-card-catalog>`;
362
+ })}
363
+ </div>
364
+ ${splicedDatas.length < datas.length
365
+ ? html`<div
366
+ class="flex flex-row justify-content-space-between align-items-flex-end"
367
+ >
368
+ <div>
369
+ ${msg(
370
+ str`Displaying ${splicedDatas.length} of ${datas.length} result${datas.length > 1 ? "s" : ""}`,
371
+ )}
372
+ </div>
373
+ <div class="flex flex-row gap-400">
374
+ <tems-button
375
+ @click=${this._showMoreResults}
376
+ type="primary"
377
+ size="sm"
378
+ label=${msg("Show more results")}
379
+ ></tems-button>
380
+ <tems-button
381
+ @click=${this._showAllResults}
382
+ type="primary"
383
+ size="sm"
384
+ label=${msg("Show all results")}
385
+ ></tems-button>
386
+ </div>
387
+ </div>`
388
+ : nothing}`
389
+ : html`${msg("No results found")} ${this.filterFactText ? `for "${this.filterFactText}".` : ""}`}
390
+ </div>`
391
+ : nothing}
392
+ </section>
393
+ </div>
394
+ </tems-viewport>`;
395
+ },
396
+ })
397
+ );
398
+ }
399
+ }
@@ -1,7 +1,12 @@
1
1
  import { formatDate, OrbitComponent, requestNavigation, sort } from "@helpers";
2
2
  import { msg } from "@lit/localize";
3
3
  import { Task } from "@lit/task";
4
- import type { PropertiesPicker, Resource, SearchObject } from "@src/component";
4
+ import type {
5
+ OrbitComponent as OrbitComponentConfig,
6
+ PropertiesPicker,
7
+ Resource,
8
+ SearchObject,
9
+ } from "@src/component";
5
10
  import { css, html, nothing } from "lit";
6
11
  import { customElement, property, state } from "lit/decorators.js";
7
12
 
@@ -31,6 +36,9 @@ export class SolidFactBundle extends OrbitComponent {
31
36
  @state()
32
37
  resultCount = this.objects?.length || 0;
33
38
 
39
+ @state()
40
+ bundleCreationComponent: OrbitComponentConfig | undefined;
41
+
34
42
  cherryPickedProperties: PropertiesPicker[] = [
35
43
  { key: "created_at", value: "created_at", cast: formatDate },
36
44
  { key: "updated_at", value: "updated_at", cast: formatDate },
@@ -68,7 +76,6 @@ export class SolidFactBundle extends OrbitComponent {
68
76
  },
69
77
  ),
70
78
  );
71
- console.log(response.facts);
72
79
  }
73
80
  return Promise.resolve(response);
74
81
  }
@@ -78,6 +85,10 @@ export class SolidFactBundle extends OrbitComponent {
78
85
  `[uniq="${this.orbit?.getComponent("menu")?.uniq}"]`,
79
86
  );
80
87
 
88
+ this.bundleCreationComponent = this.orbit?.getComponent(
89
+ "fact-bundle-creation",
90
+ );
91
+
81
92
  return Promise.resolve();
82
93
  }
83
94
 
@@ -171,6 +182,13 @@ export class SolidFactBundle extends OrbitComponent {
171
182
  this.resultCount = e.detail ?? 0;
172
183
  }
173
184
 
185
+ _goToBundleCreation(e: Event) {
186
+ e.preventDefault();
187
+ if (this.bundleCreationComponent?.route) {
188
+ requestNavigation(this.bundleCreationComponent.route);
189
+ }
190
+ }
191
+
174
192
  render() {
175
193
  return (
176
194
  this.gatekeeper() ||
@@ -186,7 +204,9 @@ export class SolidFactBundle extends OrbitComponent {
186
204
  <div slot="cta">
187
205
  <tems-button
188
206
  type="primary"
207
+ disabled=${!this.bundleCreationComponent?.route || nothing}
189
208
  label=${msg("Create a bundle")}
209
+ @click=${this._goToBundleCreation}
190
210
  ></tems-button>
191
211
  </div>
192
212
  </tems-header>
@@ -205,8 +225,9 @@ export class SolidFactBundle extends OrbitComponent {
205
225
  @clicked=${this._openModal}
206
226
  @result-count=${this._resultCountUpdate}
207
227
  ></ds4go-fact-bundle-holder>
208
- ${this.object
209
- ? html`<div
228
+ ${
229
+ this.object
230
+ ? html`<div
210
231
  class="modal"
211
232
  @click=${this._closeModalFromBackground}
212
233
  >
@@ -215,7 +236,8 @@ export class SolidFactBundle extends OrbitComponent {
215
236
  @close=${this._closeModal}
216
237
  ></ds4go-fact-bundle-modal>
217
238
  </div>`
218
- : nothing}
239
+ : nothing
240
+ }
219
241
  </div>
220
242
  </tems-viewport>`;
221
243
  },
package/src/context.json CHANGED
@@ -1 +1,15 @@
1
- {}
1
+ {
2
+ "ds4go": "https://cdn.startinblox.com/owl/ds4go.jsonld#",
3
+ "link": {
4
+ "@id": "ds4go:link",
5
+ "@type": "@id"
6
+ },
7
+ "enclosure": {
8
+ "@id": "ds4go:enclosure",
9
+ "@type": "@id"
10
+ },
11
+ "url": {
12
+ "@id": "ds4go:url",
13
+ "@type": "@id"
14
+ }
15
+ }
@@ -19,19 +19,21 @@
19
19
  .half {
20
20
  flex-basis: 50%;
21
21
  }
22
- &.justify-content-center{
22
+ &.justify-content-center {
23
23
  justify-content: center;
24
24
  }
25
- &.justify-content-space-between{
25
+ &.justify-content-space-between {
26
26
  justify-content: space-between;
27
27
  }
28
- &.align-items-center{
28
+ &.align-items-center {
29
29
  align-items: center;
30
30
  }
31
- &.align-items-flex-start{
31
+ &.align-items-flex-start {
32
32
  align-items: flex-start;
33
33
  }
34
-
34
+ .align-items-flex-end {
35
+ align-items: flex-end;
36
+ }
35
37
  }
36
38
 
37
39
  .full-width {
@@ -83,7 +83,9 @@
83
83
 
84
84
  @media screen and (min-width: 1100px) {
85
85
  div.modal {
86
- width: 80vw;
87
- height: 80vh;
86
+ width: fit-content;
87
+ max-width: 80vw;
88
+ height: fit-content;
89
+ max-height: 80vw;
88
90
  }
89
91
  }