diff --git a/packages/cxl-lumo-styles/scss/color.scss b/packages/cxl-lumo-styles/scss/color.scss index 69eb2cb4d..f70ee2e01 100644 --- a/packages/cxl-lumo-styles/scss/color.scss +++ b/packages/cxl-lumo-styles/scss/color.scss @@ -27,6 +27,7 @@ html, */ --lumo-shade: hsl(0, 0%, 10%); --lumo-contrast: hsl(0, 0%, 10%); + --lumo-contrast-20pct: hsl(0, 0%, 20%); /** * Text diff --git a/packages/cxl-ui/scss/_mixins.scss b/packages/cxl-ui/scss/_mixins.scss index 2102b2e5d..dafd3fd1e 100644 --- a/packages/cxl-ui/scss/_mixins.scss +++ b/packages/cxl-ui/scss/_mixins.scss @@ -61,3 +61,17 @@ border-radius: var(--lumo-font-size-s); } } + +@mixin pesudo-element-full-width($color) { + &::before { + position: absolute; + top: 0; + bottom: 0; + left: 50%; + z-index: -1; + width: 100vw; + content: ""; + background-color: $color; + transform: translateX(-50%); + } +} diff --git a/packages/cxl-ui/scss/cxl-dashboard-team-header.scss b/packages/cxl-ui/scss/cxl-dashboard-team-header.scss new file mode 100644 index 000000000..0ebbb21b8 --- /dev/null +++ b/packages/cxl-ui/scss/cxl-dashboard-team-header.scss @@ -0,0 +1,82 @@ +@use "~@conversionxl/cxl-lumo-styles/scss/mq"; +@use "./mixins"; + +:host { + display: flex; + justify-content: center; + width: 100%; + color: var(--lumo-tint); + background-color: var(--lumo-shade); + + .container { + position: relative; + box-sizing: border-box; + width: 100%; + max-width: var(--cxl-content-max-width-wide); + padding: var(--lumo-space-l) var(--lumo-space-m); + background-color: var(--lumo-shade); + + @include mixins.pesudo-element-full-width(var(--lumo-shade)); + + @media #{mq.$small} { + display: flex; + justify-content: space-between; + } + + @media #{mq.$large} { + padding: var(--lumo-space-xl) 0; + } + } + + header { + margin-bottom: var(--lumo-space-l); + + @media #{mq.$medium} { + display: flex; + align-items: center; + justify-content: space-between; + margin-bottom: initial; + } + + .titles { + .title { + margin-block-start: 0; + margin-block-end: 0; + } + + @media #{mq.$medium} { + padding-top: 0; + } + } + + .subtitle { + font-size: var(--lumo-font-size-xs); + line-height: 1; + text-transform: uppercase; + opacity: 0.6; + } + } + + .actions { + display: flex; + flex-direction: column; + align-items: start; + gap: var(--lumo-space-s); + + @media #{mq.$small} { + flex-direction: row; + align-items: center; + gap: var(--lumo-space-m); + } + + .invite-manage { + --_lumo-button-color: var(--lumo-tint); + --_lumo-button-background-color: var(--lumo-contrast-20pct); + } + + .team-settings { + --_lumo-button-primary-color: var(--lumo-primary-color); + --_lumo-button-primary-background-color: var(--lumo-tint); + } + } +} diff --git a/packages/cxl-ui/scss/cxl-dashboard-team-stats.scss b/packages/cxl-ui/scss/cxl-dashboard-team-stats.scss new file mode 100644 index 000000000..6bbe866d5 --- /dev/null +++ b/packages/cxl-ui/scss/cxl-dashboard-team-stats.scss @@ -0,0 +1,84 @@ +@use "~@conversionxl/cxl-lumo-styles/scss/mq"; +@use "./mixins"; + +:host { + display: flex; + justify-content: center; + width: 100%; + + .container { + position: relative; + box-sizing: border-box; + display: flex; + flex-direction: column; + gap: calc(2 * var(--lumo-space-m)); + width: 100%; + max-width: var(--cxl-content-max-width-wide); + padding: var(--lumo-space-m); + background-color: var(--cxl-color-light-gray); + + @include mixins.pesudo-element-full-width(var(--cxl-color-light-gray)); + + @media #{mq.$small} { + padding-bottom: calc(var(--lumo-space-xl) + var(--lumo-space-l)); + } + + @media #{mq.$large} { + padding: var(--lumo-space-xl) 0; + } + + header { + display: flex; + justify-content: space-between; + + .title { + margin-block: 0; + font-weight: 900; + } + + .actions { + display: none; + } + + .team-settings { + --_lumo-button-primary-color: var(--lumo-primary-color); + --_lumo-button-primary-background-color: var(--lumo-tint); + } + + @media #{mq.$small} { + .actions { + display: flex; + } + } + } + + .content { + display: flex; + flex-direction: column-reverse; + justify-content: space-between; + gap: var(--lumo-space-l); + + .progress { + min-width: 280px; + } + + .progress-subtitle { + margin-block: 0; + } + + ::slotted(.stats) { + padding: 0; + grid-template-columns: repeat(2, 1fr); + gap: calc(2 * var(--lumo-space-l)); + } + + @media #{mq.$small} { + flex-direction: row; + + ::slotted(.stats) { + grid-template-columns: repeat(3, 1fr); + } + } + } + } +} diff --git a/packages/cxl-ui/scss/cxl-section.scss b/packages/cxl-ui/scss/cxl-section.scss index f055254ab..ddad0ae23 100644 --- a/packages/cxl-ui/scss/cxl-section.scss +++ b/packages/cxl-ui/scss/cxl-section.scss @@ -1,9 +1,16 @@ @use "~@conversionxl/cxl-lumo-styles/scss/mixins"; +@use "~@conversionxl/cxl-lumo-styles/scss/mq"; :host { display: block; } +:host([theme~="cxl-section-dashboard-team-content"]) { + @media #{mq.$large} { + --cxl-wrap-padding: 0; + } +} + /** * Avoid margin collapse with background. */ diff --git a/packages/cxl-ui/scss/cxl-stats.scss b/packages/cxl-ui/scss/cxl-stats.scss index 3c3f10106..7624244ab 100644 --- a/packages/cxl-ui/scss/cxl-stats.scss +++ b/packages/cxl-ui/scss/cxl-stats.scss @@ -4,23 +4,19 @@ display: grid; grid-template-columns: repeat(2, 1fr); gap: var(--lumo-space-m); - padding: var(--lumo-space-xl) var(--lumo-space-m) var(--lumo-space-m); - - @media #{mq.$small} { - padding: var(--lumo-space-xl) var(--lumo-space-l) var(--lumo-space-l); - } @media #{mq.$medium} { - grid-template-columns: repeat(4, 1fr); + display: flex; } } :host([theme~="cxl-stats-dashboard-header"]) { - margin-top: var(--lumo-space-m); padding: 0; + margin-top: var(--lumo-space-m); border: 0; @media #{mq.$medium} { + padding-top: var(--lumo-space-s); margin-top: 0; gap: var(--lumo-space-l); } diff --git a/packages/cxl-ui/scss/global/cxl-dashboard-team-header.scss b/packages/cxl-ui/scss/global/cxl-dashboard-team-header.scss new file mode 100644 index 000000000..a98f3744a --- /dev/null +++ b/packages/cxl-ui/scss/global/cxl-dashboard-team-header.scss @@ -0,0 +1,17 @@ +/* stylelint-disable selector-max-type */ +cxl-dashboard-team-header { + form { + label { + display: none; + } + + select { + height: unset; + font-size: var(--lumo-font-size-xxl); + font-weight: 900; + color: var(--lumo-tint); + background-color: var(--lumo-contrast-20pct); + border-radius: var(--lumo-border-radius-m); + } + } +} diff --git a/packages/cxl-ui/src/components/cxl-dashboard-team-header.js b/packages/cxl-ui/src/components/cxl-dashboard-team-header.js new file mode 100644 index 000000000..5b22cb696 --- /dev/null +++ b/packages/cxl-ui/src/components/cxl-dashboard-team-header.js @@ -0,0 +1,61 @@ +/* eslint-disable import/no-extraneous-dependencies */ +import { LitElement, html } from 'lit'; +import { customElement, property } from 'lit/decorators.js'; +import { registerGlobalStyles } from '@conversionxl/cxl-lumo-styles/src/utils'; +import '@conversionxl/cxl-lumo-styles'; +import cxlDashboardTeamHeaderStyles from '../styles/cxl-dashboard-team-header-css.js'; +import cxlDashboardTeamHeaderGlobalStyles from '../styles/global/cxl-dashboard-team-header-css.js'; +import '@vaadin/icon'; +import '@vaadin/button'; + +@customElement('cxl-dashboard-team-header') +export class CxlDashboardTeamHeaderElement extends LitElement { + static get styles() { + return [cxlDashboardTeamHeaderStyles]; + } + + @property({ type: String, attribute: 'team-name' }) teamName = ''; + + @property({ type: String, attribute: 'invite-url' }) inviteURL = ''; + + @property({ type: String, attribute: 'settings-url' }) settingsURL = ''; + + @property({ type: Boolean }) multiple = false; + + render() { + return html` +
+
+
+ ${this.multiple ? 'Choose team' : 'My team'} + ${this.multiple + ? html`` + : html`

${this.teamName}

`} +
+
+
+ + + Invite & manage team + + + + + + Team settings + + +
+
+ `; + } + + firstUpdated(_changedProperties) { + super.firstUpdated(_changedProperties); + + // Global styles. + registerGlobalStyles(cxlDashboardTeamHeaderGlobalStyles, { + moduleId: 'cxl-dashboard-team-header-global', + }); + } +} diff --git a/packages/cxl-ui/src/components/cxl-dashboard-team-stats.js b/packages/cxl-ui/src/components/cxl-dashboard-team-stats.js new file mode 100644 index 000000000..416d6cba5 --- /dev/null +++ b/packages/cxl-ui/src/components/cxl-dashboard-team-stats.js @@ -0,0 +1,46 @@ +/* eslint-disable import/no-extraneous-dependencies */ +import { LitElement, html } from 'lit'; +import { customElement, property } from 'lit/decorators.js'; +import '@vaadin/progress-bar'; +import '@vaadin/button'; +import '@conversionxl/cxl-lumo-styles'; +import CXLDashboardTeamStatsStyles from '../styles/cxl-dashboard-team-stats-css.js'; + +@customElement('cxl-dashboard-team-stats') +export class CxlDashboardTeamStatsElement extends LitElement { + static get styles() { + return [CXLDashboardTeamStatsStyles]; + } + + @property({ type: Number }) progress = 0; + + @property({ type: String, attribute: 'manage-roadmaps-url' }) manageRoadmapsURL = ''; + + render() { + return html` +
+
+

