Loading src/Playground.vue +12 −3 Original line number Diff line number Diff line Loading @@ -116,10 +116,14 @@ const tableColumns: ITableColumn[] = [ { name: 'Is gay?', type: 'checkbox', filterable: true, sortable: true, }, { name: 'Status', type: 'select', filterable: true, sortable: true, options: { options: [{ value: 'Married' }, { value: 'Oh no...(s)he is dead' }], theme: 'black', Loading @@ -128,6 +132,8 @@ const tableColumns: ITableColumn[] = [ { name: 'Children', type: 'rating', filterable: true, sortable: true, options: { theme: 'yellow', }, Loading @@ -135,6 +141,8 @@ const tableColumns: ITableColumn[] = [ { name: 'Job progress', type: 'progressBar', filterable: true, sortable: true, options: { theme: 'red', size: 'small', Loading @@ -143,7 +151,8 @@ const tableColumns: ITableColumn[] = [ { name: 'Strength', type: 'knob', options: {}, filterable: true, sortable: true, }, ]; const tableData = ref([ Loading @@ -164,6 +173,7 @@ const selectOptions = [ ]; const knob = ref(0); const pbValue = ref(0); const openDrawer = () => (visibleDrawer.value = true); </script> <template> Loading Loading @@ -200,7 +210,6 @@ const pbValue = ref(0); v-model="tableData" theme="black" stripedRows editable paginator :no-editing-settings="{ cells: [[0, 0]], Loading @@ -208,7 +217,7 @@ const pbValue = ref(0); :handlers="[ { cell: [0, 0], handler: () => (visibleDrawer = true), handler: () => openDrawer(), }, ]" ></Table> Loading src/common/interfaces/componentsProp.ts +1 −0 Original line number Diff line number Diff line Loading @@ -11,6 +11,7 @@ export interface ITableColumn { name: string; options?: ITableColumnOptions; type?: TTableColumnType; width?: number; editable?: boolean; filterable?: boolean; sortable?: boolean; Loading src/components/Table/Table.stories.ts +3 −23 Original line number Diff line number Diff line Loading @@ -95,28 +95,8 @@ export const Simple: Story = { }, ], data: [ [ { value: 'Pete', }, { value: '30', }, { value: 'Chess', }, ], [ { value: 'John', }, { value: '25', }, { value: 'Football', }, ], ['Pete', '30', 'Chess'], ['John', '25', 'Football'], ], }, }; Loading @@ -133,6 +113,7 @@ export const Full: Story = { type: 'number', filterable: true, sortable: true, width: '50px', }, { name: 'Hobbies', Loading @@ -154,7 +135,6 @@ export const Full: Story = { type: 'select', options: { options: [{ value: 'Married' }, { value: 'Oh no...(s)he is dead' }], theme: 'sky', }, }, { Loading src/components/Table/Table.vue +120 −77 Original line number Diff line number Diff line Loading @@ -6,6 +6,7 @@ import { calcAdditionalHeight, calcGap, calcRows } from '@components/Table/helpe import TableHeader from '@components/Table/components/TableHeader.vue'; import TableCell from '@components/Table/components/TableCell.vue'; import Paginator from '@components/Paginator/Paginator.vue'; import ToggleSwitch from '@components/ToggleSwitch/ToggleSwitch.vue'; const props = withDefaults(defineProps<ITableProps>(), { size: 'normal', Loading @@ -18,7 +19,10 @@ const data = defineModel() as Ref<unknown[][]>; const emit = defineEmits(['updateData']); const table = ref(); const currentPage = ref(1); const currentPage = ref<number>(1); const itemsPerPage = ref<number>(10); const isEditMode = ref<boolean>(props.editable); const columns = ref(props.columns); const sortStateActive = ref<[number, string] | []>([]); const indexColumnToFilter = ref<number>(0); Loading Loading @@ -49,6 +53,8 @@ const sortState = computed<string[]>(() => { const rows = computed<unknown[][]>(() => calcRows( data.value, currentPage.value, itemsPerPage.value, sortStateActive.value, props.multipleSort, indexColumnToFilter.value, Loading @@ -58,7 +64,7 @@ const rows = computed<unknown[][]>(() => ), ); const types = computed(() => props.columns.map((column) => column.type)); const paginatorContainerHeight = computed(() => (props.paginator || props.editable ? '50px' : '0')); const themeColor = computed(() => convertThemeToColor(props.theme, props.darknessTheme)); const color = computed(() => props.textColor Loading Loading @@ -107,7 +113,6 @@ const updateData = (newValue: Ref<unknown>, rowIndex: number, columnIndex: numbe </script> <template> <div :style="`background-color: ${themeColor}; color: ${color}`"> <table :class="{ tableLines: showAllLines, Loading Loading @@ -135,6 +140,7 @@ const updateData = (newValue: Ref<unknown>, rowIndex: number, columnIndex: numbe :showAllLines="!!showAllLines" :center="!!center" :fontSize="fontSize" :isEditMode="isEditMode" @changeColumnSortMode="changeColumnSortMode" @setFilter="setFilter" @cancelFilter="cancelFilter" Loading @@ -146,45 +152,58 @@ const updateData = (newValue: Ref<unknown>, rowIndex: number, columnIndex: numbe :key="rowIndex" :class="{ noEdit: !editable || !isEditMode || (noEditingSettings?.rows && noEditingSettings?.rows.find((i) => data?.[i]?.join('') === row.join(''))), }" > <td v-for="(item, columnIndex) of row" :key="columnIndex" @click="handlers?.find((i) => i.cell?.[0] === rowIndex && i.cell?.[1] === columnIndex)?.handler" @click=" handlers ? handlers?.find((i) => i.cell?.[0] === rowIndex && i.cell?.[1] === columnIndex)?.handler() : null " :class="{ leftBorder: showAllLines, darkRow: stripedRows && rowIndex % 2, noEdit: !editable || (noEditingSettings?.columns && ~noEditingSettings.columns?.indexOf(columnIndex)), pointer: handlers?.find((i) => i.cell?.[0] === rowIndex && i.cell?.[1] === columnIndex), darkRow: stripedRows && !(rowIndex % 2), noEdit: !isEditMode || (noEditingSettings?.columns && ~noEditingSettings.columns?.indexOf(columnIndex)), pointer: handlers && handlers?.find((i) => i.cell?.[0] === rowIndex && i.cell?.[1] === columnIndex), }" :style="`padding: calc(${initGap} / 2 + ${additionalHeightFromSize}) ${initGap}`" > <TableCell :item="item" :types="types" :columns="columns" :column="columns[columnIndex]" :rowIndex="rowIndex" :columnIndex="columnIndex" :center="center" :editable="editable" :isEditMode="isEditMode" :noEditingSettings="noEditingSettings?.cells" :fontSize="fontSize" :initGap="initGap" :knobWidth="knobWidth" :noEdit="!!handlers?.find((i) => i.cell?.[0] === rowIndex && i.cell?.[1] === columnIndex)" :noEdit=" handlers ? !!handlers?.find((i) => i.cell?.[0] === rowIndex && i.cell?.[1] === columnIndex) : false " :theme="theme" @updateData="updateData" /> </td> </tr> </tbody> </table> <div class="paginatorContainer"> <section v-if="editable" class="editMenu"> <p class="editText">Edit mode:</p> <ToggleSwitch v-model="isEditMode" negativeTheme="red" /> </section> <Paginator v-show="paginator" v-model="currentPage" v-model:current="currentPage" v-model:itemsPerPage="itemsPerPage" :theme="theme" :total="data.length" :itemsPerPageOptions="[2, 5]" v-bind="paginatorOptions" class="paginator" /> Loading @@ -194,10 +213,35 @@ const updateData = (newValue: Ref<unknown>, rowIndex: number, columnIndex: numbe <style scoped> .table { border-collapse: collapse; } table * { position: relative; * { font-size: v-bind(fontSize); } } .table::after { content: ''; position: absolute; top: 100%; z-index: -1; width: 100%; height: v-bind(paginatorContainerHeight); background-color: v-bind(themeColor); } .editMenu { display: flex; align-items: center; gap: 10px; width: max-content; padding: 10px; } .editText { color: v-bind(color); } .paginatorContainer { height: 50px; display: flex; align-items: center; } tr { position: relative; } Loading @@ -216,15 +260,14 @@ tr::after { } .cell { display: flex; width: 100%; height: 100%; margin: 0 auto; } .cellCenter { justify-content: center; align-items: center; } .paginator { display: block; margin: 0 auto; } .leftBorder { Loading src/components/Table/components/TableCell.vue +73 −40 Original line number Diff line number Diff line Loading @@ -6,19 +6,22 @@ import Select from '@components/Select/Select.vue'; import Rating from '@components/Rating/Rating.vue'; import ProgressBar from '@components/ProgressBar/ProgressBar.vue'; import Knob from '@components/Knob/Knob.vue'; import type { TThemeColor } from '@interfaces/common'; interface IProps { item: unknown; types: (TTableColumnType | undefined)[]; columns: ITableColumn[]; column: ITableColumn; rowIndex: number; columnIndex: number; center: boolean | undefined; editable: boolean; isEditMode: boolean; fontSize: string; initGap: string; knobWidth: string; noEditingSettings: [number, number][] | undefined; noEdit: boolean; theme: TThemeColor; } defineProps<IProps>(); defineEmits(['updateData']); Loading @@ -26,14 +29,16 @@ defineEmits(['updateData']); <template> <div :style="`width: calc(${column.width ?? 'auto'} - 2 * ${initGap})`" :class="[ 'cell', { cellCenter: center, noEdit: noEdit || !editable || noEditingSettings?.find((i: [number, number]) => i[0] === rowIndex && i[1] === columnIndex), !isEditMode || (noEditingSettings && noEditingSettings?.find((i: [number, number]) => i[0] === rowIndex && i[1] === columnIndex)), }, ]" > Loading @@ -43,11 +48,12 @@ defineEmits(['updateData']); @input="(event) => $emit('updateData', event.target, rowIndex, columnIndex)" :id="`${rowIndex}-${columnIndex}`" :type="types[columnIndex]" :style="`display: inline; width: 100%; text-align: ${center ? 'center' : 'auto'}`" :style="`width: 100%; text-align: ${center ? 'center' : 'auto'}`" /> <div v-else-if="isEditMode"> <Checkbox v-else-if="types[columnIndex] === 'checkbox'" v-bind="filterCheckboxProps(columns[columnIndex].options)" v-if="types[columnIndex] === 'checkbox'" v-bind="filterCheckboxProps(column.options)" :active="item as boolean" @update="$emit('updateData', $event, rowIndex, columnIndex)" /> Loading @@ -55,32 +61,59 @@ defineEmits(['updateData']); v-else-if="types[columnIndex] === 'select'" noBorder noSelectedBackground v-bind="filterSelectProps(columns[columnIndex].options)" v-bind="filterSelectProps(column.options)" width="150px" :theme="theme" :selected="item as string" @update="$emit('updateData', $event, rowIndex, columnIndex)" /> <Rating v-else-if="types[columnIndex] === 'rating'" v-bind="columns[columnIndex].options" v-bind="column.options" :value="item as number" @update="$emit('updateData', $event, rowIndex, columnIndex)" /> <ProgressBar v-else-if="types[columnIndex] === 'progressBar'" v-bind="columns[columnIndex].options" v-bind="column.options" :value="item as number" @update="$emit('updateData', $event, rowIndex, columnIndex)" /> <Knob v-else-if="types[columnIndex] === 'knob'" v-bind="columns[columnIndex].options" v-bind="column.options" :value="item as number" :width="knobWidth" :fontSize="fontSize" @update="$emit('updateData', $event, rowIndex, columnIndex)" /> </div> <div v-else> <Checkbox v-if="types[columnIndex] === 'checkbox'" v-bind="filterCheckboxProps(column.options)" :active="item as boolean" /> <Select v-else-if="types[columnIndex] === 'select'" noBorder noSelectedBackground v-bind="filterSelectProps(column.options)" width="150px" :theme="theme" :selected="item as string" /> <Rating v-else-if="types[columnIndex] === 'rating'" v-bind="column.options" :value="item as number" /> <ProgressBar v-else-if="types[columnIndex] === 'progressBar'" v-bind="column.options" :value="item as number" /> <Knob v-else-if="types[columnIndex] === 'knob'" v-bind="column.options" :value="item as number" :width="knobWidth" :fontSize="fontSize" /> </div> </div> </template> <style scoped> Loading Loading
src/Playground.vue +12 −3 Original line number Diff line number Diff line Loading @@ -116,10 +116,14 @@ const tableColumns: ITableColumn[] = [ { name: 'Is gay?', type: 'checkbox', filterable: true, sortable: true, }, { name: 'Status', type: 'select', filterable: true, sortable: true, options: { options: [{ value: 'Married' }, { value: 'Oh no...(s)he is dead' }], theme: 'black', Loading @@ -128,6 +132,8 @@ const tableColumns: ITableColumn[] = [ { name: 'Children', type: 'rating', filterable: true, sortable: true, options: { theme: 'yellow', }, Loading @@ -135,6 +141,8 @@ const tableColumns: ITableColumn[] = [ { name: 'Job progress', type: 'progressBar', filterable: true, sortable: true, options: { theme: 'red', size: 'small', Loading @@ -143,7 +151,8 @@ const tableColumns: ITableColumn[] = [ { name: 'Strength', type: 'knob', options: {}, filterable: true, sortable: true, }, ]; const tableData = ref([ Loading @@ -164,6 +173,7 @@ const selectOptions = [ ]; const knob = ref(0); const pbValue = ref(0); const openDrawer = () => (visibleDrawer.value = true); </script> <template> Loading Loading @@ -200,7 +210,6 @@ const pbValue = ref(0); v-model="tableData" theme="black" stripedRows editable paginator :no-editing-settings="{ cells: [[0, 0]], Loading @@ -208,7 +217,7 @@ const pbValue = ref(0); :handlers="[ { cell: [0, 0], handler: () => (visibleDrawer = true), handler: () => openDrawer(), }, ]" ></Table> Loading
src/common/interfaces/componentsProp.ts +1 −0 Original line number Diff line number Diff line Loading @@ -11,6 +11,7 @@ export interface ITableColumn { name: string; options?: ITableColumnOptions; type?: TTableColumnType; width?: number; editable?: boolean; filterable?: boolean; sortable?: boolean; Loading
src/components/Table/Table.stories.ts +3 −23 Original line number Diff line number Diff line Loading @@ -95,28 +95,8 @@ export const Simple: Story = { }, ], data: [ [ { value: 'Pete', }, { value: '30', }, { value: 'Chess', }, ], [ { value: 'John', }, { value: '25', }, { value: 'Football', }, ], ['Pete', '30', 'Chess'], ['John', '25', 'Football'], ], }, }; Loading @@ -133,6 +113,7 @@ export const Full: Story = { type: 'number', filterable: true, sortable: true, width: '50px', }, { name: 'Hobbies', Loading @@ -154,7 +135,6 @@ export const Full: Story = { type: 'select', options: { options: [{ value: 'Married' }, { value: 'Oh no...(s)he is dead' }], theme: 'sky', }, }, { Loading
src/components/Table/Table.vue +120 −77 Original line number Diff line number Diff line Loading @@ -6,6 +6,7 @@ import { calcAdditionalHeight, calcGap, calcRows } from '@components/Table/helpe import TableHeader from '@components/Table/components/TableHeader.vue'; import TableCell from '@components/Table/components/TableCell.vue'; import Paginator from '@components/Paginator/Paginator.vue'; import ToggleSwitch from '@components/ToggleSwitch/ToggleSwitch.vue'; const props = withDefaults(defineProps<ITableProps>(), { size: 'normal', Loading @@ -18,7 +19,10 @@ const data = defineModel() as Ref<unknown[][]>; const emit = defineEmits(['updateData']); const table = ref(); const currentPage = ref(1); const currentPage = ref<number>(1); const itemsPerPage = ref<number>(10); const isEditMode = ref<boolean>(props.editable); const columns = ref(props.columns); const sortStateActive = ref<[number, string] | []>([]); const indexColumnToFilter = ref<number>(0); Loading Loading @@ -49,6 +53,8 @@ const sortState = computed<string[]>(() => { const rows = computed<unknown[][]>(() => calcRows( data.value, currentPage.value, itemsPerPage.value, sortStateActive.value, props.multipleSort, indexColumnToFilter.value, Loading @@ -58,7 +64,7 @@ const rows = computed<unknown[][]>(() => ), ); const types = computed(() => props.columns.map((column) => column.type)); const paginatorContainerHeight = computed(() => (props.paginator || props.editable ? '50px' : '0')); const themeColor = computed(() => convertThemeToColor(props.theme, props.darknessTheme)); const color = computed(() => props.textColor Loading Loading @@ -107,7 +113,6 @@ const updateData = (newValue: Ref<unknown>, rowIndex: number, columnIndex: numbe </script> <template> <div :style="`background-color: ${themeColor}; color: ${color}`"> <table :class="{ tableLines: showAllLines, Loading Loading @@ -135,6 +140,7 @@ const updateData = (newValue: Ref<unknown>, rowIndex: number, columnIndex: numbe :showAllLines="!!showAllLines" :center="!!center" :fontSize="fontSize" :isEditMode="isEditMode" @changeColumnSortMode="changeColumnSortMode" @setFilter="setFilter" @cancelFilter="cancelFilter" Loading @@ -146,45 +152,58 @@ const updateData = (newValue: Ref<unknown>, rowIndex: number, columnIndex: numbe :key="rowIndex" :class="{ noEdit: !editable || !isEditMode || (noEditingSettings?.rows && noEditingSettings?.rows.find((i) => data?.[i]?.join('') === row.join(''))), }" > <td v-for="(item, columnIndex) of row" :key="columnIndex" @click="handlers?.find((i) => i.cell?.[0] === rowIndex && i.cell?.[1] === columnIndex)?.handler" @click=" handlers ? handlers?.find((i) => i.cell?.[0] === rowIndex && i.cell?.[1] === columnIndex)?.handler() : null " :class="{ leftBorder: showAllLines, darkRow: stripedRows && rowIndex % 2, noEdit: !editable || (noEditingSettings?.columns && ~noEditingSettings.columns?.indexOf(columnIndex)), pointer: handlers?.find((i) => i.cell?.[0] === rowIndex && i.cell?.[1] === columnIndex), darkRow: stripedRows && !(rowIndex % 2), noEdit: !isEditMode || (noEditingSettings?.columns && ~noEditingSettings.columns?.indexOf(columnIndex)), pointer: handlers && handlers?.find((i) => i.cell?.[0] === rowIndex && i.cell?.[1] === columnIndex), }" :style="`padding: calc(${initGap} / 2 + ${additionalHeightFromSize}) ${initGap}`" > <TableCell :item="item" :types="types" :columns="columns" :column="columns[columnIndex]" :rowIndex="rowIndex" :columnIndex="columnIndex" :center="center" :editable="editable" :isEditMode="isEditMode" :noEditingSettings="noEditingSettings?.cells" :fontSize="fontSize" :initGap="initGap" :knobWidth="knobWidth" :noEdit="!!handlers?.find((i) => i.cell?.[0] === rowIndex && i.cell?.[1] === columnIndex)" :noEdit=" handlers ? !!handlers?.find((i) => i.cell?.[0] === rowIndex && i.cell?.[1] === columnIndex) : false " :theme="theme" @updateData="updateData" /> </td> </tr> </tbody> </table> <div class="paginatorContainer"> <section v-if="editable" class="editMenu"> <p class="editText">Edit mode:</p> <ToggleSwitch v-model="isEditMode" negativeTheme="red" /> </section> <Paginator v-show="paginator" v-model="currentPage" v-model:current="currentPage" v-model:itemsPerPage="itemsPerPage" :theme="theme" :total="data.length" :itemsPerPageOptions="[2, 5]" v-bind="paginatorOptions" class="paginator" /> Loading @@ -194,10 +213,35 @@ const updateData = (newValue: Ref<unknown>, rowIndex: number, columnIndex: numbe <style scoped> .table { border-collapse: collapse; } table * { position: relative; * { font-size: v-bind(fontSize); } } .table::after { content: ''; position: absolute; top: 100%; z-index: -1; width: 100%; height: v-bind(paginatorContainerHeight); background-color: v-bind(themeColor); } .editMenu { display: flex; align-items: center; gap: 10px; width: max-content; padding: 10px; } .editText { color: v-bind(color); } .paginatorContainer { height: 50px; display: flex; align-items: center; } tr { position: relative; } Loading @@ -216,15 +260,14 @@ tr::after { } .cell { display: flex; width: 100%; height: 100%; margin: 0 auto; } .cellCenter { justify-content: center; align-items: center; } .paginator { display: block; margin: 0 auto; } .leftBorder { Loading
src/components/Table/components/TableCell.vue +73 −40 Original line number Diff line number Diff line Loading @@ -6,19 +6,22 @@ import Select from '@components/Select/Select.vue'; import Rating from '@components/Rating/Rating.vue'; import ProgressBar from '@components/ProgressBar/ProgressBar.vue'; import Knob from '@components/Knob/Knob.vue'; import type { TThemeColor } from '@interfaces/common'; interface IProps { item: unknown; types: (TTableColumnType | undefined)[]; columns: ITableColumn[]; column: ITableColumn; rowIndex: number; columnIndex: number; center: boolean | undefined; editable: boolean; isEditMode: boolean; fontSize: string; initGap: string; knobWidth: string; noEditingSettings: [number, number][] | undefined; noEdit: boolean; theme: TThemeColor; } defineProps<IProps>(); defineEmits(['updateData']); Loading @@ -26,14 +29,16 @@ defineEmits(['updateData']); <template> <div :style="`width: calc(${column.width ?? 'auto'} - 2 * ${initGap})`" :class="[ 'cell', { cellCenter: center, noEdit: noEdit || !editable || noEditingSettings?.find((i: [number, number]) => i[0] === rowIndex && i[1] === columnIndex), !isEditMode || (noEditingSettings && noEditingSettings?.find((i: [number, number]) => i[0] === rowIndex && i[1] === columnIndex)), }, ]" > Loading @@ -43,11 +48,12 @@ defineEmits(['updateData']); @input="(event) => $emit('updateData', event.target, rowIndex, columnIndex)" :id="`${rowIndex}-${columnIndex}`" :type="types[columnIndex]" :style="`display: inline; width: 100%; text-align: ${center ? 'center' : 'auto'}`" :style="`width: 100%; text-align: ${center ? 'center' : 'auto'}`" /> <div v-else-if="isEditMode"> <Checkbox v-else-if="types[columnIndex] === 'checkbox'" v-bind="filterCheckboxProps(columns[columnIndex].options)" v-if="types[columnIndex] === 'checkbox'" v-bind="filterCheckboxProps(column.options)" :active="item as boolean" @update="$emit('updateData', $event, rowIndex, columnIndex)" /> Loading @@ -55,32 +61,59 @@ defineEmits(['updateData']); v-else-if="types[columnIndex] === 'select'" noBorder noSelectedBackground v-bind="filterSelectProps(columns[columnIndex].options)" v-bind="filterSelectProps(column.options)" width="150px" :theme="theme" :selected="item as string" @update="$emit('updateData', $event, rowIndex, columnIndex)" /> <Rating v-else-if="types[columnIndex] === 'rating'" v-bind="columns[columnIndex].options" v-bind="column.options" :value="item as number" @update="$emit('updateData', $event, rowIndex, columnIndex)" /> <ProgressBar v-else-if="types[columnIndex] === 'progressBar'" v-bind="columns[columnIndex].options" v-bind="column.options" :value="item as number" @update="$emit('updateData', $event, rowIndex, columnIndex)" /> <Knob v-else-if="types[columnIndex] === 'knob'" v-bind="columns[columnIndex].options" v-bind="column.options" :value="item as number" :width="knobWidth" :fontSize="fontSize" @update="$emit('updateData', $event, rowIndex, columnIndex)" /> </div> <div v-else> <Checkbox v-if="types[columnIndex] === 'checkbox'" v-bind="filterCheckboxProps(column.options)" :active="item as boolean" /> <Select v-else-if="types[columnIndex] === 'select'" noBorder noSelectedBackground v-bind="filterSelectProps(column.options)" width="150px" :theme="theme" :selected="item as string" /> <Rating v-else-if="types[columnIndex] === 'rating'" v-bind="column.options" :value="item as number" /> <ProgressBar v-else-if="types[columnIndex] === 'progressBar'" v-bind="column.options" :value="item as number" /> <Knob v-else-if="types[columnIndex] === 'knob'" v-bind="column.options" :value="item as number" :width="knobWidth" :fontSize="fontSize" /> </div> </div> </template> <style scoped> Loading