Skip to content

Commit

Permalink
feat(kviewswitcher): update k-view-switcher for vue 3 (#557)
Browse files Browse the repository at this point in the history
  • Loading branch information
adamdehaven authored Apr 5, 2022
1 parent f0c15a7 commit 5c5909c
Show file tree
Hide file tree
Showing 6 changed files with 373 additions and 1 deletion.
1 change: 1 addition & 0 deletions docs/.vuepress/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,7 @@ export default defineUserConfig<DefaultThemeOptions, ViteBundlerOptions>({
'/components/textarea',
'/components/toaster',
'/components/tooltip',
'/components/view-switcher',
]
},
{
Expand Down
128 changes: 128 additions & 0 deletions docs/components/view-switcher.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
---
pageClass: table-docs
turtleOptions:
headers:
- key: profile
hideLabel: true
- label: Name
key: name
- label: Age
key: age
- label: Color
key: color
---

# View Switcher

**KViewSwitcher** Is used to toggle list views to grid views.

<KComponent :data="{ currentView: 'table' }" v-slot="{ data }">
<KViewSwitcher :view="data.currentView" @view-changed="(view) => data.currentView = view"/>
</KComponent>

## Props

### view

The current view of your UI, one of `table` or `grid`. The button will show icons for the opposite. For example, if your data is currently in a list/table, passing `list` will render the grid icon.

<KComponent :data="{ currentView: 'grid' }" v-slot="{ data }">
<div>
<KCard class="mb-4">
<template v-slot:body>{{ data }}</template>
</KCard>
<KViewSwitcher :view="data.currentView" @view-changed="(view) => data.currentView = view"/>
</div>
</KComponent>

> The `KComponent` component is used in this example to create state.
```vue
<KComponent :data="{ currentView: 'grid' }" v-slot="{ data }">
<div>
<KCard class="mb-4">
<template v-slot:body>{{ data }}</template>
</KCard>
<KViewSwitcher
:view="data.currentView"
@view-changed="(view) => data.currentView = view"/>
</div>
</KComponent>
```

## Usage

`KViewSwitcher` will emit the new view on click. This then allows you to change the UI to the new view. The button will also toggle to the opposite view for users to switch back.

<KComponent :data="{ currentView: 'table', turtles: [{ name: 'Leonardo', age: 34, color: 'blue' }, { name: 'Michelangelo', age: 32, color: 'orange' }, { name: 'Raphael', age: 32, color: 'red' }, { name: 'Donatello', age: 29, color: 'purple' }] }" v-slot="{ data }">
<div>
<div class="d-flex align-items-center justify-content-between mb-4">
<h3>Teenage Mutant Ninja Turtles</h3>
<KViewSwitcher
:view="data.currentView"
@view-changed="(view) => data.currentView = view"/>
</div>
<div v-if="data.currentView === 'table'">
<KTable
:hasHover="false"
:hasSideBorder="false"
:options="{ headers: $frontmatter.turtleOptions.headers, data: data.turtles }">
<template v-slot:profile="{row}">
<img class="profile-pic" :src="getTurtlePic(row.name)" width="75" />
</template>
</KTable>
</div>
<div
v-if="data.currentView === 'grid'"
class="card-view">
<KCard
v-for="turtle in data.turtles"
:key="turtle.name">
<template v-slot:body>
<div class="mb-2"><strong>{{ turtle.name }}</strong></div>
<div class="mb-2">
<img class="profile-pic" :src="getTurtlePic(turtle.name)" width="100" />
</div>
<div class="mb-2">
<KBadge :background-color="turtle.color" color="var(--white)">{{ turtle.color }}</KBadge>
</div>
<div><strong>Age: </strong>{{ turtle.age }}</div>
</template>
</KCard>
</div>
</div>
</KComponent>

<script lang="ts">
import { defineComponent } from 'vue'

export default defineComponent({
methods: {
getTurtlePic (name) {
return `https://nick-intl.mtvnimages.com/uri/mgid:file:gsp:kids-assets:/nick/polls/images/tmnt-poll-crown-the-pizza-king-${name.toLowerCase()}.jpg?quality=0.75&height=150&width=150&crop=true`
}
}
})
</script>

<style lang="scss">
.table-docs .k-table {
display: table;
th, tr, td {
border: unset;
}
}

.profile-pic {
border-radius: 50%;
object-fit: center;
overflow: hidden;
}

.card-view {
display: grid;
grid-template-columns: repeat(4, 1fr);
grid-gap: 1rem;
text-align: center;
}
</style>
2 changes: 1 addition & 1 deletion src/components/KSkeleton/KSkeleton.vue
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
:columns="tableColumns"
:rows="tableRows"
>
<slot />
<slot name="default" />
</TableSkeleton>
<FormSkeleton v-else-if="type === 'form'" />
<FullScreenKongSkeleton
Expand Down
21 changes: 21 additions & 0 deletions src/components/KViewSwitcher/KViewSwitcher.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
// Import types for custom commands
/// <reference types="../../cypress/support" />

import { mount } from '@cypress/vue'
import KViewSwitcher from '@/components/KViewSwitcher/KViewSwitcher.vue'

describe('KViewSwitcher', () => {
it('toggles view when clicked', async () => {
mount(KViewSwitcher, {
props: {
view: 'table',
},
})

cy.get('.view-switch-button').should('have.class', 'table')
cy.get('.view-switch-button').click().then(() => {
cy.wrap(Cypress.vueWrapper.emitted()).should('have.property', 'view-changed')
// cy.get('.view-switch-button').should('have.class', 'grid')
})
})
})
221 changes: 221 additions & 0 deletions src/components/KViewSwitcher/KViewSwitcher.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,221 @@

<template>
<KButton
:is-rounded="false"
:class="[view, { paused: isPaused }]"
:title="`Toggle to ${view === 'table' ? 'grid' : 'table'} view`"
size="small"
appearance="outline"
class="k-view-switcher view-switch-button non-visual-button"
@click="toggleView"
>
<div class="icon">
<div class="dots">
<i
v-for="i in 4"
:key="i"
/>
</div>
<div class="lines">
<i
v-for="i in 4"
:key="i"
/>
</div>
</div>
</KButton>
</template>

<script lang="ts">
import { defineComponent, ref } from 'vue'
import KButton from '@/components/KButton/KButton.vue'
export default defineComponent({
name: 'KViewSwitcher',
components: { KButton },
props: {
view: {
type: String,
default: 'table',
required: true,
validator: (val: string): boolean => ['table', 'grid'].includes(val),
},
},
emits: ['view-changed'],
setup(props, { emit }) {
const isPaused = ref(true)
const toggleView = (): void => {
// Removed paused animations
isPaused.value = false
// Emit new view
emit('view-changed', props.view === 'table' ? 'grid' : 'table')
}
return {
isPaused,
toggleView,
}
},
})
</script>

<style lang="scss" scoped>
@import '@/styles/variables';
// Originally forked and modified from https://codepen.io/aaroniker/pen/dyoKeMP
.view-switch-button {
--KButtonPaddingY: 6px;
--KButtonPaddingX: 6px;
--KButtonSecondaryHover: var(--white);
--KButtonSecondaryHoverBorder: var(--blue-300);
--KButtonSecondaryFocus: none;
transform: scale(var(--scale, 1)) translateZ(0);
&.paused .icon i {
animation-duration: 0s;
}
.icon {
width: 1.5rem;
height: 1.5rem;
position: relative;
i {
position: absolute;
left: var(--left, 4px);
top: var(--top, 4px);
display: block;
border-radius: 2px;
width: var(--width, 7px);
height: var(--height, 7px);
background-color: var(--grey-500);
animation: var(--name, var(--dots-name, none)) var(--duration, var(--dots-duration, .5s)) var(--easing, var(--dots-easing, linear)) forwards var(--delay, var(--dots-delay, 0s));
transition: background-color 200ms ease;
}
.dots i {
&:nth-child(1) {
--x-middle: -8px;
--y-middle: 10px;
--x-end: -2px;
--y-end: 12px;
--x-back: 10px;
--y-back: 7px;
--x-back-end: 9px;
--y-back-end: 0;
}
&:nth-child(2) {
--left: 13px;
--x-middle: -12px;
--y-middle: 5px;
--x-end: -11px;
--y-end: 7px;
--x-back: -3px;
--y-back: 1px;
--x-back-end: -9px;
--y-back-end: 0;
}
&:nth-child(3) {
--top: 13px;
--x-middle: 4px;
--y-middle: -5px;
--x-end: -2px;
--y-end: -7px;
--x-back: -5px;
--y-back: 0px;
--x-back-end: 9px;
--y-back-end: 0;
}
&:nth-child(4) {
--left: 13px;
--top: 13px;
--x-middle: 0;
--y-middle: -10px;
--x-end: -11px;
--y-end: -12px;
--x-back: -14px;
--y-back: -8px;
--x-back-end: -9px;
--y-back-end: 0;
}
}
.lines {
--name: var(--lines-name, none);
--duration: var(--lines-duration, 0.15s);
--easing: var(--lines-easing, linear);
--delay: var(--lines-delay, 0s);
i {
--left: 9px;
--top: 3px;
--height: 2px;
--width: 11px;
transform-origin: 0 50%;
transform: translateY(20%) translateZ(0) scaleX(0);
&:nth-child(2) { --top: 8px; }
&:nth-child(3) { --top: 13px; }
&:nth-child(4) { --top: 18px; }
&:nth-child(3),
&:nth-child(4) { transform-origin: 100% 50%; }
}
}
}
&.table {
--dots-name: back;
--lines-name: scale-down;
.lines i {
transform-origin: 0 50%;
&:nth-child(3),
&:nth-child(4) { transform-origin: 100% 50%; }
}
}
&.grid {
--dots-name: move;
--lines-name: scale;
--lines-duration: 0.15s;
--lines-delay: 0.3s;
.lines i {
transform-origin: 100% 50%;
&:nth-child(3),
&:nth-child(4) { transform-origin: 0 50%; }
}
}
}
</style>

<style lang="scss">
// @keyframes animations need to be un-scoped
.k-view-switcher {
@keyframes move {
50% { transform: translate(var(--x-middle, 0), var(--y-middle, 0)) scale(.4); }
100% { transform: translate(var(--x-end, 0), var(--y-end, 0)) scale(.4); }
}
@keyframes back {
0%,
15% { transform: translate(var(--x-end, 0), var(--y-end, 0)) scale(.4); }
50% { transform: translate(var(--x-back, 0), var(--y-back, 0)) scale(.5); }
100% { transform: translate(var(--x-back-end, 0), var(--y-back-end, 0)) scale(1); }
}
@keyframes scale {
100% { transform: translateY(20%) translateZ(0) scaleX(1); }
}
@keyframes scale-down {
0% { transform: translateY(20%) translateZ(0) scaleX(1); }
100% { transform: translateY(20%) translateZ(0) scaleX(0); }
}
}
</style>
1 change: 1 addition & 0 deletions src/components/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,3 +32,4 @@ export { default as KTextArea } from './KTextArea/KTextArea.vue'
export { default as KCheckbox } from './KCheckbox/KCheckbox.vue'
export { default as KInlineEdit } from './KInlineEdit/KInlineEdit.vue'
export { default as KRadio } from './KRadio/KRadio.vue'
export { default as KViewSwitcher } from './KViewSwitcher/KViewSwitcher.vue'

0 comments on commit 5c5909c

Please sign in to comment.