Skip to content

Commit

Permalink
feat(cxl-ui): refactor light-card
Browse files Browse the repository at this point in the history
  • Loading branch information
Hener Hoop committed Oct 19, 2023
1 parent d2326ca commit 2a78f61
Show file tree
Hide file tree
Showing 5 changed files with 270 additions and 71 deletions.
165 changes: 121 additions & 44 deletions packages/cxl-ui/scss/cxl-light-card.scss
Original file line number Diff line number Diff line change
@@ -1,68 +1,145 @@
/* stylelint-disable value-no-vendor-prefix, property-no-vendor-prefix -- some of these are necessary for line-clamp implementation */
@use "~@conversionxl/cxl-lumo-styles/scss/mq";
@use "~@conversionxl/cxl-ui/scss/mixins";

:host {
min-width: 267px; // 3col widths on 1400px
max-width: 300px;
height: auto;
min-height: calc(3 * var(--lumo-space-xl));
padding: var(--lumo-space-m);

.container > .attributes {
display: none;
position: relative;
box-sizing: border-box;
display: flex;
justify-content: space-between;
width: 100%;
height: 100%;
min-width: 267px;
font-size: var(--lumo-font-size-s);
background: var(--lumo-tint);
border: 1px solid var(--lumo-contrast-10pct);
border-radius: var(--lumo-border-radius-l);
box-shadow: var(--lumo-box-shadow-xs);
break-inside: avoid;
transform: translateZ(0); // CSS columns @see https://stackoverflow.com/a/55110789/35946
}

:host([hidden]) {
display: none;
}

:host(:hover) {
border-color: var(--lumo-primary-color);
}

:host([portrait]) {
display: block;
width: 175px;
min-width: 0;
min-height: 220px;
}

:host([completed]) {
opacity: 0.6;
}

:host([theme~="minidegree"]) {
background-color: var(--lumo-contrast);
}

.badge-new {
position: absolute;
right: 5px;
bottom: 5px;
display: none;
padding: 2px 8px;
font-size: 14px;
font-weight: 700;
color: var(--lumo-primary-contrast-color);
border-radius: 40px;
background: var(--lumo-primary-color);
z-index: 2;

:host([new]) & {
display: block;
}
}

header {
.info {
.name {
font-size: var(--lumo-font-size-m);
font-weight: 600;
}
header {
display: flex;
align-items: start;
flex-direction: column;
justify-content: space-between;
padding: var(--lumo-space-m) var(--lumo-space-s) var(--lumo-space-s) var(--lumo-space-m);

.attributes {
display: flex;
flex-direction: column;
align-items: flex-start;
padding-top: var(--lumo-space-xs);
padding-bottom: 0;
gap: var(--lumo-space-xs);
}
:host([portrait]) {
padding: var(--lumo-space-m) 12px 8px 12px;
}

cxl-time {
margin-left: -3px; // to align with the instructor text
}
.name {
@include mixins.ellipsis-for-lines(2);
font-family: var(--lumo-font-family);
font-size: 15px;
font-style: normal;
font-weight: 600;
line-height: var(--lumo-line-height-xs);

:host([theme~="minidegree"]) & {
color: var(--lumo-tint);
}

.instructor-image {
height: 80px;
a {
color: var(--lumo-body-text-color);
text-decoration: none;

&:hover {
text-decoration: underline;
}
}
}
}

:host([completed]) {
opacity: 0.6;

[icon="lumo:checkmark"] {
display: inline-block;
margin-top: calc(var(--lumo-space-xs) * -1);
color: var(--lumo-primary-color);
}
}

::slotted(footer) {
display: flex;
flex-wrap: wrap;
gap: var(--lumo-space-xs);
.progress {
display: flex;
flex-direction: column;
justify-content: center;
font-size: 13px;
line-height: 18px;
color: var(--lumo-body-text-color);
}
}

:host([theme~="minidegree"]) {
.name {
color: var(--lumo-primary-contrast-color);
.avatar {
position: relative;
width: 100%;
max-width: 112px;

&:before {
content: '';
display: block;
padding-top: 100%;
}

.attributes {
color: var(--lumo-tint-40pct);
img {
position: absolute;
top: 0;
right: 0;
display: block;
width: 100%;
height: 100%;
object-fit: cover;
}

cxl-time {
margin-left: -3px; // to align with the instructor text
color: var(--lumo-tint-40pct);
:host([portrait]) & {
width: 100%;
max-width: none;

&:before {
display: none;
}

img {
position: relative;
}
}
}
128 changes: 114 additions & 14 deletions packages/cxl-ui/src/components/cxl-light-card.js
Original file line number Diff line number Diff line change
@@ -1,17 +1,40 @@
/* eslint-disable import/no-extraneous-dependencies */
import { html, nothing } from 'lit';
import { html, LitElement, nothing } from 'lit';
import { unsafeHTML } from 'lit/directives/unsafe-html.js';
import { customElement, property } from 'lit/decorators.js';
import '@conversionxl/cxl-lumo-styles';
import { customElement, property, state } from 'lit/decorators.js';

import '@vaadin/progress-bar';

import cxlLightCardStyles from '../styles/cxl-light-card-css.js';
import { CXLBaseCardElement } from './cxl-base-card.js';

@customElement('cxl-light-card')
export class CXLLightCardElement extends CXLBaseCardElement {
export class CXLLightCardElement extends LitElement {
static get styles() {
return [...super.styles, cxlLightCardStyles];
return [cxlLightCardStyles];
}

@state() _tagsHasChildren = false;

@property({ type: String }) id = '';

@property({ type: String }) theme = '';

@property({ type: String }) name = '';

@property({ type: String }) avatar = '';

@property({ type: String }) instructor = '';

@property({ type: Number }) progress = 0;

@property({ type: Number }) lessons = 0;

@property({ type: Boolean, reflect: true }) new = false;

@property({ type: Boolean, reflect: true }) portrait = false;

@property({ type: Boolean, reflect: true }) completed = false;

constructor() {
super();
this.showTimeIcon = true;
Expand All @@ -28,23 +51,100 @@ export class CXLLightCardElement extends CXLBaseCardElement {
this.requestUpdate('showTags', this._showTags);
}

_slotHasChildren(e) {
const slot = e.target;
const { name } = slot;
const children = slot.assignedNodes();
this[`_${name}HasChildren`] = !!children.length;
}

_renderBadge() {
return this.new ? html`<span class="badge-new">NEW</span>` : nothing;
}

_renderHeaderName() {
return html`
<div class="name" title=${this.name}>
${unsafeHTML(this.name)}${
this.completed ? html`<vaadin-icon icon="lumo:checkmark"></vaadin-icon>` : nothing
}
${unsafeHTML(this.name)}
${this.completed ? html`<vaadin-icon icon="lumo:checkmark"></vaadin-icon>` : nothing}
</div>
`;
}

_renderHeaderTags() {
if (this.showTags) {
return html`
<div class="tags">
${this.theme ? html`<span class="tag">${this.theme}</span>` : ''}
${this.theme && this._tagsHasChildren ? this.separator : ''}
<slot name="tags" @slotchange=${this._slotHasChildren}></slot>
</div>
`;
}

return nothing;
}

_renderHeaderProgress() {
if ( this.progress && this.lessons ) {
return html`
<div class="progress">
<span class="progress-title">
Completed ${this.progress} of ${this.lessons}
</span>
<vaadin-progress-bar value="${this.progress / this.lessons}">
Completed ${this.progress} of ${this.lessons}
</vaadin-progress-bar>
</div>
`;
}

return nothing;
}

_renderHeaderAttributes() {
if ( this.time || this.instructor ) {
return html`
<div class="attributes">
${this.time ? html`<cxl-time time=${this.time} ?show-icon=${this.showTimeIcon}></cxl-time>` : nothing}
${this.instructor ? html`<div class="instructor">By: ${this.instructor}</div>` : nothing}
</div>
`;
}

return nothing;
}

/**
* Renders the header of the component.
* @return {string} The rendered header HTML.
*/
_renderHeader() {
return html`
<header>
${this._renderHeaderTags()}
${this._renderHeaderName()}
${this._renderHeaderProgress()}
${this._renderHeaderAttributes()}
</header>
`;
}

_renderAvatar() {
return this.avatar ? html`<div class="avatar"><img src=${this.avatar} alt="${this.instructor}" /></div>` : nothing;
}

// eslint-disable-next-line class-methods-use-this
_renderFooter() {
return html`<footer><slot name="footer"></slot></footer>`;
}

render() {
return html`
<div class="container">
${this._renderHeader()}
<slot name="footer"></slot>
<vaadin-icon class="badge-new" icon="cxl:new"></vaadin-icon>
</div>
${this._renderBadge()}
${this._renderHeader()}
${this._renderFooter()}
${this._renderAvatar()}
`;
}
}
32 changes: 26 additions & 6 deletions packages/storybook/cxl-ui/cxl-light-card/default.stories.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,19 +10,39 @@ export default {
};

export const CXLLightCard = Template.bind({});
export const CXLLightCardWithProgress = Template.bind({});
export const CXLLightCardPortrait = Template.bind({});
export const CXLLightCardMinidegree = Template.bind({});
export const CXLLightCardFooter = Template.bind({});

CXLLightCard.args = {
theme: 'light-card',
theme: 'new-card',
name: 'Account based marketing',
instructor: 'Ton Wesseling',
avatar: 'https://cxl.com/institute/wp-content/uploads/2021/10/jordan-greene-instructor-headshot-1000x1000-transparent-bw-150x150.png',
completed: false,
portrait: false,
new: false,
};

CXLLightCardWithProgress.args = {
theme: 'new-card',
name: 'Account based marketing',
progress: 2,
lessons: 6,
avatar: 'https://cxl.com/institute/wp-content/uploads/2021/10/jordan-greene-instructor-headshot-1000x1000-transparent-bw-150x150.png',
completed: false,
portrait: false,
new: false,
};

CXLLightCardPortrait.args = {
theme: 'new-card',
name: 'Account based marketing',
time: '3h 00min',
instructor: 'Ton Wesseling',
avatar:
'https://cxl.com/institute/wp-content/uploads/2020/05/48192546_10156982340630746_8127333122065825792_n-wpv_400pxx400px_center_center.jpg',
footer: '',
avatar: 'https://cxl.com/institute/wp-content/uploads/2021/10/jordan-greene-instructor-headshot-1000x1000-transparent-bw-150x150.png',
completed: false,
portrait: true,
new: false,
};

CXLLightCardMinidegree.args = {
Expand Down
Loading

0 comments on commit 2a78f61

Please sign in to comment.