Skip to content

Commit

Permalink
feat: collapsible tab card
Browse files Browse the repository at this point in the history
  • Loading branch information
CarloBar1 authored and cristinecula committed Feb 11, 2024
1 parent 2f96ac2 commit 48287e6
Show file tree
Hide file tree
Showing 5 changed files with 240 additions and 142 deletions.
6 changes: 6 additions & 0 deletions package-lock.json

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

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@
"./next/*": "./src/next/*"
},
"dependencies": {
"@neovici/cosmoz-collapse": "^1.3.0",
"@neovici/cosmoz-router": "^11.0.0",
"@neovici/cosmoz-utils": "^6.0.0",
"@pionjs/pion": "^2.0.0",
Expand Down
138 changes: 93 additions & 45 deletions src/cosmoz-tab-card.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,18 @@
// @license Copyright (C) 2015 Neovici AB - Apache 2 License
import { html, component } from '@pionjs/pion';
import { html, component, useState, useEffect } from '@pionjs/pion';
import '@neovici/cosmoz-collapse';
import { when } from 'lit-html/directives/when.js';
import { css } from './utils';

const collapseIcon = html`<svg
width="16"
height="16"
viewBox="0 0 16 16"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<path d="M5 1L10 8L5 15" stroke="#101010" stroke-width="1.5" />
</svg>`;

/**
Expand All @@ -19,60 +32,95 @@ Custom property | Description | Default
`--cosmoz-tab-card-content-line-height` | Card content line height | `initial`
`--cosmoz-tab-card-content-padding` | Card content padding | `initial`
*/
const CosmozTabCard = ({ heading }) =>
html` <style>
:host {
display: block;
position: relative;
box-sizing: border-box;
background-color: #fff;
border-radius: 3px;
margin: 15px;
align-self: flex-start;
padding: var(--cosmoz-tab-card-padding, 0);
width: var(--cosmoz-tab-card-width, 300px);
box-shadow: var(
--cosmoz-shadow-2dp,
var(--shadow-elevation-2dp_-_box-shadow, 0 2px 4px 0 #e5e5e5)
);
}
#content {
line-height: var(--cosmoz-tab-card-content-line-height, initial);
padding: var(--cosmoz-tab-card-content-padding, initial);
}
#header {
display: flex;
align-items: center;
background-color: #fff;
cursor: default;
-webkit-tap-highlight-color: rgba(0, 0, 0, 0);
}
.heading {
font-family: inherit;
font-size: 17px;
font-weight: 400;
flex: 1;
margin: 0.67em 0 0;
}
</style>
<div id="header" part="header">
<h1 class="heading" part="heading">

const CosmozTabCard = (host) => {
const { heading, collapsable, collapsed: isCollapsed } = host,
[collapsed, setCollapsed] = useState(Boolean(isCollapsed)),
toggleCollapsed = () => collapsable && setCollapsed((c) => !c);

useEffect(() => {
host.toggleAttribute('collapsed', collapsed);
}, [collapsed]);

return html`<div id="header" part="header">
${when(
collapsable,
() => html`
<div @click=${toggleCollapsed} part="collapse-icon">
<slot name="collapse-icon">${collapseIcon}</slot>
</div>
`,
)}
<h1 class="heading" @click=${toggleCollapsed} part="heading">
${heading}<slot name="after-title"></slot>
</h1>
<slot name="card-actions"></slot>
</div>
<div id="content" part="content">
<slot></slot>
<cosmoz-collapse ?opened=${!collapsed}><slot></slot></cosmoz-collapse>
</div>`;
};

const style = css`
:host {
display: block;
position: relative;
box-sizing: border-box;
background-color: #fff;
border-radius: 3px;
margin: 15px;
align-self: flex-start;
padding: var(--cosmoz-tab-card-padding, 0);
width: var(--cosmoz-tab-card-width, 300px);
box-shadow: var(
--cosmoz-shadow-2dp,
var(--shadow-elevation-2dp_-_box-shadow, 0 2px 4px 0 #e5e5e5)
);
}
#content {
line-height: var(--cosmoz-tab-card-content-line-height, initial);
padding: var(--cosmoz-tab-card-content-padding, initial);
}
#header {
display: flex;
align-items: center;
gap: 8px;
background-color: #fff;
cursor: default;
-webkit-tap-highlight-color: rgba(0, 0, 0, 0);
}
.heading {
font-family: inherit;
font-size: 17px;
font-weight: 400;
flex: 1;
}
[part='collapse-icon'] {
order: var(--cosmoz-tab-card-collapse-icon-order);
transition: transform 250ms linear;
transform: rotate(90deg);
}
:host([collapsed]) [part='collapse-icon'] {
transform: rotate(0deg);
}
:host([collapsable]) [part='collapse-icon'],
:host([collapsable]) .heading {
cursor: pointer;
user-select: none;
}
`;

customElements.define(
'cosmoz-tab-card',
component(CosmozTabCard, {
observedAttributes: ['heading'],
observedAttributes: ['heading', 'collapsable', 'collapsed'],
styleSheets: [style],
}),
);
55 changes: 26 additions & 29 deletions src/utils.js
Original file line number Diff line number Diff line change
@@ -1,36 +1,33 @@
const isValid = tab => !tab.hidden && !tab.disabled,
import { tagged } from '@neovici/cosmoz-utils';

const isValid = (tab) => !tab.hidden && !tab.disabled,
/**
* Gets the element icon name.
*
* @param {HTMLElement} tab The tab to compute icon for
* @param {boolean} isSelected Is the tab selected
* @returns {string} Name of the element icon.
*/
getIcon = (tab, isSelected) => isSelected ? tab.selectedIcon ?? tab.icon : tab.icon,

getName = tab => tab.getAttribute('name'),

getIcon = (tab, isSelected) =>
isSelected ? tab.selectedIcon ?? tab.icon : tab.icon,
getName = (tab) => tab.getAttribute('name'),
/**
* Gets the element icon style property and value if icon color is
* set, otherwise return nothing.
*
* @param {HTMLElement} tab The tab to compute icon stype for
* @returns {string/void} Style color property and value for the icon.
*/
getIconStyle = tab => {
getIconStyle = (tab) => {
const iconColor = tab.iconColor ?? '#15b0d3';
return ['color: ' + iconColor, tab.iconStyle]
.join(';');
return ['color: ' + iconColor, tab.iconStyle].join(';');
},

valid = tabs => tabs.find(isValid),

valid = (tabs) => tabs.find(isValid),
choose = (tabs, selected) => {
if (selected == null) {
return valid(tabs);
}
const selectedTab = tabs.find(tab => getName(tab) === selected);
const selectedTab = tabs.find((tab) => getName(tab) === selected);
if (selectedTab == null) {
return valid(tabs);
}
Expand All @@ -44,21 +41,21 @@ const isValid = tab => !tab.hidden && !tab.disabled,
}
return fallback;
},
collect = slot => slot.assignedElements().flatMap(el => {
if (el.matches('cosmoz-tab')) {
return [el];
}
if (el.matches('slot')) {
return collect(el);
}
return [];
});
collect = (slot) =>
slot.assignedElements().flatMap((el) => {
if (el.matches('cosmoz-tab')) {
return [el];
}
if (el.matches('slot')) {
return collect(el);
}
return [];
}),
sheet = (...styles) => {
const cs = new CSSStyleSheet();
cs.replaceSync(styles.join(''));
return cs;
},
css = (strings, ...values) => sheet(tagged(strings, ...values));

export {
choose,
collect,
isValid,
getIcon,
getIconStyle,
getName
};
export { choose, collect, isValid, getIcon, getIconStyle, getName, sheet, css };
Loading

0 comments on commit 48287e6

Please sign in to comment.