Team progress & stats

+ +
+
+
+ Team roadmap progress + +

${(100 * this.progress).toFixed(0)}% complete

+
+
+ +
+
+
+ `; + } +} diff --git a/packages/cxl-ui/src/index-core.js b/packages/cxl-ui/src/index-core.js index 93bf7ecbd..c62791bde 100644 --- a/packages/cxl-ui/src/index-core.js +++ b/packages/cxl-ui/src/index-core.js @@ -24,6 +24,8 @@ export { CXLCheckoutDetailsElement } from './components/cxl-checkout-details.js' export { CXLDashboardHeaderElement } from './components/cxl-dashboard-header.js'; export { CxlDashboardSectionElement } from './components/cxl-dashboard-section.js'; export { CXLDashboardNotificationElement } from './components/cxl-dashboard-notification.js'; +export { CxlDashboardTeamHeaderElement } from './components/cxl-dashboard-team-header.js'; +export { CxlDashboardTeamStatsElement } from './components/cxl-dashboard-team-stats.js'; export { CXLFeaturedCourseCardElement } from './components/cxl-featured-course-card.js'; export { CXLLightCardElement } from './components/cxl-light-card.js'; export { CXLMarketingNavElement } from './components/cxl-marketing-nav.js'; diff --git a/packages/storybook/cxl-ui/cxl-team-dashboard/cxl-dashboard-team-header.stories.js b/packages/storybook/cxl-ui/cxl-team-dashboard/cxl-dashboard-team-header.stories.js new file mode 100644 index 000000000..8cfc43b52 --- /dev/null +++ b/packages/storybook/cxl-ui/cxl-team-dashboard/cxl-dashboard-team-header.stories.js @@ -0,0 +1,17 @@ +import { TeamDashboardHeaderTemplate, ArgTypes } from './header-template'; + +export default { + title: 'CXL UI/cxl-team-dashboard', +}; + +export const CXLDashboardTeamHeader = TeamDashboardHeaderTemplate.bind({}); + +CXLDashboardTeamHeader.argTypes = { + ...ArgTypes, +}; + +CXLDashboardTeamHeader.args = { + teamName: 'NOPE Creative', + teamId: 6351659, + multiple: false, +}; diff --git a/packages/storybook/cxl-ui/cxl-team-dashboard/cxl-dashboard-team-stats.stories.js b/packages/storybook/cxl-ui/cxl-team-dashboard/cxl-dashboard-team-stats.stories.js new file mode 100644 index 000000000..b3107d0fe --- /dev/null +++ b/packages/storybook/cxl-ui/cxl-team-dashboard/cxl-dashboard-team-stats.stories.js @@ -0,0 +1,15 @@ +import { TeamDashboardStatsTemplate, ArgTypes } from './stats-template'; + +export default { + title: 'CXL UI/cxl-team-dashboard', +}; + +export const CXLDashboardTeamStats = TeamDashboardStatsTemplate.bind({}); + +CXLDashboardTeamStats.argTypes = { + ...ArgTypes, +}; + +CXLDashboardTeamStats.args = { + progress: 0.65, +}; diff --git a/packages/storybook/cxl-ui/cxl-team-dashboard/cxl-team-dashboard.stories.js b/packages/storybook/cxl-ui/cxl-team-dashboard/cxl-team-dashboard.stories.js new file mode 100644 index 000000000..80fe0699a --- /dev/null +++ b/packages/storybook/cxl-ui/cxl-team-dashboard/cxl-team-dashboard.stories.js @@ -0,0 +1,30 @@ +import { html } from 'lit'; +import { CXLDashboardTeamHeader } from './cxl-dashboard-team-header.stories'; +import { CXLMarketingNav } from '../cxl-marketing-nav.stories'; +import { CXLFooterNav } from '../footer-nav.stories'; +import { CXLDashboardTeamStats } from './cxl-dashboard-team-stats.stories'; + +export default { + title: 'CXL UI/cxl-team-dashboard', +}; + +export const CXLDashboard = (args) => html` + + ${CXLMarketingNav()} +
+ ${CXLDashboardTeamHeader({ + teamName: args.header_name, + teamId: args.header_team_id, + multiple: args.header_multiple, + })} + ${CXLDashboardTeamStats({ progress: 0.65 })} +
+ ${CXLFooterNav()} +
+`; + +CXLDashboard.args = { + header_name: 'NOPE Creative', + header_team_id: 6351659, + header_multiple: false, +}; diff --git a/packages/storybook/cxl-ui/cxl-team-dashboard/header-template.js b/packages/storybook/cxl-ui/cxl-team-dashboard/header-template.js new file mode 100644 index 000000000..09fc8ca06 --- /dev/null +++ b/packages/storybook/cxl-ui/cxl-team-dashboard/header-template.js @@ -0,0 +1,69 @@ +import { html } from 'lit'; +import '@conversionxl/cxl-lumo-styles'; +import '@conversionxl/cxl-ui/src/components/cxl-dashboard-team-header.js'; +import '@conversionxl/cxl-ui/src/components/cxl-stats'; + +export const TeamDashboardHeaderTemplate = (header) => html` + + + + +
+ +
+ +
+
+
+`; + +export const ArgTypes = { + teamId: { control: 'number' }, + teamName: { control: 'text' }, + multiple: { control: 'boolean' }, +}; diff --git a/packages/storybook/cxl-ui/cxl-team-dashboard/stats-template.js b/packages/storybook/cxl-ui/cxl-team-dashboard/stats-template.js new file mode 100644 index 000000000..568e1e177 --- /dev/null +++ b/packages/storybook/cxl-ui/cxl-team-dashboard/stats-template.js @@ -0,0 +1,13 @@ +import { html } from 'lit'; +import '@conversionxl/cxl-ui/src/components/cxl-dashboard-team-stats.js'; +import { CXLStats } from '../cxl-stats/default.stories'; + +export const TeamDashboardStatsTemplate = (args) => html` + +
${CXLStats({ statsCount: 3 })}
+
+`; + +export const ArgTypes = { + progress: { type: Number }, +};