@kompasid/lit-web-components 0.9.19 → 0.9.21

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.
@@ -1,70 +1,23 @@
1
- /* eslint-disable lit-a11y/click-events-have-key-events */
2
- import { html, css, LitElement } from 'lit'
1
+ import { LitElement, html, css } from 'lit'
3
2
  import { customElement, property, state } from 'lit/decorators.js'
3
+ import { repeat } from 'lit/directives/repeat.js'
4
4
  import { unsafeSVG } from 'lit/directives/unsafe-svg.js'
5
5
  import { TWStyles } from '../../../tailwind/tailwind.js'
6
6
  import { getFontAwesomeIcon } from '../../utils/fontawesome-setup.js'
7
7
  import { decodeSpecialChars } from '../../utils/decodeSpecialChars.js'
8
8
  import { timedContent } from '../../utils/timedContent.js'
9
9
 
10
- interface DataExternalLink {
11
- external?: boolean
12
- gtmClass?: string
13
- icon: object | null
14
- iconify: string | null
15
- isNew: boolean
16
- name: string
17
- url: string
18
- }
19
-
20
- interface dataType {
21
- href: string
22
- external?: boolean
23
- icon: object | null
24
- iconify: string | null
25
- name: string
26
- slug: string
27
- redDot: [
28
- {
29
- start: string
30
- end: string
31
- }
32
- ]
33
- children: [
34
- {
35
- href: string
36
- external: boolean
37
- icon: string
38
- iconify: string | null
39
- name: string
40
- slug: string
41
- redDot: [
42
- {
43
- start: string
44
- end: string
45
- }
46
- ]
47
- }
48
- ]
49
- }
50
- interface DataSideBarLink {
51
- feature: dataType[]
52
- category: dataType[]
53
- lainnya: dataType[]
54
- }
10
+ // Importing the data fetch functions
11
+ import {
12
+ fetchExternalLinks,
13
+ fetchSidebarData,
14
+ DataExternalLink,
15
+ DataSideBarLink,
16
+ DataSideBarItem,
17
+ } from './SidebarDataController.js'
55
18
 
56
19
  @customElement('kompasid-menu-side-bar')
