Skip to content

Commit

Permalink
Equipment adding form
Browse files Browse the repository at this point in the history
Fixes #147
  • Loading branch information
Perdolique committed Oct 10, 2024
1 parent 549cda2 commit 3121e91
Show file tree
Hide file tree
Showing 37 changed files with 3,590 additions and 304 deletions.
1 change: 1 addition & 0 deletions app/app.vue
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,7 @@
// Reset default button styles
button {
border: none;
padding: 0;
}
}
</style>
1 change: 1 addition & 0 deletions app/assets/styles/_base.scss
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
--screen-mobile-m: #{$screen-mobile-m};
--screen-mobile-l: #{$screen-mobile-l};
--screen-tablet: #{$screen-tablet};
--screen-laptop: #{$screen-laptop};
--screen-desktop-s: #{$screen-desktop-s};
--screen-desktop-m: #{$screen-desktop-m};
--screen-desktop-l: #{$screen-desktop-l};
Expand Down
12 changes: 12 additions & 0 deletions app/assets/styles/_media.scss
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,20 @@
}
}

@mixin mobileMedium() {
@media screen and (min-width: $screen-mobile-m) {
@content;
}
}

@mixin tablet() {
@media screen and (min-width: $screen-tablet) {
@content;
}
}

@mixin laptop() {
@media screen and (min-width: $screen-laptop) {
@content;
}
}
1 change: 1 addition & 0 deletions app/assets/styles/_variables.scss
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ $screen-mobile-s: 320px;
$screen-mobile-m: 375px;
$screen-mobile-l: 414px;
$screen-tablet: 768px;
$screen-laptop: 1024px;
$screen-desktop-s: 1280px;
$screen-desktop-m: 1440px;
$screen-desktop-l: 1920px;
118 changes: 118 additions & 0 deletions app/components/ImageUpload.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
<template>
<button
type="button"
:class="$style.component"
@click="onButtonClick"
>
<img
v-if="imageFileObjectUrl"
:src="imageFileObjectUrl"
alt="Uploaded image"
:class="$style.image"
/>

<Icon
v-else
size="1.5rem"
name="tabler:upload"
:class="$style.icon"
/>

<input
type="file"
accept="image/png, image/jpeg, image/webp"
ref="fileInput"
:class="$style.input"
@change="onFileChange"
/>
</button>
</template>

<script lang="ts" setup>
const fileInputRef = useTemplateRef('fileInput');
const imageFile = ref<File | null>(null);
const imageFileObjectUrl = computed(() => {
if (imageFile.value === null) {
return null;
}
return URL.createObjectURL(imageFile.value);
});
function onButtonClick() {
fileInputRef.value?.click();
}
function onFileChange(event: Event) {
if (event.target instanceof HTMLInputElement) {
const file = event.target.files?.[0];
if (file !== undefined) {
imageFile.value = file;
}
}
}
// Revoke the old object URL when the image file changes
watch(imageFileObjectUrl, (_, oldObjectUrl) => {
if (oldObjectUrl !== null) {
URL.revokeObjectURL(oldObjectUrl);
}
});
// Revoke the object URL when the component is unmounted
onBeforeUnmount(() => {
if (imageFileObjectUrl.value !== null) {
URL.revokeObjectURL(imageFileObjectUrl.value);
}
});
</script>

<style module>
.component {
align-content: center;
outline: 2px solid transparent;
border: 1px solid var(--color-blue-500);
border-radius: var(--border-radius-24);
background-color: var(--color-blue-100);
cursor: pointer;
overflow: hidden;
transition:
background-color 0.2s,
border-color 0.2s,
outline-color 0.2s;
&:hover,
&:focus-visible{
background-color: var(--color-blue-200);
border-color: var(--color-blue-600);
outline-color: var(--color-blue-600);
}
}
.image {
width: 100%;
height: 100%;
object-fit: cover;
}
.icon {
display: block;
margin: 0 auto;
color: var(--color-blue-500);
transition:
color 0.2s,
scale 0.2s;
.component:hover &,
.component:focus-visible & {
color: var(--color-blue-600);
scale: 1.3;
}
}
.input {
display: none;
}
</style>
3 changes: 1 addition & 2 deletions app/components/PerdInput.vue
Original file line number Diff line number Diff line change
Expand Up @@ -64,15 +64,14 @@
.label {
position: absolute;
left: 0;
left: var(--input-spacing-horizontal);
top: 0;
bottom: 0;
right: 0;
display: flex;
align-items: center;
pointer-events: none;
color: var(--input-color-main);
left: var(--input-spacing-horizontal);
transform-origin: top left;
user-select: none;
transition:
Expand Down
2 changes: 1 addition & 1 deletion app/components/PerdMenu/OptionBase.vue
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@
background-color: var(--color-background);
color: var(--color-blue-600);
cursor: pointer;
transition: background-color 0.2s ease-out;
transition: background-color var(--transition-time-quick) ease-out;
font-size: var(--font-size-14);
white-space: nowrap;
Expand Down
128 changes: 128 additions & 0 deletions app/components/PerdSelect.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
<template>
<div :class="$style.component">
<Icon
name="tabler:chevron-down"
size="1.5rem"
:class="$style.arrow"
/>

<select
:class="$style.select"
v-model="selectedOption"
:required="required"
>
<option
disabled
value=""
class="placeholder"
:class="$style.option"
>
{{ placeholder }}
</option>

<option
v-for="option in options"
:key="option.value"
:value="option.value"
:selected="isSelected(option.value)"
:class="$style.option"
>
{{ option.label }}
</option>
</select>
</div>
</template>

<script lang="ts" setup>
interface Option {
readonly value: string;
readonly label: string;
}
interface Props {
readonly placeholder: string;
readonly options: readonly Option[];
readonly required: HTMLSelectElement['required'];
}
defineProps<Props>();
const selectedOption = defineModel<string>();
function isSelected(value: string) {
return selectedOption.value === value;
}
</script>

<style module>
.component {
position: relative;
}
.arrow {
position: absolute;
right: var(--input-spacing-horizontal);
top: 50%;
translate: 0 -50%;
pointer-events: none;
color: var(--input-secondary-color-text);
transition: rotate var(--transition-time-quick);
/* FIXME: experimental and shouldn't be in production */
.component:has(.select:open) & {
rotate: x 180deg;
}
}
.select {
appearance: none;
width: 100%;
outline: none;
height: var(--input-height);
padding: 0 calc(var(--input-spacing-horizontal) + 1.5rem) 0 var(--input-spacing-horizontal);
cursor: pointer;
border-radius: var(--input-border-radius);
background-color: var(--input-secondary-color-main);
color: var(--input-secondary-color-text);
border: 1px solid var(--input-secondary-color-border);
overflow: hidden;
text-overflow: ellipsis;
transition: background-color var(--transition-time-quick);
&:hover,
&:focus-visible {
background-color: var(--input-secondary-color-focus);
}
/* FIXME: experimental and shouldn't be in production */
&:open {
background-color: var(--input-secondary-color-active);
}
}
.option {
outline: none;
height: 40px;
padding: 0 var(--spacing-32) 0 var(--spacing-12);
background-color: var(--color-background);
color: var(--color-blue-600);
cursor: pointer;
transition: background-color var(--transition-time-quick) ease-out;
max-width: 100px;
overflow: hidden;
text-overflow: ellipsis;
&:hover,
&:focus-visible {
background-color: var(--color-blue-100);
}
&:checked {
background-color: var(--color-blue-200);
}
&:global(.placeholder) {
display: none;
}
}
</style>
Loading

0 comments on commit 3121e91

Please sign in to comment.