+
-
+
+
+ ${term.name}
+ ${term.count
+ ? html`(${term.count})`
+ : nothing}
+
+
`;
}
diff --git a/frontend/src/search-radio.ts b/frontend/src/search-radio.ts
new file mode 100644
index 00000000..8ceb61b8
--- /dev/null
+++ b/frontend/src/search-radio.ts
@@ -0,0 +1,112 @@
+import {LitElement, html, css} from 'lit';
+import {customElement, property} from 'lit/decorators.js';
+import {CheckedInputMixin} from './mixins/checked-input';
+
+/**
+ * A custom element that represents a radio.
+ *
+ * This component is useful to have state of variable reflected back in the radio,
+ * overriding updated method.
+ * @extends {LitElement}
+ */
+@customElement('searchalicious-radio')
+export class SearchaliciousRadio extends CheckedInputMixin(LitElement) {
+ /**
+ * The styles for the radio.
+ * @type {import('lit').CSSResult}
+ */
+ static override styles = css`
+ .radio-wrapper {
+ display: flex;
+ align-items: center;
+ flex-wrap: wrap;
+ }
+
+ input[type='radio'] {
+ cursor: pointer;
+ position: relative;
+ flex-shrink: 0;
+ width: 20px;
+ height: 20px;
+ margin-right: 0;
+ appearance: none;
+ border: 1px solid var(--searchalicious-radio-color, black);
+ background-color: transparent;
+ border-radius: 50%;
+ }
+
+ input[type='radio']:checked {
+ }
+
+ input[type='radio']:focus {
+ outline: 1px solid var(--searchalicious-radio-focus-color, black);
+ }
+
+ input[type='radio']:checked:after {
+ position: absolute;
+ content: '';
+ height: 14px;
+ width: 14px;
+ top: 50%;
+ left: 50%;
+ transform: translate(-50%, -50%);
+ border-radius: 50%;
+ background-color: var(--searchalicious-radio-color, black);
+ }
+
+ label {
+ cursor: pointer;
+ padding-left: 8px;
+ }
+ `;
+
+ /**
+ * Allows or disallows the radio to be unchecked.
+ */
+ @property({type: Boolean, attribute: 'can-be-unchecked'})
+ canBeUnchecked = false;
+
+ /**
+ * Represents the id of the input.
+ */
+ @property({type: String})
+ inputId = '';
+
+ /**
+ * Allows for the radio to be unchecked.
+ * @param {Event} e - The event object.
+ */
+ _handleClick() {
+ if (this.canBeUnchecked && this.checked) {
+ this.checked = false;
+ this._dispatchChangeEvent(this.checked, this.name);
+ }
+ }
+ /**
+ * Renders the radio.
+ * @returns {import('lit').TemplateResult<1>} - The HTML template for the radio.
+ */
+ override render() {
+ return html`
+
+
+
+
+ `;
+ }
+}
+declare global {
+ interface HTMLElementTagNameMap {
+ 'searchalicious-radio': SearchaliciousRadio;
+ }
+}
diff --git a/frontend/src/search-toggle.ts b/frontend/src/search-toggle.ts
new file mode 100644
index 00000000..cc7114d0
--- /dev/null
+++ b/frontend/src/search-toggle.ts
@@ -0,0 +1,122 @@
+import {LitElement, html, css} from 'lit';
+import {customElement} from 'lit/decorators.js';
+import {CheckedInputMixin} from './mixins/checked-input';
+
+/**
+ * A custom element that represents a toggle.
+ *
+ * This component is useful to have state of variable reflected back in the toggle,
+ * overriding updated method.
+ * @extends {LitElement}
+ */
+@customElement('searchalicious-toggle')
+export class SearchaliciousToggle extends CheckedInputMixin(LitElement) {
+ /**
+ * The styles for the toggle.
+ * input[type='checkbox'] is hidden and the slider is used to represent the toggle.
+ * We keep input for accessibility.
+ * @type {import('lit').CSSResult}
+ */
+ static override styles = css`
+ .toggle-wrapper {
+ display: flex;
+ align-items: center;
+ flex-wrap: wrap;
+ }
+
+ label {
+ cursor: pointer;
+ padding-right: 8px;
+ }
+
+ .toggle {
+ cursor: pointer;
+ position: relative;
+ display: inline-block;
+ width: 30px;
+ height: 17px;
+ flex-shrink: 0;
+ }
+
+ .toggle input {
+ display: none;
+ }
+
+ .slider {
+ position: absolute;
+ cursor: pointer;
+ top: 0;
+ left: 0;
+ right: 0;
+ bottom: 0;
+ background-color: var(
+ --searchalicious-toggle-inactive-background-color,
+ grey
+ );
+ transition: 0.4s;
+ border-radius: 17px;
+ }
+
+ .slider:before {
+ position: absolute;
+ content: '';
+ height: 13px;
+ width: 13px;
+ left: 2px;
+ bottom: 2px;
+ background-color: var(--searchalicious-toggle-circle-color, white);
+ transition: 0.4s;
+ border-radius: 50%;
+ }
+ input[type='checkbox']:checked + .slider {
+ background-color: var(
+ --searchalicious-toggle-active-background-color,
+ black
+ );
+ }
+ input[type='checkbox']:checked + .slider:before {
+ transform: translateX(13px);
+ }
+ `;
+
+ onClick() {
+ const inputElement = this.getInputElement();
+ if (!inputElement) {
+ return;
+ }
+ inputElement.checked = !inputElement.checked;
+ this.checked = !this.checked;
+ this._dispatchChangeEvent(this.checked, this.name);
+ }
+
+ /**
+ * Renders the toggle.
+ * @returns {import('lit').TemplateResult<1>} - The HTML template for the toggle.
+ */
+ override render() {
+ return html`
+
+
+
+
+
+
+
+ `;
+ }
+}
+
+declare global {
+ interface HTMLElementTagNameMap {
+ 'searchalicious-toggle': SearchaliciousToggle;
+ }
+}
diff --git a/frontend/src/utils/enums.ts b/frontend/src/utils/enums.ts
index 00cdccd2..27fbda89 100644
--- a/frontend/src/utils/enums.ts
+++ b/frontend/src/utils/enums.ts
@@ -19,6 +19,7 @@ export enum SearchaliciousEvents {
*/
export enum BasicEvents {
CLICK = 'click',
+ CHANGE = 'change',
}
/**