diff --git a/src/App.vue b/src/App.vue index 638314cd..1c510849 100644 --- a/src/App.vue +++ b/src/App.vue @@ -25,6 +25,8 @@ id="main-content" class="content" > + + { + searchResultStore.TVFacets = response.data.facet_counts.facet_fields.creator_affiliation_facet.filter( + (entry) => typeof entry === 'string', + ); + }); + APIService.getRadioFacets().then((response) => { + searchResultStore.RadioFacets = response.data.facet_counts.facet_fields.creator_affiliation_facet.filter( + (entry) => typeof entry === 'string', + ); + }); let facetAPiCallFurfilled = false; // timeout promise for long responsetimes. (7 seconds pt.) const maximumWaitTime = new Promise((resolve) => { @@ -148,9 +165,28 @@ export default defineComponent({ Promise.race([facetAPICall, maximumWaitTime]) .then((response) => { if (response) { + const isTimeFiltersSet = + startDate.value.getTime() !== startYear.value.getTime() || + endDate.value.getTime() !== endYear.value.getTime(); + //if api comes back first, we get the results and set our boolean to true. const typedResponse = response as APISearchResponseType; searchResultStore.initFacets = typedResponse.data.facet_counts; + const years = searchResultStore.initFacets.facet_fields.temporal_start_year; + + startYear.value = new Date(startYear.value); + startYear.value.setFullYear(Number(years[0])); + + endYear.value = new Date(endYear.value); + endYear.value.setFullYear(Number(years[years.length - 2])); + + if (!isTimeFiltersSet) { + const startHolder = new Date(startYear.value.getTime()); + const endHolder = new Date(endYear.value.getTime()); + startDate.value = startHolder; + endDate.value = endHolder; + } + searchResultStore.firstBackendFetchExecuted = true; facetAPiCallFurfilled = true; } @@ -161,8 +197,26 @@ export default defineComponent({ // if the timeout was first, we still want the api promise returned here // unless it the results were already back - then we ignore this. if (!facetAPiCallFurfilled) { + const isTimeFiltersSet = + startDate.value.getTime() !== startYear.value.getTime() || + endDate.value.getTime() !== endYear.value.getTime(); const typedResponse = response as APISearchResponseType; searchResultStore.initFacets = typedResponse.data.facet_counts; + const years = searchResultStore.initFacets.facet_fields.temporal_start_year; + + startYear.value = new Date(startYear.value); + startYear.value.setFullYear(Number(years[0])); + + endYear.value = new Date(endYear.value); + endYear.value.setFullYear(Number(years[years.length - 2])); + + if (!isTimeFiltersSet) { + const startHolder = new Date(startYear.value.getTime()); + const endHolder = new Date(endYear.value.getTime()); + startDate.value = startHolder; + endDate.value = endHolder; + } + searchResultStore.firstBackendFetchExecuted = true; facetAPiCallFurfilled = true; } diff --git a/src/api/api-client.ts b/src/api/api-client.ts index 718ce68c..fe254b34 100644 --- a/src/api/api-client.ts +++ b/src/api/api-client.ts @@ -149,6 +149,14 @@ export class APIServiceClient { return await this.httpClient.get(`bff/v1/proxy/search/?q=*&facet=true&facet.limit=-1&rows=0`); } + async getRadioFacets(): Promise { + return await this.httpClient.get(`bff/v1/proxy/search/?q=*&facet=true&facet.limit=-1&rows=0&fq=origin:"ds.radio"`); + } + + async getTVFacets(): Promise { + return await this.httpClient.get(`bff/v1/proxy/search/?q=*&facet=true&facet.limit=-1&rows=0&fq=origin:"ds.tv"`); + } + async getSearchResults( query: string, filters: string, diff --git a/src/assets/images/filter-thumbnails/dr.jpg b/src/assets/images/filter-thumbnails/dr.jpg new file mode 100644 index 00000000..f84d228f Binary files /dev/null and b/src/assets/images/filter-thumbnails/dr.jpg differ diff --git a/src/assets/images/filter-thumbnails/dr1.jpg b/src/assets/images/filter-thumbnails/dr1.jpg new file mode 100644 index 00000000..da8145ac Binary files /dev/null and b/src/assets/images/filter-thumbnails/dr1.jpg differ diff --git a/src/assets/images/filter-thumbnails/dr2.jpg b/src/assets/images/filter-thumbnails/dr2.jpg new file mode 100644 index 00000000..442fbb1b Binary files /dev/null and b/src/assets/images/filter-thumbnails/dr2.jpg differ diff --git a/src/assets/images/filter-thumbnails/drk.jpg b/src/assets/images/filter-thumbnails/drk.jpg new file mode 100644 index 00000000..1afab7b6 Binary files /dev/null and b/src/assets/images/filter-thumbnails/drk.jpg differ diff --git a/src/assets/images/filter-thumbnails/drultra.jpg b/src/assets/images/filter-thumbnails/drultra.jpg new file mode 100644 index 00000000..0c70ed4f Binary files /dev/null and b/src/assets/images/filter-thumbnails/drultra.jpg differ diff --git a/src/assets/images/filter-thumbnails/drupdate.jpg b/src/assets/images/filter-thumbnails/drupdate.jpg new file mode 100644 index 00000000..4d1c9944 Binary files /dev/null and b/src/assets/images/filter-thumbnails/drupdate.jpg differ diff --git a/src/assets/images/filter-thumbnails/p1.jpg b/src/assets/images/filter-thumbnails/p1.jpg new file mode 100644 index 00000000..1cca52fd Binary files /dev/null and b/src/assets/images/filter-thumbnails/p1.jpg differ diff --git a/src/assets/images/filter-thumbnails/p2.jpg b/src/assets/images/filter-thumbnails/p2.jpg new file mode 100644 index 00000000..efce70d8 Binary files /dev/null and b/src/assets/images/filter-thumbnails/p2.jpg differ diff --git a/src/assets/images/filter-thumbnails/p3.jpg b/src/assets/images/filter-thumbnails/p3.jpg new file mode 100644 index 00000000..e230b787 Binary files /dev/null and b/src/assets/images/filter-thumbnails/p3.jpg differ diff --git a/src/assets/images/filter-thumbnails/p4.jpg b/src/assets/images/filter-thumbnails/p4.jpg new file mode 100644 index 00000000..d5f9a264 Binary files /dev/null and b/src/assets/images/filter-thumbnails/p4.jpg differ diff --git a/src/assets/images/filter-thumbnails/p5.jpg b/src/assets/images/filter-thumbnails/p5.jpg new file mode 100644 index 00000000..4780eefd Binary files /dev/null and b/src/assets/images/filter-thumbnails/p5.jpg differ diff --git a/src/assets/images/filter-thumbnails/p6.jpg b/src/assets/images/filter-thumbnails/p6.jpg new file mode 100644 index 00000000..32160b1a Binary files /dev/null and b/src/assets/images/filter-thumbnails/p6.jpg differ diff --git a/src/assets/images/filter-thumbnails/p8.jpg b/src/assets/images/filter-thumbnails/p8.jpg new file mode 100644 index 00000000..5f2a3cff Binary files /dev/null and b/src/assets/images/filter-thumbnails/p8.jpg differ diff --git a/src/assets/images/filter-thumbnails/ramasjang-tv.jpg b/src/assets/images/filter-thumbnails/ramasjang-tv.jpg new file mode 100644 index 00000000..cea0b24e Binary files /dev/null and b/src/assets/images/filter-thumbnails/ramasjang-tv.jpg differ diff --git a/src/assets/styles/base.css b/src/assets/styles/base.css index a0125a04..6040ffbf 100644 --- a/src/assets/styles/base.css +++ b/src/assets/styles/base.css @@ -1,26 +1,26 @@ /*Headings*/ -.heading-sub{ +.heading-sub { font-size: var(--fs-lg); line-height: var(--lh-medium-tight); font-weight: var(--fw-regular); } -.heading-display{ +.heading-display { font-size: var(--fs-xxl); line-height: var(--lh-small); font-weight: var(--fw-regular); font-family: 'LibreBaskerville'; } -h1{ +h1 { font-size: var(--fs-xl); line-height: var(--lh-small); font-weight: var(--fw-regular); } -h2{ +h2 { font-size: var(--fs-md); line-height: var(--lh-medium-tight); font-weight: var(--fw-regular); } -h3{ +h3 { font-size: var(--fs-sm); line-height: var(--lh-small); font-weight: var(--fw-bold); @@ -33,83 +33,83 @@ p.fixed-size { line-height: var(--lh-base-fixed); } -p{ +p { font-size: var(--fs-base); line-height: var(--lh-large); font-weight: var(--fw-regular); } -a{ +a { font-size: var(--fs-base); line-height: var(--lh-large); font-weight: var(--fw-regular); } /*Labels*/ -.label-small{ +.label-small { font-size: var(--fs-meta); line-height: var(--lh-large); font-weight: var(--fw-regular); } -.label-small-bold{ +.label-small-bold { font-size: var(--fs-meta); line-height: var(--lh-large); font-weight: var(--fw-bold); } -.label-regular{ +.label-regular { font-size: var(--fs-base); line-height: var(--lh-large); font-weight: var(--fw-regular); } -.label-medium{ +.label-medium { font-size: var(--fs-lead); line-height: var(--lh-large-tight); font-weight: var(--fw-regular); } -.label-medium-bold{ +.label-medium-bold { font-size: var(--fs-lead); line-height: var(--lh-large-tight); font-weight: var(--fw-bold); } -.label-big{ +.label-big { font-size: var(--fs-md); line-height: var(--lh-medium-tight); font-weight: var(--fw-regular); } /*Buttons*/ -.btn-reg{ +.btn-reg { font-size: var(--fs-base); line-height: var(--lh-btn); font-weight: var(--fw-regular); } -.btn-medium{ +.btn-medium { font-size: var(--fs-lead); line-height: var(--lh-btn); font-weight: var(--fw-regular); } -.btn-big{ +.btn-big { font-size: var(--fs-md); line-height: var(--lh-btn); font-weight: var(--fw-regular); } -.input{ +.input { font-size: var(--fs-base); line-height: var(--lh-large); font-weight: var(--fw-regular); } /*Menu*/ -.menu-main{ +.menu-main { font-size: var(--fs-lead); line-height: var(--lh-large-tight); font-weight: var(--fw-bold); } -.menu-secondary{ +.menu-secondary { font-size: var(--fs-meta); line-height: var(--lh-large); font-weight: var(--fw-bold); } -.menu-footer{ +.menu-footer { font-size: var(--fs-base); line-height: var(--lh-medium); font-weight: var(--fw-regular); diff --git a/src/assets/styles/elements.css b/src/assets/styles/elements.css index b7b4d525..525c1540 100644 --- a/src/assets/styles/elements.css +++ b/src/assets/styles/elements.css @@ -9,12 +9,17 @@ --bg-default: #ffff; --bg-transparent: transparent; --bg-golden:#e0b500; + --bg-default-gray: #ccc; + --bg-secondary-light: #caf0fe; + --bg-secondary-light-20: #F0FBFF; /*colors*/ --color-main:#002e70; --color-default:#ffff; --color-disabled:#969696; --color-disabled-sort:#d5d5d5; + --color-disabled-font: #757575; + --color-alert-red: rgb(184, 0, 0); /*borders*/ --color-border-active: #002e70; @@ -23,10 +28,12 @@ --color-border-success:#49DA87; --color-border-disabled:#757575; --color-border-transparent: transparent; + --color-border-light-focused: #96E2FD; + --color-borders-light: #96E2FD; /*rounded*/ --rounded-small: var(--scale-50); - --rounded-medium: var(--scale-150); + --rounded-medium: var(--scale-100); --rounded-large: var(--scale-800); /*padding*/ diff --git a/src/assets/styles/vue-slider-styles.css b/src/assets/styles/vue-slider-styles.css index 140c27ee..4bc13ef5 100644 --- a/src/assets/styles/vue-slider-styles.css +++ b/src/assets/styles/vue-slider-styles.css @@ -1,58 +1,112 @@ .vue-slider-rail { - background-color: white !important; + background-color: var(--bg-transparent) !important; border-radius: 0px !important; + height: 100% !important; + + +} + +.vue-slider-rail::before { + display: block; + height: 5px; + content: ''; + z-index: 6 !important; + border: 1px solid var(--color-border-disabled); + background-color: var(--bg-disabled) !important; + border-radius: 4px; + top:calc(100%) !important; + position: relative; +} + +.testClass { + mix-blend-mode: lighten; } +.slider-whiteoff-container .vue-slider-rail::before { + border: 1px solid var(--color-border-disabled); + background-color: var(--bg-disabled) !important; +} .vue-slider { - height: 60px !important; + height: 100px !important; padding: 0px !important; + align-content: center; +} +.time-search-filter-container .vue-slider { + height: 50px !important; } .vue-slider-process { border-radius: 0px !important; - background-color: #caf0fe !important; - z-index: 5 !important; - opacity: 0.5; - mix-blend-mode: hue !important; - filter: hue-rotate(245deg) !important; + background-color: transparent !important; + z-index: 6 !important; + border-radius: 0 !important; } +.vue-slider-process::before { + display: block; + height: 5px; + position: absolute; + background-color: var(--bg-main) !important; + content: ''; + border:1px solid var(--color-border-active); + top: calc(100%); + z-index: 7 !important; + width: 100%; +} +.slider-whiteoff-container .vue-slider-process::before { + +} .vue-slider-mark-step { + background-color: var(--bg-transparent) !important; border-radius: 0px !important; + height: 100% !important; + top: -10px !important; } .vue-slider-dot { - width: 12px !important; - border: 1px solid #002e70; - height: 100% !important; - background-color: #002e70 !important; + width: 44px !important; + height: 44px !important; + background-color: var(--bg-default) !important; display: flex; box-sizing: border-box; - border-radius: 5px; - z-index: 6 !important; + border-radius: 50%; + z-index: 9 !important; + border: 4px solid var(--color-border-active); + justify-content: center; + align-items: center; + box-shadow: none; + top: 100% !important; + } .vue-slider-dot-handle { - height: 18px !important; - width: 18px !important; - margin-top: 20px !important; - margin-left: -18px !important; - background-color: #f7ae3b !important; - left: 14px; + height: 32px !important; + width: 32px !important; + display: flex; + align-items: center; + justify-content: center; + background-color: var(--bg-main) !important; position: relative; + box-sizing: border-box ; + box-shadow: none !important; + border-color: transparent; + border: 1px solid transparent; + transition: all 0.2s linear; +} +.vue-slider-dot-handle:hover,.vue-slider-dot-handle:active{ + height: 36px !important; + width: 36px !important; + background-color: var(--bg-main-hover) !important; } - .vue-slider-dot-handle:before { content: ''; display: block; - width: 10px; - height: 10px; + width: 7px; + height: 7px; box-sizing: border-box; - border-left: 2px solid #002e70; - border-top: 2px solid #002e70; + border-left: 2px solid var(--color-default); + border-top: 2px solid var(--color-default); transform: rotateZ(-45deg); position: relative; - top: 4px; - left: -10px; transition: all 0.1s linear 0s; opacity:1; } @@ -60,44 +114,57 @@ .vue-slider-dot-handle:after { content: ''; display: block; - width: 10px; - height: 10px; + width: 7px; + height: 7px; box-sizing: border-box; - border-left: 2px solid #002e70; - border-top: 2px solid #002e70; + border-left: 2px solid var(--color-default); + border-top: 2px solid var(--color-default); transform: rotateZ(135deg); position: relative; - top: -6px; - left: 19px; transition: all 0.1s linear 0s; opacity:1; } .vue-slider-dot-handle:hover:before { - left: -11px; + left: -2px; + border-color: var(--color-main); } .vue-slider-dot-handle:hover:after { - left: 20px; + left: 2px;border-color: var(--color-main); +} +.vue-slider-dot-handle:active:before{ + left:-5px; + border-color: var(--color-main); +} +.vue-slider-dot-handle:active:after{ + left:5px; + border-color: var(--color-main); } - .vue-slider-mark { width: 2px !important; height: 15% !important; - background-color: #002e70 !important; + background-color: transparent !important; margin-bottom: 10px !important; - top: 92% !important; } .vue-slider-mark-label { - padding-top: 5px; - transform: rotateZ(-35deg) translate(-50%, -50%) !important; - left: -25px !important; - background-color:white; display:none; - font-size: var(--fs-meta) !important; + /* font-size: var(--fs-meta) !important; */ + background: var(--bg-disabled) 0% 0% no-repeat padding-box !important; + color: white !important; + border-radius: 5px; + top: calc(-100% - 1em) !important; + font-size: var(--fs-lead) !important; + line-height: var(--lh-large-tight); + font-weight: var(--fw-regular); + padding: 2px 5px; + min-width: 20px; + text-align: center; +} +.slider-whiteoff-container .vue-slider-mark-label { + top: calc(100% - 3.8em) !important; } - .vue-slider-dot-tooltip-inner { background-color: #fff6c4 !important; color: #002e70 !important; @@ -108,7 +175,9 @@ border-top-color: #fff6c4 !important; color: black !important; } - +.vue-slider-marks { + z-index: 8; +} .vue-slider-marks .vue-slider-mark:first-of-type .vue-slider-mark-label { left: 15px !important; } @@ -120,22 +189,27 @@ .vue-slider-marks .vue-slider-mark .vue-slider-mark-label { display: none; } - -.vue-slider-marks .vue-slider-mark { - display: none; -} - -@media (min-width: 640px) { - .vue-slider-marks .vue-slider-mark:nth-child(5n) { +.slider-whiteoff-container .vue-slider-marks .vue-slider-mark:nth-child(10n) .vue-slider-mark-label { display: block; - } } - -.vue-slider-marks .vue-slider-mark:nth-child(1n) .vue-slider-mark-label { - display: block; - color: #002e70; +.slider-whiteoff-container .vue-slider-marks .vue-slider-mark:nth-child(10n) .vue-slider-mark-step:after{ + top:calc(100% - 3px) !important; + height: 7px; } +.time-search-filter-container .vue-slider-marks .vue-slider-mark:nth-child(15n) .vue-slider-mark-label{ + display:block +} +.time-search-filter-container .vue-slider-marks .vue-slider-mark:nth-child(15n) .vue-slider-mark-step:after{ + top:calc(100% - 3px) !important; + height: 7px; +} +.vue-slider-marks .vue-slider-mark-active .vue-slider-mark-label-active { + opacity: 100%; + background-color: var(--bg-default)!important; + border: 1px solid var(--color-border-active); + color: var(--color-main) !important; +} .vue-slider-marks .vue-slider-mark:nth-child(1n) { height: 50% !important; top: 75% !important; @@ -144,19 +218,18 @@ .vue-slider-marks .vue-slider-mark:nth-child(1n) .vue-slider-mark-step:after { content: ''; display: block; - width: 20px; - border-top: 2px solid #002e70; - height: 2px; - left: -6px; + width: 2px; + background-color: var(--bg-disabled); + height: 4px; position: absolute; - top: calc(100%); - transform: rotateZ(-35deg) translate(-50%, -50%); - transform-origin: center; + top: calc(99%) !important; +} +.vue-slider-marks .vue-slider-mark-active:nth-child(1n) .vue-slider-mark-step:after{ + background-color: var(--bg-main); } - .vue-slider-dot-tooltip-inner { background: #002e70 0% 0% no-repeat padding-box !important; - font-size: 20px !important; + font-size: var(--fs-lead) !important; color: white !important; } .vue-slider-dot-tooltip-top { @@ -167,4 +240,66 @@ font-size: var(--fs-lead); line-height: var(--lh-large-tight); font-weight: var(--fw-regular); +} +.vue-slider-dot-tooltip-inner-top::after { + border: none !important; +} +.slider-whiteoff-container .vue-slider-marks .vue-slider-mark:nth-child(20n) .vue-slider-mark-label { + display: none !important; + } + +@media (min-width: 640px) { + .vue-slider-marks .vue-slider-mark:nth-child(1n) .vue-slider-mark-step:after{ + top: calc(98%) !important; + } + .slider-whiteoff-container .vue-slider-marks .vue-slider-mark:nth-child(10n) .vue-slider-mark-step:after{ + top:calc(98%) !important; + height: 5px; + } + .slider-whiteoff-container .vue-slider-marks .vue-slider-mark:nth-child(7n) .vue-slider-mark-step:after{ + top:calc(100% - 3px) !important; + height: 7px; + } + .slider-whiteoff-container .vue-slider { + height: 200px !important; + align-content: end; + } + .slider-whiteoff-container .vue-slider-mark-step { + display:none !important; + } + .slider-whiteoff-container .vue-slider-marks .vue-slider-mark:nth-child(20n) .vue-slider-mark-label { + display: block !important; + } +} +@media (min-width: 850px) { + + .slider-whiteoff-container .vue-slider-marks .vue-slider-mark:nth-child(7n) .vue-slider-mark-step:after{ + top:calc(98%) !important; + height: 5px; + } + + .slider-whiteoff-container .vue-slider-marks .vue-slider-mark:nth-child(5n) .vue-slider-mark-step:after{ + top:calc(100% - 3px) !important; + height: 7px; + } + .slider-whiteoff-container .vue-slider-mark-label { + top: calc(100% - 3.8em) !important; + } +} +@media(min-width:990px){ + + .slider-whiteoff-container .vue-slider-mark-label { + top: calc(100% - 3.5em) !important; + } + .slider-whiteoff-container .vue-slider-marks .vue-slider-mark:nth-child(7n) .vue-slider-mark-step:after{ + top:calc(98%) !important; + height: 5px; + } + .slider-whiteoff-container .vue-slider-marks .vue-slider-mark:nth-child(5n) .vue-slider-mark-step:after{ + top:calc(100% - 5px) !important; + height: 7px; + } + .slider-whiteoff-container .vue-slider-marks .vue-slider-mark-active:nth-child(1n) .vue-slider-mark-step:after{ + background-color: var(--bg-default); +} } \ No newline at end of file diff --git a/src/components/common/CustomRadioButton.vue b/src/components/common/CustomRadioButton.vue new file mode 100644 index 00000000..2e54a591 --- /dev/null +++ b/src/components/common/CustomRadioButton.vue @@ -0,0 +1,166 @@ + + + + + diff --git a/src/components/common/CustomRadioGroup.vue b/src/components/common/CustomRadioGroup.vue new file mode 100644 index 00000000..b3799272 --- /dev/null +++ b/src/components/common/CustomRadioGroup.vue @@ -0,0 +1,89 @@ + + + + + diff --git a/src/components/common/CustomTimelineSelect.vue b/src/components/common/CustomTimelineSelect.vue index f4190015..083827ed 100644 --- a/src/components/common/CustomTimelineSelect.vue +++ b/src/components/common/CustomTimelineSelect.vue @@ -40,7 +40,7 @@ export default defineComponent({ name: 'CustomTimelineSelect', props: { listItems: { - type: Array as PropType, + type: Array as PropType, default() { return []; }, @@ -108,6 +108,8 @@ export default defineComponent({ background-position-x: 85px; background-position-y: 13px; width: 118px; + box-sizing: border-box; + height: 48px; } .select-container:hover select { diff --git a/src/components/common/FilterExpander.vue b/src/components/common/FilterExpander.vue new file mode 100644 index 00000000..9686a505 --- /dev/null +++ b/src/components/common/FilterExpander.vue @@ -0,0 +1,409 @@ + + + + diff --git a/src/components/common/GridDisplay.vue b/src/components/common/GridDisplay.vue index b24f3157..eb944172 100644 --- a/src/components/common/GridDisplay.vue +++ b/src/components/common/GridDisplay.vue @@ -21,7 +21,7 @@ @@ -151,6 +191,30 @@ export default defineComponent({ opacity: 0.5; } +.display-image { + width: 30px; + height: 30px; + padding: 0px 5px; + background-repeat: no-repeat; + background-position: center center; +} + +.display-image.disabled.no-icon { + opacity: 0.3; +} + +.display-image.svg { + background-image: url('@/assets/icons/blue/diverse-blue.svg'); +} + +.display-image.svg.disabled { + background-image: url('@/assets/icons/gray/diverse-gray.svg'); +} + +.display-image.channel { + background-size: contain; +} + .name { height: 100%; display: flex; @@ -166,7 +230,7 @@ export default defineComponent({ margin: 5px 0px; margin-left: 5px; border-radius: 5px; - background-color: rgba(170, 170, 170, 1); + background-color: var(--color-disabled-font); height: 12px; display: inline-block; } @@ -187,9 +251,25 @@ export default defineComponent({ } .checkbox-container { - max-height: 24px; - padding: 0px 5px; + height: 36px; + margin: 0px 5px; + padding: 2px 5px; text-align: left; + border: 1px solid transparent; + transition: all 0.2s linear 0s; + box-sizing: border-box; +} + +.checkbox-container:hover { + background-color: var(--bg-secondary-light-20); + border: 1px solid var(--color-border-light-focused); + border-radius: 4px; +} + +.checkbox-container.checked { + background-color: var(--bg-secondary-light); + border: 1px solid var(--color-borders-light); + border-radius: 4px; } .checkbox-container.disabled .label { @@ -202,6 +282,10 @@ export default defineComponent({ .label { position: relative; + display: flex; + cursor: pointer; + align-items: center; + height: 100%; } .underline { @@ -214,15 +298,16 @@ export default defineComponent({ .checkbox-container.disabled .title, .checkbox-container.disabled .tag-number { - color: rgb(177, 177, 177) !important; + color: var(--color-disabled-font); font-weight: normal; } .tag-number { color: #383838; - display: inline-block; padding-left: 5px; height: 100%; + margin-left: auto; + order: 2; } .loading.tag-number { @@ -235,8 +320,8 @@ export default defineComponent({ margin: 0 0 0 5px; } .checkbox-container.disabled .loading.tag-number .text { - background-color: rgb(177, 177, 177) !important; - opacity: 0.5; + background-color: var(--color-disabled-font); + opacity: 1; } .title { @@ -244,24 +329,17 @@ export default defineComponent({ max-width: calc(100% - 95px); white-space: nowrap; overflow: clip; - text-transform: uppercase; color: #002e70; - display: inline-block; } .loading .checkbox:after { - border: 1px solid rgba(170, 170, 170, 1) !important; + border: 2px solid var(--color-disabled-font); background-color: rgb(255, 255, 255) !important; cursor: default; } .loading .checkbox:checked:after { - background-color: rgba(170, 170, 170, 1) !important; -} - -.loading .checkbox:hover:before { - cursor: default !important; - border-color: white !important; + background-color: var(--color-disabled-font); } .checkbox:disabled { @@ -272,29 +350,6 @@ export default defineComponent({ cursor: default; } -.checkbox:disabled:hover:after { - background-color: transparent; - cursor: default; -} - -.checkbox:disabled:hover:after { - cursor: default; - background-color: #002e70; -} - -.checkbox:hover:after { - background-color: #caf0fe; -} - -.checkbox:checked:hover:before { - border-color: #002e70; - cursor: pointer; -} -.checkbox:checked:hover:after { - border-color: rgba(170, 170, 170, 1); - background-color: white; -} - input:focus { box-shadow: 0 0 0 2px rgba(39, 94, 254, 0.5); } @@ -308,7 +363,7 @@ input:focus { } .checkbox-container.disabled .checkbox:after { - border: 1px solid rgb(145, 145, 145); + border: 3px solid rgb(145, 145, 145); } .checkbox-container.disabled .checkbox:hover:after { @@ -321,9 +376,11 @@ input:focus { transition: all 0.15s linear 0s; content: ''; display: block; - width: 15px; - height: 15px; - border: 1px solid #002e70; + width: 20px; + height: 20px; + border: 3px solid #002e70; + border-radius: 4px; + box-sizing: border-box; } .checkbox:checked:after { @@ -333,18 +390,85 @@ input:focus { .checkbox:checked:before { content: ''; display: block; - width: 7px; - height: 12px; + width: 9px; + height: 16px; border-bottom: 2px solid white; border-right: 2px solid white; position: absolute; - top: 1px; - left: 5px; + top: -1px; + left: 6px; box-sizing: border-box; transform-origin: center; transform: rotateZ(45deg); } +.svg.diverse { + background-image: url('@/assets/icons/blue/diverse-blue.svg'); +} +.svg.dokumentar { + background-image: url('@/assets/icons/blue/dokumentar-blue.svg'); +} +.svg.film-og-serier { + background-image: url('@/assets/icons/blue/fiktion-blue.svg'); +} +.svg.kultur-og-oplysning { + background-image: url('@/assets/icons/blue/kultur-blue.svg'); +} +.svg.livsstil { + background-image: url('@/assets/icons/blue/livsstil-blue.svg'); +} +.svg.musik { + background-image: url('@/assets/icons/blue/musik-blue.svg'); +} +.svg.nyheder-politik-og-samfund { + background-image: url('@/assets/icons/blue/nyheder-blue.svg'); +} +.svg.sport { + background-image: url('@/assets/icons/blue/sport-blue.svg'); +} +.svg.humor-quiz-og-underholdning { + background-image: url('@/assets/icons/blue/underholdning-blue.svg'); +} +.svg.natur-og-videnskab { + background-image: url('@/assets/icons/blue/videnskab-blue.svg'); +} +.svg.brn-og-unge { + background-image: url('@/assets/icons/blue/born-blue.svg'); +} + +.svg.diverse.disabled { + background-image: url('@/assets/icons/gray/diverse-gray.svg'); +} +.svg.dokumentar.disabled { + background-image: url('@/assets/icons/gray/dokumentar-gray.svg'); +} +.svg.film-og-serier.disabled { + background-image: url('@/assets/icons/gray/fiktion-gray.svg'); +} +.svg.kultur-og-oplysning.disabled { + background-image: url('@/assets/icons/gray/kultur-gray.svg'); +} +.svg.livsstil.disabled { + background-image: url('@/assets/icons/gray/livsstil-gray.svg'); +} +.svg.musik.disabled { + background-image: url('@/assets/icons/gray/musik-gray.svg'); +} +.svg.nyheder-politik-og-samfund.disabled { + background-image: url('@/assets/icons/gray/nyheder-gray.svg'); +} +.svg.sport.disabled { + background-image: url('@/assets/icons/gray/sport-gray.svg'); +} +.svg.humor-quiz-og-underholdning.disabled { + background-image: url('@/assets/icons/gray/underholdning-gray.svg'); +} +.svg.natur-og-videnskab.disabled { + background-image: url('@/assets/icons/gray/videnskab-gray.svg'); +} +.svg.brn-og-unge.disabled { + background-image: url('@/assets/icons/gray/born-gray.svg'); +} .label { cursor: pointer; } diff --git a/src/components/common/TimePicker.vue b/src/components/common/TimePicker.vue new file mode 100644 index 00000000..80b3103c --- /dev/null +++ b/src/components/common/TimePicker.vue @@ -0,0 +1,770 @@ + + + + + diff --git a/src/components/common/TimeSearchComponent.vue b/src/components/common/TimeSearchComponent.vue index ac867b66..3c1325ab 100644 --- a/src/components/common/TimeSearchComponent.vue +++ b/src/components/common/TimeSearchComponent.vue @@ -133,7 +133,7 @@ export default defineComponent({ start: 0, rows: 10, fq: [], - sort: `random_${Date.now()} ASC`, + /* sort: `random_${Date.now()} ASC`, */ }, }); @@ -194,7 +194,7 @@ export default defineComponent({ start: 0, rows: 10, fq: fqArray, - sort: `random_${Date.now()} ASC`, + /* sort: `random_${Date.now()} ASC`, */ }, }; }; @@ -228,7 +228,7 @@ h2 { .result-container { width: 100%; - padding-top: 25px; + padding-top: 45px; } .result-header { @@ -300,7 +300,6 @@ h2 { text-align: left; max-width: 1280px; width: 100%; - display: none; } .header h2 { @@ -327,6 +326,9 @@ h2 { .time-result-item:nth-child(n + 5) { display: none; } + .result-container { + padding-top: 85px; + } } @media (min-width: 480px) { diff --git a/src/components/common/TimelineHeadline.vue b/src/components/common/TimelineHeadline.vue index 5c651857..835fa9d9 100644 --- a/src/components/common/TimelineHeadline.vue +++ b/src/components/common/TimelineHeadline.vue @@ -21,6 +21,8 @@ v-for="(item, index) in selectedItems.selectedItems" :key="`${index}-${item.name}`" class="selected-entity label-small" + button-color="main" + button-size="small" :data-testid="addTestDataEnrichment('button', 'timeline-headline', `${headline}-small-status-toggle`, 0)" :button-text=" formatStringForTime( @@ -31,8 +33,6 @@ ) " button-type="btn-tag" - button-size="small" - button-color="main" :title="`${t('facets.remove')} ${ useTranslation ? (item.translation ? t(item.translation) : t(item.name)) : item.name }`" diff --git a/src/components/common/timeSearch/DayPicker.vue b/src/components/common/timeSearch/DayPicker.vue index 4ca64d13..28ebbd58 100644 --- a/src/components/common/timeSearch/DayPicker.vue +++ b/src/components/common/timeSearch/DayPicker.vue @@ -1,39 +1,12 @@ + + diff --git a/src/locales/da.json b/src/locales/da.json index e33e7247..4d46ed8c 100644 --- a/src/locales/da.json +++ b/src/locales/da.json @@ -95,19 +95,72 @@ "fromTheArchive":"{month} måned på DR gennem tiden" }, "facets" : { + "headline":"Udvælg filtre, til at indsnævre din søgning på:", + "starHeadline": "Udvælg filtre, til at indsnævre din søgning.", "allGenres": "Alle kategorier", "genres": "kategori | kategorier", - "genresSubtitle": "Vælg, hvilken kategori du gerne vil søge i.", + "selectedGenres": "{selected} af {total} kategorier valgt", + "genresSubtitle": "Vælg, hvilken kategori du gerne vil søge i.", "allChannels":"Alle kanaler", + "selectedTVChannels":"{selected} af {total} kanaler valgt", + "selectedRadioChannels":"{selected} af {total} kanaler valgt", "channels":"kanal | kanaler", + "tvChannels":"TV kanal | TV kanaler", + "radioChannels": "RADIO kanal | RADIO kanaler", "chosen":"Valgt", + "contentFrom": "Indhold tilbage fra ", "reset":"nulstil", - "choose": "Vælg", - "slowResponse": { - "title": "Lang respons tid", - "text": "Vi beklager for lang respons tid, vi oplever meget trafik i øjeblikket" - }, - "remove":"Fjern" + "choose": "Vælg", + "seeResults": "Se dit {resultCount} resultat | Se dine {resultCount} resultater", + "to":"Til", + "from":"Fra", + "year":"år", + "slowResponse": { + "title": "Lang respons tid", + "text": "Vi beklager for lang respons tid, vi oplever meget trafik i øjeblikket" + }, + "remove":"Fjern", + "searchingIn":"Søger kun på", + "desc":"beskrivelse", + "title":"titel", + "searchMethod": { + "all": { + "title":"Søg i alt", + "desc":"Der søges i alle metadata-felter." + }, + "title": { + "title":"Søg kun på titler", + "desc":"Der søges kun i titel-feltet." + }, + "desc": { + "title":"Søg kun på beskrivelser", + "desc":"Der søges kun i beskrivelses-feltet." + } + }, + "searchMaterial": { + "both": { + "title":"Søg på både TV og RADIO", + "desc":"Der søges både i RADIO-poster og TV-poster." + }, + "tv": { + "title":"Søg kun på TV", + "desc":"Der søges kun i TV-poster." + }, + "radio": { + "title":"Søg kun på RADIO", + "desc":"Der søges kun i RADIO-poster." + } + }, + "timePeriod": { + "date": { + "title":"Dato", + "desc":"Der søges på en dag." + }, + "period": { + "title":"Periode", + "desc":"Der søges på en år-række." + } + } }, "breadcrumb": { "frontpage":"Forside", @@ -143,7 +196,7 @@ "allTimeslots":"Hele døgnet", "selection":"Et udpluk af resultaterne", "dateSubtitle": "Her kan du vælge en bestemt dato og se, hvad der blev sendt den dag.", - "timeMachineSubtitle": "Her kan du vælge årstal, måneder og ugedage, hvis du søger udsendelser sendt i en bestemt periode. Når du klikker dig videre til resultatlisten, får du flere filtreringsmuligheder.", + "timeMachineSubtitle": "Her kan du vælge årstal, hvis du søger udsendelser sendt i en bestemt periode. Når du klikker dig videre til resultatlisten, får du flere filtreringsmuligheder.", "searchCategoriesSubtitle": "Vælg en kategori og gå på opdagelse i udsendelserne. Kategorierne indeholder primært TV-udsendelser, mens du finder de fleste af radioudsendelserne i “Radio-rodekassen.”", "monthHeadline":"Vælg måneder", "dayHeadline":"Vælg ugedage", @@ -538,7 +591,8 @@ "natur-og-videnskab":"Natur og videnskab", "nyheder-politik-og-samfund":"Nyheder, politik og samfund", "radio-rodekasse":"Blandet radio", - "tv-rodekasse":"Blandet TV" + "tv-rodekasse":"Blandet TV", + "sport": "Sport" }, "find":{ "headline":"Hvad vil du søge efter?", @@ -546,5 +600,13 @@ "maybeYouWantTo":"Eller måske vil du hellere:", "restoreFilters":"Gendanne filtre fra sidste søgning", "GoToFrontpage":"Til forsiden af DR-arkivet" + }, + "calendar": { + "open":"Åben kalender", + "close": "Luk kalender", + "explanation": "Skriv en dato i formatet DD-MM-ÅÅÅÅ eller vælg fra kalenderen.", + "select": "Vælg dato", + "nextMonth": "Næste måned", + "prevMonth":"Forrige måned" } } diff --git a/src/locales/en.json b/src/locales/en.json index ee921928..5313c19f 100644 --- a/src/locales/en.json +++ b/src/locales/en.json @@ -95,20 +95,72 @@ "fromTheArchive":"The month of {month} on DR through time" }, "facets" : { + "headline":"Choose filters, to limit your search for:", + "starHeadline": "Choose filters to refine your search.", "allGenres": "all categories", "genres": "category | categories", + "selectedGenres": "{selected} of {total} categories selected", "genresSubtitle": "Select a category and explore the programmes.", "allChannels":"all channels", + "selectedTVChannels":"{selected} of {total} channels selected", + "selectedRadioChannels":"{selected} of {total} kanalchannelser selected", "channels":"channel | channels", + "tvChannels":"TV channel | TV channels", + "radioChannels": "RADIO channel | RADIO channels", "chosen":"chosen", + "contentFrom": "Content dating back to", "reset":"reset", "choose": "Select", + "seeResults": "See your {resultCount} result | See your {resultCount} results", + "to":"To", + "from":"From", + "year":"year", "slowResponse": { "title": "Slow response time", "text": "We're sorry for long response, we are experiencing a lot of traffic at the moment" }, - "remove":"Remove" - + "remove":"Remove", + "searchingIn":"Only searching with", + "desc":"description", + "title":"title", + "searchMethod": { + "all": { + "title": "Search everything", + "desc": "Searches across all metadata fields." + }, + "title": { + "title": "Search titles only", + "desc": "Searches only in the title field." + }, + "desc": { + "title": "Search descriptions only", + "desc": "Searches only in the description field." + } + }, + "searchMaterial": { + "both": { + "title": "Search both TV and RADIO", + "desc": "Searches both RADIO and TV records." + }, + "tv": { + "title": "Search TV only", + "desc": "Searches only in TV records." + }, + "radio": { + "title": "Search RADIO only", + "desc": "Searches only in RADIO records." + } + }, + "timePeriod": { + "date": { + "title":"Date", + "desc":"Searches for a single day." + }, + "period": { + "title":"Period", + "desc":"Searches for a period of years." + } + } }, "breadcrumb": { "frontpage":"Frontpage", @@ -144,7 +196,7 @@ "allTimeslots":"All hours", "selection":"A selection of the results", "dateSubtitle": "Here you can select a specific date and see what was aired that day.", - "timeMachineSubtitle": "Here you can select years, months, and days of the week, if you are looking for programmes from a specific period in time. When you click through to the list of results, you will get more filtering options.", + "timeMachineSubtitle": "Here you can select years, if you are looking for programmes from a specific period in time. When you click through to the list of results, you will get more filtering options.", "searchCategoriesSubtitle": "Select a category and explore the programmes. The categories primarily contain TV programmes, while you will find most of the radio programmes in the “Radio jumble box.”", "monthHeadline":"Select months", "dayHeadline":"Select days of the week", @@ -543,7 +595,9 @@ "natur-og-videnskab":"Nature and science", "nyheder-politik-og-samfund":"News, politics, and society", "radio-rodekasse":"Mixed radio", - "tv-rodekasse":"Mixed TV" + "tv-rodekasse":"Mixed TV", + "sport": "Sport" + }, "find":{ "headline":"What do you want to search for?", @@ -551,5 +605,13 @@ "maybeYouWantTo":"Or maybe you would rather:", "restoreFilters":"Restore filters from previous search", "GoToFrontpage":"Go to the front page of DR-arkivet" + }, + "calendar": { + "open":"Open calendar", + "close": "Close calendar", + "explanation": "Type a date in format DD-MM-YYYY or pick from the calendar.", + "select": "Select date", + "nextMonth": "Next month", + "prevMonth":"Previous month" } } \ No newline at end of file diff --git a/src/store/searchResultStore.ts b/src/store/searchResultStore.ts index 74c1d478..a0dc88bd 100644 --- a/src/store/searchResultStore.ts +++ b/src/store/searchResultStore.ts @@ -43,7 +43,11 @@ export const useSearchResultStore = defineStore('searchResults', () => { const filters = ref([] as Array); const channelFilters = ref([] as Array); const categoryFilters = ref([] as Array); + const TVFacets = ref([] as Array); + const RadioFacets = ref([] as Array); const preliminaryFilter = ref(''); + const preliminarySearchMethod = ref(''); + const preliminaryPeriodSearch = ref('date'); const showFacets = ref(false); const blockAutocomplete = ref(false); const resultGrid = ref(false); @@ -114,7 +118,7 @@ export const useSearchResultStore = defineStore('searchResults', () => { if (URLFilters instanceof Array) { URLFilters.forEach((filter) => { if (filter?.split('%3A')[0].includes('origin')) { - preliminaryFilter.value = filter; + preliminaryFilter.value = decodeURIComponent(filter); } else if (filter?.split('%3A')[0].includes('creator_affiliation_facet')) { const cleanedString = filter.replace(/[()]/g, ''); channelFilters.value = cleanedString.split(' OR '); @@ -128,7 +132,7 @@ export const useSearchResultStore = defineStore('searchResults', () => { } else { const str = URLFilters; if (str.split('%3A')[0].includes('origin')) { - filters.value.push(`fq=${str}`); + preliminaryFilter.value = decodeURIComponent(str); } else if (str.split('%3A')[0].includes('creator_affiliation_facet')) { const cleanedString = str.replace(/[()]/g, ''); channelFilters.value = cleanedString.split(' OR '); @@ -142,6 +146,16 @@ export const useSearchResultStore = defineStore('searchResults', () => { } }; + const setPreliminarySearchMethodFromURL = (query: string) => { + if (query.includes('title:')) { + preliminarySearchMethod.value = 'title'; + } else if (query.includes('description:')) { + preliminarySearchMethod.value = 'desc'; + } else { + preliminarySearchMethod.value = 'all'; + } + }; + const setRowCountFromURL = (rows: string | undefined) => { if (rows) { rowCount.value = rows; @@ -213,6 +227,10 @@ export const useSearchResultStore = defineStore('searchResults', () => { channelFilters.value = []; categoryFilters.value = []; preliminaryFilter.value = ''; + preliminarySearchMethod.value = 'all'; + if (currentQuery.value.includes('title:') || currentQuery.value.includes('description:')) { + currentQuery.value = currentQuery.value.split(':')[1].replaceAll('"', ''); + } }; const resetAutocomplete = () => { @@ -286,9 +304,6 @@ export const useSearchResultStore = defineStore('searchResults', () => { }; const getSearchResults = async (query: string) => { - if (currentQuery.value === '*:*') { - currentQuery.value = ''; - } setBlockAutocomplete(true); lastSearchQuery.value = query; @@ -321,7 +336,6 @@ export const useSearchResultStore = defineStore('searchResults', () => { presetGenreFilters += `&fq=${preliminaryFilter.value}`; presetChannelFilters += `&fq=${preliminaryFilter.value}`; } - currentChannelFacetString.value = presetChannelFilters; currentGenreFacetString.value = presetGenreFilters; @@ -378,6 +392,9 @@ export const useSearchResultStore = defineStore('searchResults', () => { loading.value = false; } } + if (currentQuery.value === '*:*') { + currentQuery.value = ''; + } }; const filterQueryLength = computed(() => { @@ -447,6 +464,7 @@ export const useSearchResultStore = defineStore('searchResults', () => { setFiltersFromURL, setStartFromURL, setCurrentQueryFromURL, + setPreliminarySearchMethodFromURL, resetStart, setSortFromURL, resetSort, @@ -467,5 +485,9 @@ export const useSearchResultStore = defineStore('searchResults', () => { maxPages, queryLimitReached, filterQueryLength, + TVFacets, + RadioFacets, + preliminarySearchMethod, + preliminaryPeriodSearch, }; }); diff --git a/src/types/CalendarTypes.ts b/src/types/CalendarTypes.ts new file mode 100644 index 00000000..b4a18d6a --- /dev/null +++ b/src/types/CalendarTypes.ts @@ -0,0 +1,8 @@ +export interface CalendarDay { + date: Date; + label: number; + inMonth: boolean; + key: string; + disabled: boolean; + ariaLabel: string; +} diff --git a/src/utils/datepicker-utils.ts b/src/utils/datepicker-utils.ts index aadc4b46..4fd117f5 100644 --- a/src/utils/datepicker-utils.ts +++ b/src/utils/datepicker-utils.ts @@ -1,11 +1,58 @@ import { endYear, startYear } from '@/components/common/timeSearch/TimeSearchInitValues'; import { Ref } from 'vue'; +const updateDate = ( + timer: ReturnType | null, + isDateValid: Ref, + inputVar: Ref, + updateLink: () => void, +) => { + if (timer) { + clearTimeout(timer); + } + if (inputVar.value === undefined || inputVar.value === null) { + return; + } + isDateValid.value = false; + + const constructedDate = + inputVar.value.getDate() + '/' + Number(inputVar.value.getMonth() + 1) + '/' + inputVar.value.getFullYear(); + const dateInput = constructedDate.replace(/[.-]/g, '/'); + const splitDateInput = dateInput.split('/'); + if (splitDateInput.length === 3) { + const [day, month, yearInitial] = splitDateInput.map(Number); + let year = yearInitial; + // Validate if day, month, and year are numbers + if (!isNaN(day) && !isNaN(month) && !isNaN(year)) { + // Months in JavaScript Date are 0-indexed, so subtract 1 from the month + if (year.toString().length === 2) { + if (year < Number(new Date().getFullYear().toString().slice(-2))) { + year = Number(`20${year}`); + } else { + year = Number(`19${year}`); + } + } + const setDate = new Date(year, month - 1, day); + // Ensure the date is valid + if (setDate.getDate() === day && setDate.getMonth() === month - 1 && setDate.getFullYear() === year) { + // Validate if the date is within the range + const minDate = new Date(startYear.value); + const maxDate = new Date(endYear.value); + + if (setDate >= minDate && setDate <= maxDate) { + updateLink(); + isDateValid.value = true; + } + } + } + } +}; + const updateSelectedDate = ( e: Event, timer: ReturnType | null, isDateValid: Ref, - inputVar: Ref, + inputVar: Ref, updateLink: () => void, ) => { if (timer) { @@ -54,4 +101,4 @@ const updateSelectedDate = ( } }; -export { updateSelectedDate }; +export { updateSelectedDate, updateDate }; diff --git a/src/utils/filter-utils.ts b/src/utils/filter-utils.ts index ccb9ef68..3d92ebdf 100644 --- a/src/utils/filter-utils.ts +++ b/src/utils/filter-utils.ts @@ -1,7 +1,8 @@ -import { RouteLocationNormalizedLoaded } from 'vue-router'; +import { Router, RouteLocationNormalizedLoaded } from 'vue-router'; import { FacetPair } from '@/types/GenericRecordTypes'; import { SelectorData } from '@/types/TimeSearchTypes'; import { santizeAndSimplify } from './test-enrichments'; + const createTagFilter = (key: string) => { return `${'categories:"' + key + '"'}`; }; @@ -179,6 +180,70 @@ const normalizeFq = (fq: string | string[] | undefined): string[] => { return Array.isArray(fq) ? fq : [fq]; }; +export function removeSingleFacetEntry(fqString: string, key: string, valueToRemove: string): string | null { + // Match key:"value" (value may be URL encoded in your current strings) + const regex = new RegExp(`${key}%3A%22(.*?)%22`, 'g'); + + const matches = [...fqString.matchAll(regex)]; + + if (matches.length === 0) { + return fqString; + } + + const remainingValues = matches.map((m) => decodeURIComponent(m[1])).filter((v) => v !== valueToRemove); + + if (remainingValues.length === 0) { + return null; // entire fq clause should be removed + } + + if (remainingValues.length === 1) { + return `${key}%3A%22${encodeURIComponent(remainingValues[0])}%22`; + } + + const rebuilt = remainingValues.map((v) => `${key}%3A%22${encodeURIComponent(v)}%22`).join(' OR '); + + return `(${rebuilt})`; +} + +const removeFilterAndSearch = ( + facet: string, + router: Router, + route: RouteLocationNormalizedLoaded, + selected?: Array, +) => { + const routeQueries = cloneRouteQuery(route); + let fq = normalizeFq(routeQueries.fq); + if (facet !== 'genre_facet') { + if (!selected) { + fq = fq.filter((query) => !query.includes(facet)); + } else { + const removable = selected?.filter((entity: SelectorData) => entity.selected) ?? []; + fq = fq + .map((query) => { + if (!query.includes(facet)) { + return query; + } + let updated: string | null = query; + removable.forEach((item) => { + if (updated) { + updated = removeSingleFacetEntry(updated, facet, item.name); + } + }); + return updated; + }) + .filter((q): q is string => Boolean(q)); + } + } else { + fq = fq.filter((query) => !query.includes(facet)); + } + + routeQueries.fq = fq; + router.push({ + name: 'Search', + query: routeQueries, + }); +}; + // A simple method to arrange the facets in an orderly fasion, so they're easier to loop through. // Might not be relevant when we know more about the backend structure. const simplifyFacets = (facet: Array): FacetPair[] => { @@ -234,4 +299,5 @@ export { normalizeFq, removeTimeFacetsFromRoute, extendFacetPairToSelectorData, + removeFilterAndSearch, }; diff --git a/src/utils/focus-trap.ts b/src/utils/focus-trap.ts new file mode 100644 index 00000000..f49edb1b --- /dev/null +++ b/src/utils/focus-trap.ts @@ -0,0 +1,60 @@ +export const createFocusTrap = (container: HTMLElement, onEscape?: () => void) => { + let previousFocus: HTMLElement | null = null; + + //all our selectors for focus elements + const focusableSelector = 'button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])'; + + const handleKeydown = (e: KeyboardEvent) => { + if (!container) { + return; + } + + // we close the the entire trap on escape. + if (e.key === 'Escape') { + onEscape?.(); + return; + } + //otherwise, we only care about tap when we change focus. + if (e.key !== 'Tab') { + return; + } + + const focusables = [...container.querySelectorAll(focusableSelector)].filter( + (el) => el.offsetParent !== null, + ); + + if (!focusables.length) { + return; + } + + const first = focusables[0]; + const last = focusables[focusables.length - 1]; + + if (!e.shiftKey && document.activeElement === last) { + e.preventDefault(); + first.focus(); + } + + if (e.shiftKey && document.activeElement === first) { + e.preventDefault(); + last.focus(); + } + }; + + const activate = () => { + previousFocus = document.activeElement as HTMLElement; + container.addEventListener('keydown', handleKeydown); + container.querySelector(focusableSelector)?.focus(); + }; + + // When we deactivate, we retore focus to the elemet it was on before we opened. + const deactivate = () => { + container.removeEventListener('keydown', handleKeydown); + previousFocus?.focus(); + }; + + return { + activate, + deactivate, + }; +}; diff --git a/src/utils/record-utils.ts b/src/utils/record-utils.ts index 962f7389..e96b8374 100644 --- a/src/utils/record-utils.ts +++ b/src/utils/record-utils.ts @@ -8,11 +8,73 @@ import p5 from '@/assets/images/audio-thumbnails/p5.jpg'; import p6 from '@/assets/images/audio-thumbnails/p6.jpg'; import p8 from '@/assets/images/audio-thumbnails/p8.jpg'; +import fdr from '@/assets/images/filter-thumbnails/dr.jpg'; +import fdr1 from '@/assets/images/filter-thumbnails/dr1.jpg'; +import fdr2 from '@/assets/images/filter-thumbnails/dr2.jpg'; +import fdrk from '@/assets/images/filter-thumbnails/drk.jpg'; +import fdrultra from '@/assets/images/filter-thumbnails/drultra.jpg'; +import fdruppdate from '@/assets/images/filter-thumbnails/drupdate.jpg'; +import fp1 from '@/assets/images/filter-thumbnails/p1.jpg'; +import fp2 from '@/assets/images/filter-thumbnails/p2.jpg'; +import fp3 from '@/assets/images/filter-thumbnails/p3.jpg'; +import fp4 from '@/assets/images/filter-thumbnails/p4.jpg'; +import fp5 from '@/assets/images/filter-thumbnails/p5.jpg'; +import fp6 from '@/assets/images/filter-thumbnails/p6.jpg'; +import fp8 from '@/assets/images/filter-thumbnails/p8.jpg'; +import frama from '@/assets/images/filter-thumbnails/ramasjang-tv.jpg'; + const getEntryId = (recordData: BroadcastRecordType) => { const kalturaIdObj = recordData.identifier.find((obj) => obj.PropertyID === 'KalturaID'); return kalturaIdObj ? kalturaIdObj.value : ''; }; +const getFilterThumbnail = (channel: string) => { + channel = channel.toLowerCase(); + switch (true) { + case channel.includes('dr 1'): + return fdr1; + + case channel.includes('dr 2'): + return fdr2; + + case channel.includes('dr update'): + return fdruppdate; + + case channel.includes('dr ultra'): + return fdrultra; + + case channel.includes('dr k'): + return fdrk; + + case channel.includes('ramasjang'): + return frama; + + case channel.includes('p1'): + return fp1; + + case channel.includes('p2'): + return fp2; + + case channel.includes('p3'): + return fp3; + + case channel.includes('p4'): + return fp4; + + case channel.includes('p5'): + return fp5; + + case channel.includes('p6'): + return fp6; + + case channel.includes('p8'): + return fp8; + + default: + return fdr; + } +}; + const getThumbnailPicture = (channel: string) => { channel = channel.toLowerCase(); switch (true) { @@ -42,4 +104,4 @@ const getThumbnailPicture = (channel: string) => { } }; -export { getEntryId, getThumbnailPicture }; +export { getEntryId, getThumbnailPicture, getFilterThumbnail }; diff --git a/src/utils/svg-graph.ts b/src/utils/svg-graph.ts index 731ba01d..7a40ae7a 100644 --- a/src/utils/svg-graph.ts +++ b/src/utils/svg-graph.ts @@ -8,10 +8,10 @@ function createSVGCurvedLine(points: pointItem[]) { svg.setAttribute('viewBox', '0 0 100 100'); // Use a square viewBox for simplicity svg.setAttribute('preserveAspectRatio', 'none'); const path = document.createElementNS(svgns, 'path'); - path.setAttribute('fill', '#caf0fe'); // No fill to avoid closing the path - path.setAttribute('stroke', '#F7AE3B4D'); - path.setAttribute('stroke-width', '1'); - path.setAttribute('opacity', '1'); + path.setAttribute('fill', '#002e70'); // No fill to avoid closing the path + path.setAttribute('stroke', '#001c44'); + path.setAttribute('stroke-width', '2'); + path.setAttribute('fill-opacity', '1'); path.setAttribute('vector-effect', 'non-scaling-stroke'); // Normalize y coordinates to fit within [0, 100] range @@ -50,6 +50,39 @@ function createSVGCurvedLine(points: pointItem[]) { path.setAttribute('d', d); svg.appendChild(path); + + const defs = document.createElementNS(svgns, 'defs'); + const clipPath = document.createElementNS(svgns, 'clipPath'); + const clipId = `clip-${Math.random().toString(36).slice(2)}`; + + clipPath.setAttribute('id', clipId); + + const clipShape = path.cloneNode(true) as SVGElement; + clipShape.removeAttribute('fill'); + clipShape.removeAttribute('stroke'); + + clipPath.appendChild(clipShape); + defs.appendChild(clipPath); + svg.appendChild(defs); + + for (let i = 0; i < normalizedPoints.length; i += 1) { + const p = normalizedPoints[i]; + + const line = document.createElementNS(svgns, 'line'); + + line.setAttribute('x1', String(p.x)); + line.setAttribute('y1', '100'); + line.setAttribute('x2', String(p.x)); + line.setAttribute('y2', String(Number(p.y - 10))); + line.setAttribute('class', 'line-class'); + line.setAttribute('stroke', '#ffffff27'); + line.setAttribute('stroke-width', '1'); + line.setAttribute('vector-effect', 'non-scaling-stroke'); + line.setAttribute('clip-path', `url(#${clipId})`); + + svg.appendChild(line); + } + return svg; } diff --git a/src/views/Search.vue b/src/views/Search.vue index c41da915..8a33518d 100644 --- a/src/views/Search.vue +++ b/src/views/Search.vue @@ -227,9 +227,10 @@ export default defineComponent({ } } } + searchResultStore.setPreliminarySearchMethodFromURL(route.query.q as string); searchResultStore.setFiltersFromURL(routeFacetQueries as string[]); timeSearchStore.setFiltersFromUrl(routeFacetQueries as string[]); - searchResultStore.getSearchResults(route.query.q as string); + searchResultStore.getSearchResults(searchResultStore.currentQuery as string); setCurrentTitleForPage(); } else if (route.query.q === undefined) { searchResultStore.resetSearch(); @@ -254,12 +255,13 @@ export default defineComponent({ searchResultStore.queryLimitReached = false; } if (!queryLimitReached.value) { + searchResultStore.setPreliminarySearchMethodFromURL(route.query.q as string); searchResultStore.setFiltersFromURL(route.query.fq as string[]); searchResultStore.setSortFromURL(route.query.sort as string); searchResultStore.setCurrentQueryFromURL(route.query.q as string); searchResultStore.setRowCountFromURL(route.query.rows as string); timeSearchStore.setFiltersFromUrl(route.query.fq as string[]); - searchResultStore.getSearchResults(route.query.q as string); + searchResultStore.getSearchResults(searchResultStore.currentQuery as string); setCurrentTitleForPage(); }