Skip to content

Commit

Permalink
Merge branch 'main' of https://github.com/traPtitech/anke-to-v3-UI in…
Browse files Browse the repository at this point in the history
…to main
  • Loading branch information
cp-20 committed Dec 11, 2023
2 parents 7f5ae52 + 65efc28 commit 8f5c60c
Show file tree
Hide file tree
Showing 45 changed files with 13,342 additions and 1,519 deletions.
32 changes: 32 additions & 0 deletions .github/workflows/ci.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
name: Testing build Nuxt.js App

on:
pull_request:
push:
branches:
- main

jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3

- name: Setup Node.js
uses: actions/setup-node@v2
with:
node-version: 20.x

- name: Cache node_modules
uses: actions/cache@v3
env:
cache-name: cache-node-modules
with:
path: '**/node_modules'
key: ${{ runner.os }}-build-${{ env.cache-name }}-${{ hashFiles('**/package-lock.json') }}

- name: Install dependencies
run: npm ci

- name: Testing build App
run: npm run build
29 changes: 29 additions & 0 deletions assets/style/global.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
.p-inputtext::placeholder, .p-multiselect .p-multiselect-label.p-placeholder {
color: #d1c7c8;
}

.p-button.p-button-icon-only {
height: 3rem;
}

.p-dropdown-panel .p-dropdown-items .p-dropdown-item {
padding: 8px 16px;
}

.p-calendar .p-datepicker-trigger-icon, .p-multiselect-filter-container .p-multiselect-filter-icon {
transform: translateY(-50%);
right: 16px;
}

.p-multiselect-header .p-multiselect-close {
display: none;
}

.p-multiselect-panel .p-multiselect-header .p-multiselect-filter-container .p-inputtext {
padding: 8px;
}

.p-multiselect-panel .p-multiselect-items .p-multiselect-item {
height: 2.5rem !important;
padding: 0 16px;
}
3 changes: 3 additions & 0 deletions assets/style/variables.scss
Original file line number Diff line number Diff line change
@@ -1,2 +1,5 @@
$breakpoint-sm: 480px;
$breakpoint-md: 768px;
$breakpoint-lg: 1024px;

$color-primary: #b12929;
161 changes: 161 additions & 0 deletions components/new-questionnaire-form/choice-group.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,161 @@
<script lang="ts" setup>
import { Container, Draggable } from 'vue3-smooth-dnd';
const props = defineProps<{
modelValue: QuestionSettingsMultipleChoice['options'];
}>();
defineEmits<{
(
e: 'update:modelValue',
value: QuestionSettingsMultipleChoice['options'],
): void;
}>();
watch(
() => props.modelValue,
(currentValue, prevValue) => {
if (!process.client) return;
if (document === undefined) return;
if (prevValue === undefined) {
document
.querySelectorAll<HTMLInputElement>('.choice-group-label-input')[0]
?.focus();
return;
}
if (currentValue.length === prevValue.length) return;
const newValueIndex = currentValue.findIndex(
(c) => !prevValue.some((p) => p.id === c.id),
);
if (newValueIndex === -1) return;
// すぐフォーカスを当てるとレンダリング前(?)にフォーカスが当たって思った要素にフォーカスが当たらない
setTimeout(() => {
document
.querySelectorAll<HTMLInputElement>('.choice-group-label-input')
[newValueIndex]?.focus();
}, 10);
},
{ immediate: true },
);
</script>

<template>
<div>
<Container
drag-handle-selector=".drag-handle"
lock-axis="y"
@drop="
$emit(
'update:modelValue',
moveInArray(modelValue, $event.removedIndex, $event.addedIndex),
)
"
>
<Draggable
v-for="(option, index) in modelValue"
:key="option.id"
class="choice-group-element"
>
<div class="drag-handle">
<Icon
size="24px"
name="ic:outline-drag-indicator"
class="drag-icon"
/>
</div>
<slot />
<InputText
:model-value="option.label"
class="choice-group-label-input"
placeholder="選択肢"
@update:model-value="
$emit(
'update:modelValue',
replaceInArray(modelValue, index, {
...option,
label: $event,
}),
)
"
@keypress="
if ($event.key === 'Enter') {
$emit(
'update:modelValue',
insertToArray(modelValue, index + 1, {
id: createId(),
label: '',
}),
);
}
"
/>
<Button
outlined
class="delete-choice-button p-button-icon-only"
@click="
$emit(
'update:modelValue',
modelValue.filter((o) => o.id !== option.id),
)
"
>
<Icon size="24px" name="mdi:trash-can-outline" />
</Button>
</Draggable>
</Container>
<Button
class="add-choice-button"
outlined
@click="
$emit(
'update:modelValue',
modelValue.concat({ id: createId(), label: '' }),
)
"
>
<Icon size="24px" name="mdi:plus" />
<span>新しい選択肢を追加</span>
</Button>
</div>
</template>

