Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor(topicsSelection): use headless ui component #519

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
152 changes: 102 additions & 50 deletions frontend/components/card/CardTopicSelection.vue
Original file line number Diff line number Diff line change
@@ -1,61 +1,113 @@
<template>
<div class="w-full card-style px-5 py-6">
<h2 class="block font-medium responsive-h3 mb-1">
<Combobox
v-model="value"
v-slot="{ open }"
multiple
as="div"
class="w-full card-style px-5 py-6"
>
<ComboboxLabel
as="h2"
class="block font-medium responsive-h3 mb-1 dark:text-dark-text"
>
{{ $t("components.card-topic-selection.header") }}
</h2>
<p>
{{ $t("components.card-topic-selection.topic-selection-prompt") }}
</p>
<input
v-model="formData.newTopic"
@keydown.enter="addTopic()"
@keydown.prevent.enter="addTopic()"
class="px-4 py-2 mt-2 w-full border rounded-md border-light-section-div dark:border-dark-section-div bg:light-content dark:bg-dark-content"
type="text"
name="newTopic"
placeholder="Add a new topic"
</ComboboxLabel>

<ComboboxInput
@change="query = $event.target.value"
:display-value="() => query"
placeholder="Select a topic"
class="py-2 w-full bg-transparent border-b-2 dark:text-dark-special-text"
/>
<ul class="list-none flex items-center gap-2 pt-2">
<li
v-for="topic in formData.topics"
class="bg-light-placeholder dark:bg-dark-placeholder py-1 px-2 rounded-md text-white flex items-center"

<ComboboxOptions
static
class="flex flex-col gap-1 mt-2 md:flex-row md:items-center"
>
<ComboboxOption
v-for="topic of filteredTopics"
v-slot="{ active, selected }"
:key="topic.value"
:value="topic.value"
as="template"
>
{{ topic }}
<button @click="removeTopic(topic)" class="ml-2">
<svg
class="w-4 h-4"
fill="none"
stroke="currentColor"
viewBox="0 0 24 24"
xmlns="http://www.w3.org/2000/svg"
>
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M6 18L18 6M6 6l12 12"
></path>
</svg>
</button>
</li>
</ul>
</div>
<li
class="flex justify-between items-center gap-2 rounded-lg p-2 border bg-light-btn border-dark-btn font-bold cursor-pointer hover:bg-light-cta-orange-hover hover:dark:bg-dark-cta-orange-hover"
:class="{
'outline outline-blue-400 bg-light-cta-orange-hover dark:bg-dark-cta-orange-hover':
active && open,
'bg-light-cta-orange dark:bg-dark-cta-orange': selected,
}"
>
<span class="flex items-center gap-2">
<Icon :name="topic.icon" size="20" />
{{ $t(topic.label) }}
</span>
<Icon
v-if="selected"
name="bi:x-lg"
class="cursor-pointer"
size="20"
/>
</li>
</ComboboxOption>
</ComboboxOptions>
</Combobox>
</template>

<script setup lang="ts">
const formData = ref({
topics: ["justice", "activism"],
newTopic: "",
import {
Combobox,
ComboboxInput,
ComboboxOption,
ComboboxOptions,
} from "@headlessui/vue";

import { GLOBAL_TOPICS, Topic, TopicsTag } from "~/types/topics";

const props = defineProps({
modelValue: {
type: Array as PropType<Topic[]>,
required: true,
default: () => [],
},
});

const emit = defineEmits(["update:modelValue"]);

const value = computed<Topic[]>({
get() {
return props.modelValue;
},
set(value: Topic[]) {
emit("update:modelValue", value);
},
});

const addTopic = () => {
if (formData.value.newTopic) {
formData.value.topics.push(formData.value.newTopic);
formData.value.newTopic = "";
}
};
const query = ref("");

const topics = computed((): TopicsTag[] => {
return [
...selectedTopicTags.value,
...GLOBAL_TOPICS.filter((topic) => !isActiveTopic(topic.value)),
];
});

const selectedTopicTags = computed(() => {
return value.value
.map((topic) => {
return GLOBAL_TOPICS.find((tag) => tag.value === topic);
})
.filter((tag) => tag) as TopicsTag[];
});

const filteredTopics = computed(() => {
return topics.value.filter((topic) => {
return topic.value.includes(query.value.trim().toLowerCase());
});
});

const removeTopic = (topic: string) => {
formData.value.topics = formData.value.topics.filter((t) => t !== topic);
};
function isActiveTopic(topic: Topic) {
return value.value.includes(topic);
}
</script>
4 changes: 4 additions & 0 deletions frontend/i18n/en-US.json
Original file line number Diff line number Diff line change
Expand Up @@ -245,6 +245,10 @@
"components.view-selector.view-as-grid-aria-label": "View as grid",
"components.view-selector.view-as-list-aria-label": "View as list",
"components.view-selector.view-as-map-aria-label": "View as map",
"global.topics.activism": "Activism",
"global.topics.justice": "Justice",
"global.topics.environment": "Environment",
"global.topics.education": "Education",
"pages.about.activist.header": "About activist.org",
"pages.about.activist.img-alt-text": "The activist icon: a lower case letter a with a half circle pointing down and to the left in the center.",
"pages.about.activist.section-1-paragraph-1": "We're a Berlin-based team with a growing global network of affiliates and partners. Our goal is to create and maintain a platform that enables more people to safely engage in activism, learn from experienced organizers as well as replicate proven and novel strategies and tactics for social and ecological change.",
Expand Down
3 changes: 2 additions & 1 deletion frontend/pages/organizations/create.vue
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@
:placeholder="$t('pages.organizations.create.tagline-placeholder')"
/>
</div>
<CardTopicSelection class="mt-5" />
<CardTopicSelection v-model="formData.topics" class="mt-5" />
<div class="mx-14 w-full mt-5">
<CardConnect
:social-links="formData.social_accounts"
Expand Down Expand Up @@ -119,8 +119,9 @@
topics: ["justice", "activism"],
});


const submit = async () => {
const { data: responseData } = await useFetch(

Check warning on line 124 in frontend/pages/organizations/create.vue

View workflow job for this annotation

GitHub Actions / Run PR Frontend Check

'responseData' is assigned a value but never used
"http://127.0.0.1:8000/v1/entities/organizations/",
{
method: "POST",
Expand Down
35 changes: 35 additions & 0 deletions frontend/types/topics.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
export enum Topic {
JUSTICE = "justice",
ACTIVISM = "activism",
EDUCATION = "education",
ENVIRONMENT = "environment",
}

export interface TopicsTag {
label: string;
icon: string;
value: Topic;
}

export const GLOBAL_TOPICS: TopicsTag[] = [
{
icon: "material-symbols:volunteer-activism",
value: Topic.ACTIVISM,
label: `global.topics.${Topic.ACTIVISM}`,
},
{
icon: "mdi:justice",
value: Topic.JUSTICE,
label: `global.topics.${Topic.JUSTICE}`,
},
{
icon: "mdi:environment",
value: Topic.ENVIRONMENT,
label: `global.topics.${Topic.ENVIRONMENT}`,
},
{
icon: "bi:book",
value: Topic.EDUCATION,
label: `global.topics.${Topic.EDUCATION}`,
},
];
Loading