Skip to content

Commit

Permalink
[Release] HdTileSelect component #470
Browse files Browse the repository at this point in the history
# Changelog

## Features
- ✨ HdTileSelect component (#465)
  • Loading branch information
viniciuskneves authored Jun 26, 2020
2 parents 591dead + 55adcbe commit 2df6c37
Show file tree
Hide file tree
Showing 14 changed files with 640 additions and 3 deletions.
2 changes: 1 addition & 1 deletion .nvmrc
Original file line number Diff line number Diff line change
@@ -1 +1 @@
v10.20.1
v10.21.0
1 change: 1 addition & 0 deletions main.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ export { default as HdResponsive } from './src/components/HdResponsive.vue';
export { default as HdTable } from './src/components/HdTable.vue';
export { default as HdTabsMenu } from './src/components/HdTabsMenu.vue';
export { default as HdTagsList } from './src/components/HdTagsList.vue';
export { default as HdTileSelect } from './src/components/HdTileSelect.vue';
export { default as HdTimeslots } from './src/components/HdTimeslots.vue';
export { default as HdToast } from './src/components/HdToast.vue';
export { default as HdToggle } from './src/components/HdToggle.vue';
Expand Down
2 changes: 1 addition & 1 deletion package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "homeday-blocks",
"version": "11.1.2",
"version": "11.2.0",
"description": "A Vue component library built by Homeday's frontend team.",
"main": "main.js",
"repository": {
Expand Down
112 changes: 112 additions & 0 deletions src/components/HdTileSelect.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
<template>
<div class="field tile-select">
<div class="tile-select__items">
<hd-tile-select-item
v-for="item in itemsMapped"
:key="item.value"
:value="item.value"
:text="item.text"
:model="value"
@input="$emit('input', $event)"
/>
<hd-tile-select-editable-item
v-if="acceptNewValue"
v-bind="$attrs"
v-model="customInputModel"
:text="customInputText"
:model="value"
/>
</div>
</div>
</template>

<script>
/**
* @typedef ItemEntryObject
* @type {Object}
* @property {string} text - Text to be used in the tile
* @property {(string|number|boolean)} value - Value associated with the tile
*/
import _get from 'lodash/get';
import HdTileSelectItem from 'homeday-blocks/src/components/HdTileSelectItem.vue';
import HdTileSelectEditableItem from 'homeday-blocks/src/components/HdTileSelectEditableItem.vue';
export default {
name: 'HdTileSelect',
inheritAttrs: false,
components: {
HdTileSelectItem,
HdTileSelectEditableItem,
},
data() {
return {
customInputModel: '',
};
},
props: {
/** @type {(string|number|boolean|ItemEntryObject)[]} */
items: {
type: Array,
required: true,
validator(value) {
return value.length > 1;
},
},
value: {
type: [String, Number, Boolean],
default: '',
},
formatter: {
type: Function,
default: value => (String(value)),
},
acceptNewValue: {
type: Boolean,
default: false,
},
},
watch: {
customInputModel(newValue) {
this.$emit('input', newValue);
},
},
computed: {
itemsMapped() {
return this.items.map(item => ({
value: _get(item, 'value', item),
text: this.formatter(_get(item, 'text', item)),
}));
},
customInputText() {
if (this.customInputModel === '') {
return '+';
}
return this.formatter(this.customInputModel);
},
},
};
</script>

<style lang="scss" scoped>
@import "@/styles/mixins.scss";
$item-min-size: 100px;
$border-width: 1px;
.tile-select {
@include font('text-xsmall');
&__items {
display: grid;
grid-template-columns: repeat(auto-fit, minmax($item-min-size, 1fr));
grid-gap: $border-width;
background-color: getShade($quaternary-color, 60);
border: $border-width solid getShade($quaternary-color, 60);
border-radius: 4px;
font-weight: 600;
overflow: hidden;
}
}
</style>
97 changes: 97 additions & 0 deletions src/components/HdTileSelectEditableItem.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
<template>
<hd-tile-select-item
v-if='!isEditing'
class="tile-select-editable-item"
:class="{
'tile-select-editable-item--selected': isSelected,
}"
v-bind="$attrs"
:value="value"
@input="enableEditing"
/>
<input
v-else
class="tile-select-editable-item tile-select-editable-item--input"
:class="{
'tile-select-editable-item--selected': isSelected,
}"
ref="input"
:value="value"
v-bind="$attrs"
@input="$emit('input', $event.target.value)"
@focus="enableEditing"
@blur="disableEditing"
@keydown.enter.prevent.stop="disableEditing"
>
</template>

<script>
import HdTileSelectItem from 'homeday-blocks/src/components/HdTileSelectItem.vue';
export default {
name: 'HdTileSelectEditableItem',
inheritAttrs: false,
components: {
HdTileSelectItem,
},
props: {
value: {
type: [String, Number],
required: true,
},
},
data() {
return {
isEditing: false,
};
},
computed: {
isSelected() {
return this.$attrs.model === this.value;
},
},
methods: {
enableEditing() {
this.isEditing = true;
this.$nextTick(() => {
this.$refs.input.focus();
});
},
disableEditing() {
this.isEditing = false;
},
},
};
</script>

<style lang="scss" scoped>
@import "@/styles/mixins.scss";
.tile-select-editable-item {
background-color: getShade($quaternary-color, 40);
&--selected {
background-color: getShade($secondary-color, 110);
color: white;
}
&--input{
height: 100%;
width: 100%;
border: 0;
padding: 0;
font-family: inherit;
font-size: inherit;
text-align: center;
// Hide the browser's controls
-moz-appearance: textfield;
&::-webkit-outer-spin-button,
&::-webkit-inner-spin-button {
-webkit-appearance: none;
margin: 0;
}
}
}
</style>
79 changes: 79 additions & 0 deletions src/components/HdTileSelectItem.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
<template>
<button
class="tile-select-item"
type="button"
:class="{
'tile-select-item--selected': isSelected,
}"
@click="emitInput"
@keydown.enter.space.prevent="emitInput"
>
{{ text || value }}
</button>
</template>

<script>
export default {
name: 'HdTileSelectItem',
model: {
prop: 'model',
},
props: {
model: {
type: [String, Number, Boolean],
default: '',
},
value: {
type: [String, Number, Boolean],
required: true,
},
text: {
type: String,
default: '',
},
},
computed: {
isSelected() {
return this.model === this.value;
},
},
methods: {
emitInput() {
this.$emit('input', this.value);
},
},
};
</script>

<style lang="scss" scoped>
@import "@/styles/mixins.scss";
.tile-select-item {
cursor: pointer;
width: 100%;
background-color: white;
border: 0;
padding: $inset-m;
font-family: inherit;
font-size: inherit;
font-weight: inherit;
color: inherit;
&:hover {
color: $secondary-color;
box-shadow: 0 0 5px 1px rgba(0, 0, 0, .4);
z-index: 2;
}
&--selected {
background-color: getShade($secondary-color, 110);
color: white;
box-shadow: 0 0 5px 1px rgba(0, 0, 0, .4);
z-index: 1;
&:hover {
color: white;
}
}
}
</style>
Loading

0 comments on commit 2df6c37

Please sign in to comment.