57
20
  export class KompasMenuSideBar extends LitElement {
58
- hasSlotContent = false
59
- shadowRoot: any
60
-
61
- firstUpdated() {
62
- const slot = this.shadowRoot?.querySelector('slot')
63
- const assignedNodes = slot!.assignedNodes({ flatten: true })
64
-
65
- this.hasSlotContent = assignedNodes.length > 0
66
- }
67
-
68
21
  static styles = [
69
22
  css`
70
23
  .slide-side-enter-active,
@@ -86,228 +39,107 @@ export class KompasMenuSideBar extends LitElement {
86
39
  .menu-menu-sidebar::-webkit-scrollbar {
87
40
  width: 4px;
88
41
  }
89
-
90
42
  .menu-menu-sidebar::-webkit-scrollbar-track {
91
43
  background: white;
92
44
  }
93
-
94
45
  .menu-menu-sidebar::-webkit-scrollbar-thumb {
95
46
  background-color: #00557d; /* Replace with your brand color */
96
47
  border-radius: 8px;
97
48
  }
98
-
99
49
  .menu-menu-sidebar::-webkit-scrollbar-button,
100
50
  .menu-menu-sidebar::-webkit-scrollbar-corner {
101
51
  background-color: white;
102
52
  }
53
+ /* existing styles */
103
54
  `,
104
55
  TWStyles,
105
56
  ]
106
- @property({ type: Array }) dataExternal: DataExternalLink[] = []
107
- @property({ type: Boolean }) isDark = false
108
57
 
109
- async connectedCallback() {
110
- super.connectedCallback()
111
- try {
112
- await this.fetchExternal()
113
- } catch (error) {
114
- this.handleFetchError(error)
115
- }
116
- }
58
+ /**
59
+ * Props
60
+ * property kompasProSubscription untuk menghandle apakah user sudah login atau belum
61
+ */
117
62
 
118
- handleFetchError(error: unknown) {
119
- const errorMessage =
120
- error instanceof Error ? error.message : 'Kesalahan tidak diketahui'
121
- alert(`Terjadi kesalahan: ${errorMessage}`)
122
- }
63
+ @property({ type: Boolean }) isDark = false
64
+ @property({ type: Boolean }) isProductionMode = false
65
+ @property({ type: String }) subscriptionPackage = ''
123
66
 
124
- dataSidebar: DataSideBarLink = {
67
+ @state() dataExternal: DataExternalLink[] = []
68
+ @state() dataSidebar: DataSideBarLink = {
69
+ bundles: [],
125
70
  feature: [],
126
71
  category: [],
127
72
  lainnya: [],
128
73
  }
74
+ @state() showNavBar = false
75
+ @state() expandedSlug: string | null = null
76
+ hasSlotContent = false
129
77
 
130
- async fetchExternal() {
131
- // External
132
- const endpointExternal = `https://cdn-dev-www.kompas.id/assets/json/ApiMenuExternalLinkV2.json`
133
- const response = await fetch(endpointExternal, {
134
- headers: {
135
- 'Content-Type': 'application/json',
136
- },
137
- })
138
- const resultExternal = await response.json()
139
- // eslint-disable-next-line no-undef
140
- if (!resultExternal || !Array.isArray(resultExternal)) {
141
- console.error(
142
- 'Error: resultExternal.result is undefined or not an array',
143
- resultExternal
144
- )
145
- this.dataExternal = [] // Ensure dataExternal is an empty array instead of undefined
146
- } else {
147
- this.dataExternal = resultExternal.map(
148
- (externalLink: Partial<DataExternalLink>) => ({
149
- external: externalLink.external ?? false,
150
- gtmClass: externalLink.gtmClass ?? '',
151
- icon: externalLink.icon ?? null,
152
- iconify: externalLink.iconify ?? null,
153
- isNew: externalLink.isNew ?? false,
154
- name: externalLink.name ?? '',
155
- url: externalLink.url ?? '',
156
- })
157
- )
158
- }
159
-
160
- // Sidebar
161
- const endpointSidebar = `https://cdn-dev-www.kompas.id/assets/json/ApiMenuSideV3.json`
162
- const responseSidebar = await fetch(endpointSidebar, {
163
- headers: {
164
- 'Content-Type': 'application/json',
165
- },
166
- })
167
- const resultSidebar = await responseSidebar.json()
168
- // Validate the structure of the response
169
- if (!resultSidebar || typeof resultSidebar !== 'object') {
170
- console.error('Invalid response format:', resultSidebar)
171
- return
172
- }
173
-
174
- // Convert object to an array
175
- const sidebarArray = Object.values(resultSidebar)
176
- const [featureArray, categoryArray, othersArray] = sidebarArray as [
177
- any[],
178
- any[],
179
- any[]
180
- ]
181
- const features: dataType[] =
182
- featureArray?.map((item: any) => ({
183
- href: item?.href ?? '',
184
- external: item?.external ?? false,
185
- icon: item?.icon ?? null,
186
- iconify: item?.iconify ?? null,
187
- name: item?.name ?? '',
188
- slug: item?.slug ?? '',
189
- redDot: [
190
- {
191
- start: item?.redDot?.start ?? '',
192
- end: item?.redDot?.end ?? '',
193
- },
194
- ],
195
- children: Array.isArray(item?.children)
196
- ? item.children.map((child: any) => ({
197
- href: child?.href ?? '',
198
- external: child?.external ?? false,
199
- icon: child?.icon ?? '',
200
- iconify: child?.iconify ?? '',
201
- name: child?.name ?? '',
202
- slug: child?.slug ?? '',
203
- redDot: [
204
- {
205
- start: child?.redDot?.start ?? '',
206
- end: child?.redDot?.end ?? '',
207
- },
208
- ],
209
- }))
210
- : [],
211
- })) ?? []
78
+ // Fetch data when the component is connected to the DOM
79
+ async connectedCallback() {
80
+ super.connectedCallback()
81
+ await this.loadData()
82
+ }
212
83
 
213
- // Map category data
214
- const categories: dataType[] =
215
- categoryArray?.map((item: any) => ({
216
- href: item?.href ?? '',
217
- external: item?.external ?? false,
218
- icon: item?.icon ?? null,
219
- iconify: item?.iconify ?? null,
220
- name: item?.name ?? '',
221
- slug: item?.slug ?? '',
222
- redDot: [
223
- {
224
- start: item?.redDot?.start ?? '',
225
- end: item?.redDot?.end ?? '',
226
- },
227
- ],
228
- children: Array.isArray(item?.children)
229
- ? item.children.map((child: any) => ({
230
- href: child?.href ?? '',
231
- external: child?.external ?? false,
232
- icon: child?.icon ?? '',
233
- iconify: child?.iconify ?? '',
234
- name: child?.name ?? '',
235
- slug: child?.slug ?? '',
236
- redDot: [
237
- {
238
- start: child?.redDot?.start ?? '',
239
- end: child?.redDot?.end ?? '',
240
- },
241
- ],
242
- }))
243
- : [],
244
- })) ?? []
84
+ private hasKompasOnePackage(): boolean {
85
+ return this.subscriptionPackage
86
+ ?.split(' ')
87
+ .some(pkg => pkg.includes('kompas-one'))
88
+ }
245
89
 
246
- // Map others data
247
- const others: dataType[] =
248
- othersArray?.map((item: any) => ({
249
- href: item?.href ?? '',
250
- external: item?.external ?? false,
251
- icon: item?.icon ?? null,
252
- iconify: item?.iconify ?? null,
253
- name: item?.name ?? '',
254
- slug: item?.slug ?? '',
255
- redDot: [
256
- {
257
- start: item?.redDot?.start ?? '',
258
- end: item?.redDot?.end ?? '',
259
- },
260
- ],
261
- children: Array.isArray(item?.children)
262
- ? item.children.map((child: any) => ({
263
- href: child?.href ?? '',
264
- external: child?.external ?? false,
265
- icon: child?.icon ?? '',
266
- iconify: child?.iconify ?? '',
267
- name: child?.name ?? '',
268
- slug: child?.slug ?? '',
269
- redDot: [
270
- {
271
- start: child?.redDot?.start ?? '',
272
- end: child?.redDot?.end ?? '',
273
- },
274
- ],
275
- }))
276
- : [],
277
- })) ?? []
90
+ private hasKompasProPackage(): boolean {
91
+ return this.subscriptionPackage
92
+ ?.split(' ')
93
+ .some(pkg => pkg.includes('kompas-pro'))
94
+ }
278
95
 
279
- this.dataSidebar = {
280
- feature: features,
281
- category: categories,
282
- lainnya: others,
283
- }
284
- this.requestUpdate()
96
+ private filterBundles(bundles: DataSideBarItem[]): DataSideBarItem[] {
97
+ return bundles
98
+ .filter(b => b.isShow)
99
+ .map(b => {
100
+ if (b.name === 'Kompas One') {
101
+ return {
102
+ ...b,
103
+ children: this.hasKompasOnePackage() ? b.children : [],
104
+ }
105
+ }
106
+ if (b.name === 'Kompas Pro') {
107
+ return {
108
+ ...b,
109
+ children: this.hasKompasProPackage() ? b.children : [],
110
+ }
111
+ }
112
+ return b
113
+ })
285
114
  }
286
115
 
287
- renderChips() {
288
- const chips = []
289
- chips.push(
290
- html`
291
- <div class="flex">
292
- <div
293
- class="py-0.5 px-1.5 rounded-full"
294
- style="position: relative; display: inline-flex; background-color:#D71920;"
295
- >
296
- <span class="font-bold font-sans text-xs text-white capitalize"
297
- >Baru</span
298
- >
299
- </div>
300
- </div>
301
- `
302
- )
303
- return chips
116
+ // Function to load the data using async API calls
117
+ private async loadData() {
118
+ try {
119
+ const [extData, sbData] = await Promise.all([
120
+ fetchExternalLinks(this.isProductionMode),
121
+ fetchSidebarData(this.isProductionMode),
122
+ ])
123
+
124
+ this.dataExternal = extData
125
+ this.dataSidebar = {
126
+ ...sbData,
127
+ bundles: this.filterBundles(sbData.bundles),
128
+ }
129
+ } catch (error) {
130
+ console.error('Error loading data:', error)
131
+ }
304
132
  }
305
133
 
306
- hasChildren(item: any): boolean {
307
- return Array.isArray(item?.children) && item.children.length > 0
134
+ private toggleNavSidebar(e: Event) {
135
+ e.stopPropagation()
136
+ this.showNavBar = !this.showNavBar
308
137
  }
309
138
 
310
- rubricClicked(item: { name: string; href?: string }, event?: Event): void {
139
+ private rubricClicked(
140
+ item: { name: string; href?: string },
141
+ event?: Event
142
+ ): void {
311
143
  if (event) {
312
144
  event.stopPropagation() // Prevent parent click event
313
145
  }
@@ -318,62 +150,191 @@ export class KompasMenuSideBar extends LitElement {
318
150
  // add data layer here
319
151
  }
320
152
 
321
- @state()
322
- private expandedSlug: string | null = null
323
- private toggleChildren(item: any) {
153
+ private hasChildren(item: DataSideBarItem): boolean {
154
+ return Array.isArray(item.children) && item.children.length > 0
155
+ }
156
+
157
+ private toggleChildren(item: DataSideBarItem) {
324
158
  this.expandedSlug = this.expandedSlug === item.slug ? null : item.slug
325
159
  }
326
160
 
327
- @state()
328
- private showNavBar: boolean = false
161
+ private renderItem(item: any, padClass: string) {
162
+ return html`
163
+ <div class="w-full font-sans text-black">
164
+ <div
165
+ class="flex items-center justify-between text-sm font-medium ${padClass} transition-all cursor-pointer"
166
+ role="button"
167
+ tabindex="0"
168
+ @click=${(e: Event) => this.rubricClicked(item, e)}
169
+ @keydown=${(e: KeyboardEvent) => {
170
+ if (e.key === 'Enter') this.rubricClicked(item, e)
171
+ }}
172
+ >
173
+ <div
174
+ class="w-[216px] hover:bg-[#f3f4f6] rounded h-12 flex items-center space-x-3"
175
+ >
176
+ ${item.iconSrc
177
+ ? html`<div class="flex">
178
+ <img
179
+ src="${item.iconSrc}"
180
+ alt="Kompas.id"
181
+ scale="0"
182
+ class="block w-5"
183
+ />
184
+ </div>`
185
+ : ''}
186
+ <span class="font-bold">${decodeSpecialChars(item.name)}</span>
187
+ ${timedContent(item.redDot.start ?? '', item.redDot.end ?? '')
188
+ ? html`<span
189
+ class="bg-orange-400 h‑2 w‑2 rounded-full relative -top-[12px] shrink-0"
190
+ ></span>`
191
+ : ''}
192
+ </div>
193
+ ${this.hasChildren(item)
194
+ ? html`
195
+ <span
196
+ class="flex justify-center items-center rounded my‑1 py‑4 w‑10 h‑10 cursor‑pointer text-grey-400"
197
+ role="button"
198
+ tabindex="0"
199
+ @click=${(e: Event) => {
200
+ e.stopPropagation()
201
+ this.toggleChildren(item)
202
+ }}
203
+ @keydown=${(e: KeyboardEvent) => {
204
+ if (e.key === 'Enter') {
205
+ e.stopPropagation()
206
+ this.toggleChildren(item)
207
+ }
208
+ }}
209
+ >
210
+ ${unsafeSVG(
211
+ getFontAwesomeIcon(
212
+ 'fas',
213
+ this.expandedSlug === item.slug
214
+ ? 'chevron-up'
215
+ : 'chevron-down',
216
+ 12,
217
+ 12
218
+ )
219
+ )}
220
+ </span>
221
+ `
222
+ : null}
223
+ </div>
329
224
 
330
- toggleNavSidebar = (e: Event) => {
331
- e.stopPropagation() // prevent bubbling
332
- this.showNavBar = !this.showNavBar
225
+ ${this.hasChildren(item) && this.expandedSlug === item.slug
226
+ ? html`<div
227
+ class="${padClass.includes('px-6')
228
+ ? 'pt‑1 pb‑2 space-y‑1 text-black'
229
+ : 'pl‑14 pt‑1 pb‑2 space-y‑1 text-black'}"
230
+ >
231
+ ${repeat(
232
+ item.children ?? [],
233
+ (c: DataSideBarItem) => c.slug,
234
+ (child: DataSideBarItem) => html`
235
+ <div
236
+ role="button"
237
+ tabindex="0"
238
+ class="flex items-center justify-between text-sm font-medium px-6 transition-all cursor-pointer"
239
+ @click=${() => this.rubricClicked(child)}
240
+ @keydown=${(e: KeyboardEvent) => {
241
+ if (e.key === 'Enter') this.rubricClicked(child, e)
242
+ }}
243
+ >
244
+ <div
245
+ class="w-[216px] hover:bg-[#f3f4f6] rounded h-12 flex items-center pl-8"
246
+ >
247
+ ${decodeSpecialChars(child.name)}
248
+ ${timedContent(
249
+ child.redDot.start ?? '',
250
+ child.redDot.end ?? ''
251
+ )
252
+ ? html`<span
253
+ class="bg-orange-400 h‑2 w‑2 rounded-full relative -top-[12px] shrink-0"
254
+ ></span>`
255
+ : ''}
256
+ </div>
257
+ <span class="ml-auto text-grey-400">
258
+ ${child.external
259
+ ? unsafeSVG(
260
+ getFontAwesomeIcon('fas', 'external-link', 16, 16)
261
+ )
262
+ : ''}
263
+ </span>
264
+ </div>
265
+ `
266
+ )}
267
+ </div>`
268
+ : ''}
269
+ </div>
270
+ `
271
+ }
272
+
273
+ private renderSection(title: string | null, items: any[], padClass: string) {
274
+ if (!items || items.length === 0) return null // skip if empty data
275
+ return html`
276
+ ${title
277
+ ? html`<span class="text-sm text-grey-400 ${padClass} font-normal"
278
+ >${title}</span
279
+ >`
280
+ : null}
281
+ ${repeat(
282
+ items,
283
+ item => item.slug,
284
+ item => this.renderItem(item, padClass)
285
+ )}
286
+ `
287
+ }
288
+
289
+ private renderChips() {
290
+ return html`
291
+ <div
292
+ class="py-0.5 px-1.5 rounded-full bg-[#D71920] inline-flex"
293
+ style="background-color:#D71920;"
294
+ >
295
+ <span class="font-bold font-sans text-xs text-white capitalize"
296
+ >Baru</span
297
+ >
298
+ </div>
299
+ `
333
300
  }
334
301
 
335
302
  render() {
336
303
  return html`
337
- <!-- Button Menu -->
304
+ <!-- Toggle Button -->
338
305
  <div
306
+ role="button"
307
+ tabindex="0"
339
308
  class="w-fit flex items-center justify-center cursor-pointer relative"
340
309
  @click=${this.toggleNavSidebar}
310
+ @keydown=${(e: KeyboardEvent) => {
311
+ if (e.key === 'Enter') this.toggleNavSidebar(e)
312
+ }}
341
313
  >
342
314
  <slot></slot>
343
- ${!this.hasSlotContent
344
- ? html`
345
- <div
346
- class="h-4 inline-flex ${this.isDark
347
- ? 'text-[#FFFFFF]'
348
- : 'text-brand-1'}"
349
- >
350
- ${unsafeSVG(getFontAwesomeIcon('fas', 'bars'))}
351
- </div>
352
- <span
353
- class="font-sans hidden sm:inline ml-2 tracking-wide font-bold ${this
354
- .isDark
355
- ? 'text-[#FFFFFF]'
356
- : 'text-brand-1'}"
357
- >
358
- Menu
359
- </span>
360
- `
361
- : ''}
315
+ <div class="h-4 inline-flex text-brand-1">
316
+ ${unsafeSVG(getFontAwesomeIcon('fas', 'bars', 20, 20))}
317
+ </div>
318
+ <span
319
+ class="font-sans hidden sm:inline ml-2 tracking-wide font-bold text-brand-1"
320
+ >
321
+ Menu
322
+ </span>
362
323
  </div>
363
- <!-- Side Menu -->
324
+ <!-- Sidebar Menu -->
364
325
  <nav
365
- @click=${this.toggleNavSidebar}
366
326
  class=${this.showNavBar
367
327
  ? 'fixed left-0 top-0 w-screen z-[100]'
368
328
  : 'hidden'}
369
329
  >
330
+ <!-- Sidebar Content -->
370
331
  <div
371
- ref="toggle-nav-sidebar"
372
- class="bg-white h-screen menu-menu-sidebar overflow-y-auto pb-20 pt-0 shadow-lg"
373
- style="width: 312px;"
332
+ class="bg-white h-screen menu-menu-sidebar overflow-y-auto pb-20 shadow-lg"
333
+ style="width:312px;"
374
334
  >
335
+ <!-- Logo and Close Button -->
375
336
  <div
376
- class="bg-[#FFFFFF] flex flex-col items-center justify-center mb-6 w-full"
337
+ class="bg-[#FFFFFF] flex flex-col items-center justify-center w-full"
377
338
  >
378
339
  <div
379
340
  ref="logo-kompas"
@@ -388,343 +349,74 @@ export class KompasMenuSideBar extends LitElement {
388
349
  />
389
350
  </a>
390
351
  <span
352
+ role="button"
353
+ tabindex="0"
391
354
  class="font-bold cursor-pointer text-grey-400 flex h-10 items-center justify-center rounded text-base w-10 py-4"
392
355
  @click=${this.toggleNavSidebar}
356
+ @keydown=${(e: KeyboardEvent) => {
357
+ if (e.key === 'Enter') this.toggleNavSidebar(e)
358
+ }}
393
359
  >
394
360
  ${unsafeSVG(getFontAwesomeIcon('fa', 'times', 20, 20))}
395
361
  </span>
396
362
  </div>
397
- <div class="flex flex-wrap px-6 w-full">
398
- ${this.dataExternal.map(
399
- item => html`
400
- <a href="${item.url}" class="flex w-1/2 no-underline">
401
- <div
402
- class="cursor-pointer flex items-center pb-4 w-[312px]"
403
- >
404
- ${item.icon &&
405
- Array.isArray(item.icon) &&
406
- item.icon.length >= 2
407
- ? html`
408
- <div class="flex mr-2 text-brand-1">
409
- ${unsafeSVG(
410
- getFontAwesomeIcon(item.icon[0], item.icon[1])
411
- )}
412
- </div>
413
- `
414
- : ''}
415
- <span class="font-sans relative text-xs text-[#666666]">
416
- ${item.name}
417
- </span>
418
- <div class="ml-1">
419
- ${item.isNew ? this.renderChips() : ''}
420
- </div>
421
- </div>
422
- </a>
423
- `
424
- )}
425
- </div>
426
363
  </div>
427
- <div class="border-b border-[#DDD] m-6 "></div>
428
- <!-- feature -->
429
- <div class="flex">
430
- <div class="flex justify-between flex-col">
431
- ${this.dataSidebar.feature.map(
432
- item => html`
433
- <div class="w-full font-sans">
434
- <!-- Parent item -->
435
- <div
436
- class="flex items-center justify-between px-6 text-sm font-medium text-gray-700 transition-all cursor-pointer"
437
- @click=${(e: Event) => this.rubricClicked(item, e)}
438
- >
439
- <div
440
- class="w-[216px] hover:bg-[#f3f4f6] rounded h-12 flex items-center"
441
- >
442
- <div class="flex items-center space-x-3">
443
- <span
444
- class="text-sm font-bold relative text-[#333] w-full"
445
- >${decodeSpecialChars(item.name)}</span
446
- >
447
- ${timedContent(
448
- item.redDot[0].start,
449
- item.redDot[0].end
450
- )
451
- ? html`<span
452
- class="bg-orange-400 h-2 relative rounded-full w-2 flex shrink-0 -top-[12px]"
453
- ></span>`
454
- : ''}
455
- </div>
456
- </div>
457
-
458
- <!-- Toggle chevron -->
459
- ${this.hasChildren(item)
460
- ? html`
461
- <span
462
- class="text-xs text-brand-1 bg-[#e1f0ff] flex justify-center items-center rounded my-1 p-4 w-10 h-10 cursor-pointer"
463
- @click=${(e: Event) => {
464
- e.stopPropagation() // Prevents click from bubbling to parent
465
- this.toggleChildren(item)
466
- }}
467
- >
468
- ${this.expandedSlug === item.slug
469
- ? unsafeSVG(
470
- getFontAwesomeIcon(
471
- 'fas',
472
- 'chevron-up',
473
- 12,
474
- 12
475
- )
476
- )
477
- : unsafeSVG(
478
- getFontAwesomeIcon(
479
- 'fas',
480
- 'chevron-down',
481
- 12,
482
- 12
483
- )
484
- )}
485
- </span>
486
- `
487
- : null}
488
- </div>
489
-
490
- <!-- Children items -->
491
- ${this.hasChildren(item) && this.expandedSlug === item.slug
492
- ? html`
493
- <div class="pl-14 pt-1 pb-2 space-y-1">
494
- ${item.children.map(
495
- child => html`
496
- <div
497
- class="flex items-center text-sm text-[#333] px-4 cursor-pointer transition-all"
498
- @click=${() => this.rubricClicked(child)}
499
- >
500
- <div
501
- class="w-[216px] hover:bg-[#f3f4f6] rounded h-12 flex items-center pl-11"
502
- >
503
- ${decodeSpecialChars(child.name)}
504
- ${timedContent(
505
- child.redDot[0].start,
506
- child.redDot[0].end
507
- )
508
- ? html`<span
509
- class="bg-orange-400 h-2 relative rounded-full w-2 flex shrink-0 -top-[12px]"
510
- ></span>`
511
- : ''}
512
- <div></div>
513
- </div>
514
- </div>
515
- `
516
- )}
517
- </div>
518
- `
364
+ <!-- External Links -->
365
+ <div class="flex flex-wrap px-6">
366
+ ${repeat(
367
+ this.dataExternal,
368
+ item => item.name,
369
+ item => html`
370
+ <a href="${item.url}" class="flex w-1/2 no-underline px-2">
371
+ <div class="cursor-pointer flex items-center pb-4">
372
+ ${item.icon &&
373
+ Array.isArray(item.icon) &&
374
+ item.icon.length >= 2
375
+ ? html`<div class="flex mr-2 text-brand-1">
376
+ ${unsafeSVG(
377
+ getFontAwesomeIcon(item.icon[0], item.icon[1])
378
+ )}
379
+ </div>`
519
380
  : ''}
520
- </div>
521
- `
522
- )}
523
- </div>
524
- </div>
525
- <div class="border-b border-[#DDD] m-6 "></div>
526
- <!-- category -->
527
- <div class="flex">
528
- <div class="w-full flex justify-between flex-col">
529
- <span class="text-sm text-grey-400 px-6 font-normal"
530
- >Redaksional</span
531
- >
532
- ${this.dataSidebar.category.map(
533
- item => html`
534
- <div class="w-full font-sans">
535
- <!-- Parent item -->
536
- <div
537
- class="flex items-center justify-between text-sm font-medium px-6 transition-all cursor-pointer"
538
- @click=${(e: Event) => this.rubricClicked(item, e)}
381
+ <span class="font-sans text-xs text-[#666666]"
382
+ >${item.name}</span
539
383
  >
540
- <div
541
- class="w-[216px] hover:bg-[#f3f4f6] rounded h-12 flex items-center"
542
- >
543
- <div class="flex items-center space-x-3">
544
- <span
545
- class="font-bold ${item.name === 'Beranda'
546
- ? 'text-[#00559a]'
547
- : 'text-[#333] w-full'}"
548
- >${decodeSpecialChars(item.name)}</span
549
- >
550
-
551
- ${timedContent(
552
- item.redDot[0].start,
553
- item.redDot[0].end
554
- )
555
- ? html`<span
556
- class="bg-orange-400 h-2 relative rounded-full w-2 flex shrink-0 -top-[12px]"
557
- ></span>`
558
- : ''}
559
- </div>
560
- </div>
561
-
562
- <!-- Toggle chevron -->
563
- ${this.hasChildren(item)
564
- ? html`
565
- <span
566
- class="flex justify-center items-center rounded my-1 py-4 w-10 h-10 cursor-pointer font-bold text-grey-400"
567
- @click=${(e: Event) => {
568
- e.stopPropagation() // Prevents click from bubbling to parent
569
- this.toggleChildren(item)
570
- }}
571
- >
572
- ${this.expandedSlug === item.slug
573
- ? unsafeSVG(
574
- getFontAwesomeIcon(
575
- 'fas',
576
- 'chevron-up',
577
- 12,
578
- 12
579
- )
580
- )
581
- : unsafeSVG(
582
- getFontAwesomeIcon(
583
- 'fas',
584
- 'chevron-down',
585
- 12,
586
- 12
587
- )
588
- )}
589
- </span>
590
- `
591
- : null}
592
- </div>
593
-
594
- <!-- Children items -->
595
- ${this.hasChildren(item) && this.expandedSlug === item.slug
596
- ? html`
597
- <div class="pt-1 pb-2 space-y-1">
598
- ${item.children.map(
599
- child => html`
600
- <div
601
- class="flex items-center text-sm text-[#333] px-4 cursor-pointer transition-all"
602
- @click=${() => this.rubricClicked(child)}
603
- >
604
- <div
605
- class="w-[216px] hover:bg-[#f3f4f6] rounded h-12 flex items-center pl-11"
606
- >
607
- ${decodeSpecialChars(child.name)}
608
- ${timedContent(
609
- child.redDot[0].start,
610
- child.redDot[0].end
611
- )
612
- ? html`<span
613
- class="bg-orange-400 h-2 relative rounded-full w-2 flex shrink-0 -top-[12px]"
614
- ></span>`
615
- : ''}
616
- </div>
617
- </div>
618
- `
619
- )}
620
- </div>
621
- `
384
+ ${item.isNew
385
+ ? html`<span class="ml-1">${this.renderChips()}</span>`
622
386
  : ''}
623
387
  </div>
624
- `
625
- )}
626
- </div>
627
- </div>
628
-
629
- <div class="border-b border-[#DDD] m-6 "></div>
630
- <!-- Lainnya -->
631
- <div class="flex">
632
- <div class="w-full flex justify-between flex-col">
633
- <span class="text-sm text-grey-400 px-6 font-normal"
634
- >Lainnya</span
635
- >
636
- ${this.dataSidebar.lainnya.map(
637
- item => html`
638
- <div class="w-full font-sans">
639
- <!-- Parent item -->
640
- <div
641
- class="flex items-center justify-between text-sm font-medium px-6 transition-all cursor-pointer"
642
- @click=${(e: Event) => this.rubricClicked(item, e)}
643
- >
644
- <div
645
- class="w-[216px] hover:bg-[#f3f4f6] rounded h-12 flex items-center"
646
- >
647
- <div class="flex items-center space-x-3">
648
- <span class="font-bold text-[#333]"
649
- >${decodeSpecialChars(item.name)}</span
650
- >
651
- ${timedContent(
652
- item.redDot[0].start,
653
- item.redDot[0].end
654
- )
655
- ? html`<span
656
- class="bg-orange-400 h-2 relative rounded-full w-2 flex shrink-0 -top-[12px]"
657
- ></span>`
658
- : ''}
659
- </div>
660
- </div>
661
-
662
- <!-- Toggle chevron -->
663
- ${this.hasChildren(item)
664
- ? html`
665
- <span
666
- class="flex justify-center items-center rounded my-1 py-4 w-10 h-10 cursor-pointer font-bold"
667
- @click=${(e: Event) => {
668
- e.stopPropagation() // Prevents click from bubbling to parent
669
- this.toggleChildren(item)
670
- }}
671
- >
672
- ${this.expandedSlug === item.slug
673
- ? unsafeSVG(
674
- getFontAwesomeIcon(
675
- 'fas',
676
- 'chevron-up',
677
- 12,
678
- 12
679
- )
680
- )
681
- : unsafeSVG(
682
- getFontAwesomeIcon(
683
- 'fas',
684
- 'chevron-down',
685
- 12,
686
- 12
687
- )
688
- )}
689
- </span>
690
- `
691
- : null}
692
- </div>
693
-
694
- <!-- Children items -->
695
- ${this.hasChildren(item) && this.expandedSlug === item.slug
696
- ? html`
697
- <div class="pt-1 pb-2 space-y-1">
698
- ${item.children.map(
699
- child => html`
700
- <div
701
- class="flex items-center text-sm text-[#333] px-4 cursor-pointer transition-all"
702
- @click=${() => this.rubricClicked(child)}
703
- >
704
- <div
705
- class="w-[216px] hover:bg-[#f3f4f6] rounded h-12 flex items-center pl-11"
706
- >
707
- ${decodeSpecialChars(child.name)}
708
- ${timedContent(
709
- child.redDot[0].start,
710
- child.redDot[0].end
711
- )
712
- ? html`<span
713
- class="bg-orange-400 h-2 relative rounded-full w-2 flex shrink-0 -top-[12px]"
714
- ></span>`
715
- : ''}
716
- </div>
717
- </div>
718
- `
719
- )}
720
- </div>
721
- `
722
- : ''}
723
- </div>
724
- `
725
- )}
726
- </div>
388
+ </a>
389
+ `
390
+ )}
727
391
  </div>
392
+ ${this.dataExternal.length > 0
393
+ ? html`<div class="border-b border-[#DDD] mx-6 my-4"></div>`
394
+ : null}
395
+
396
+ <!-- Bundle Section -->
397
+ ${this.renderSection(null, this.dataSidebar.bundles, 'px-6')}
398
+ ${this.dataSidebar.bundles.length > 0
399
+ ? html`<div class="border-b border-[#DDD] mx-6 my-4"></div>`
400
+ : null}
401
+
402
+ <!-- Feature Section -->
403
+ ${this.renderSection(null, this.dataSidebar.feature, 'px-6')}
404
+ ${this.dataSidebar.feature.length > 0
405
+ ? html`<div class="border-b border-[#DDD] mx-6 my-4"></div>`
406
+ : null}
407
+
408
+ <!-- Category (Redaksional) -->
409
+ ${this.renderSection(
410
+ 'Redaksional',
411
+ this.dataSidebar.category,
412
+ 'px-6'
413
+ )}
414
+ ${this.dataSidebar.category.length > 0
415
+ ? html`<div class="border-b border-[#DDD] mx-6 my-4"></div>`
416
+ : null}
417
+
418
+ <!-- Others (Lainnya) -->
419
+ ${this.renderSection('Lainnya', this.dataSidebar.lainnya, 'px-6')}
728
420
  </div>
729
421
  </nav>
730
422
  `