<style lang="scss" scoped>
.delete-choice-button {
border-color: #e6d6d6;
}
.add-choice-button {
margin-top: 16px;
width: 100%;
font-weight: bold;
display: flex;
align-items: center;
gap: 8px;
justify-content: center;
}
/* smooth-dndのスタイルを上書きするために詳細度を上げる */
.choice-group-element.choice-group-element.choice-group-element {
display: flex;
align-items: center;
gap: 8px;
padding: 4px 0;
}
.drag-handle {
cursor: move;
}
.drag-icon {
color: #a38f91;
}
.choice-group-label-input {
flex: 1;
}
</style>
101 changes: 101 additions & 0 deletions components/new-questionnaire-form/form-control.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
<script lang="ts" setup>
import MultipleChoiceInput from './multiple-choice-input.vue';
import NumberInput from './number-input.vue';
import ScaleInput from './scale-input.vue';
import SingleChoiceInput from './single-choice-input.vue';
import TextInput from './text-input.vue';
import TextLongInput from './text-long-input.vue';
const props = defineProps<{
modelValue: QuestionSettings;
}>();
const emit = defineEmits<{
(e: 'update:modelValue', value: QuestionSettings): void;
(e: 'copy'): void;
(e: 'delete'): void;
}>();
const question = computed({
get: () => props.modelValue,
set: (value) => emit('update:modelValue', value),
});
const formComponents = {
text: TextInput,
'text-long': TextLongInput,
'multiple-choice': MultipleChoiceInput,
'single-choice': SingleChoiceInput,
number: NumberInput,
scale: ScaleInput,
} satisfies Record<QuestionSettings['type'], unknown>;
const formComponent = computed(() => formComponents[question.value.type]);
</script>

<template>
<div class="form-control-container">
<InputText v-model="question.title" placeholder="質問" />
<Textarea
v-model="question.description"
placeholder="詳細な説明 (任意)"
class="form-control-description"
/>

<component :is="formComponent" v-model="question" />
<div class="form-control-footer">
<div class="form-control-required-switch">
<label>必須</label>
<InputSwitch v-model="question.required"></InputSwitch>
</div>
<div class="form-control-footer-buttons">
<Button
class="p-button-icon-only p-button-text"
title="質問を複製"
@click="$emit('copy')"
>
<Icon size="24px" name="mdi:content-copy" />
</Button>
<Button
class="p-button-icon-only p-button-text"
title="質問を削除"
@click="$emit('delete')"
>
<Icon size="24px" name="mdi:trash-can-outline" />
</Button>
</div>
</div>
</div>
</template>

<style lang="scss" scoped>
.form-control-container {
display: flex;
flex-direction: column;
gap: 8px;
}
.form-control-description {
font-size: 14px;
}
.form-control-footer {
display: flex;
justify-content: flex-end;
align-items: center;
gap: 16px;
}
.form-control-footer-buttons {
display: flex;
align-items: center;
gap: 8px;
}
.form-control-required-switch {
display: flex;
align-items: center;
gap: 8px;
font-weight: bold;
}
</style>
58 changes: 58 additions & 0 deletions components/new-questionnaire-form/multi-select-input.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
<script lang="ts" setup>
import type {
MultiSelectAllChangeEvent,
MultiSelectChangeEvent,
} from 'primevue/multiselect';
type Item = {
label: string;
value: string;
};
const props = defineProps<{
modelValue: string[];
options: Item[];
placeholder?: string;
}>();
const emit = defineEmits<{
(e: 'update:modelValue', value: string[]): void;
}>();
const items = computed({
get: () => props.modelValue,
set: (value) => emit('update:modelValue', value),
});
const isAllSelected = ref(items.value.length === props.options.length);
const onSelectAllChange = (event: MultiSelectAllChangeEvent) => {
items.value = event.checked ? props.options.map(({ value }) => value) : [];
isAllSelected.value = event.checked;
};
const onChange = (event: MultiSelectChangeEvent) => {
isAllSelected.value = event.value.length === items.value.length;
};
</script>

<template>
<MultiSelect
v-model="items"
:options="options"
option-label="label"
option-value="value"
filter
:select-all="isAllSelected"
:virtual-scroller-options="{ itemSize: 50 }"
:placeholder="placeholder"
scroll-height="320px"
@selectall-change="onSelectAllChange($event)"
@change="onChange($event)"
>
<template #option="{ option, index }">
<slot name="option" :option="option" :index="index" />
</template>
</MultiSelect>
</template>

<style lang="scss" scoped></style>
Loading

0 comments on commit 8f5c60c

Please sign in to comment.