Skip to content

Commit d61330e

Browse files
Merge pull request #1591 from CleverCloud/cc-range-selector/init
2 parents a793be7 + 0dd4450 commit d61330e

13 files changed

+2442
-1
lines changed

sandbox/forms/form-demo-with-cc-components.js

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import '../../src/components/cc-input-date/cc-input-date.js';
44
import '../../src/components/cc-input-number/cc-input-number.js';
55
import '../../src/components/cc-input-text/cc-input-text.js';
66
import '../../src/components/cc-picker/cc-picker.js';
7+
import '../../src/components/cc-range-selector/cc-range-selector.js';
78
import '../../src/components/cc-select/cc-select.js';
89
import '../../src/components/cc-toggle/cc-toggle.js';
910
import { formSubmit } from '../../src/lib/form/form-submit-directive.js';
@@ -30,6 +31,16 @@ const PICKER_OPTIONS = [
3031
{ body: 'George Harrison', value: 'HARRISON' },
3132
];
3233

34+
const RANGE_SELECTOR_OPTIONS = [
35+
{ body: `L`, value: 'lun' },
36+
{ body: `M`, value: 'mar' },
37+
{ body: `M`, value: 'mer' },
38+
{ body: `J`, value: 'jeu' },
39+
{ body: `V`, value: 'ven' },
40+
{ body: `S`, value: 'sam' },
41+
{ body: `D`, value: 'dim' },
42+
];
43+
3344
export class FormDemoWithCcComponents extends LitElement {
3445
render() {
3546
return html`
@@ -43,6 +54,12 @@ export class FormDemoWithCcComponents extends LitElement {
4354
<cc-input-text label="Email address" name="email" type="email" required></cc-input-text>
4455
<cc-select label="Favorite color" name="color" .options=${COLOR_OPTIONS} required></cc-select>
4556
<cc-picker label="Find the intruder" name="star" .options=${PICKER_OPTIONS} required></cc-picker>
57+
<cc-range-selector
58+
label="Select a range"
59+
name="planning"
60+
.options=${RANGE_SELECTOR_OPTIONS}
61+
required
62+
></cc-range-selector>
4663
4764
<cc-button primary type="submit">Submit</cc-button>
4865
</form>
Lines changed: 192 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,192 @@
1+
import { LitElement, css, html } from 'lit';
2+
import { classMap } from 'lit/directives/class-map.js';
3+
4+
/**
5+
* A tile component that can be used to display a range selection state.
6+
*
7+
* This component is specifically designed for cc-range-selector and is not meant to be used standalone.
8+
* It is a presentational component that displays visual states based on properties set by its parent.
9+
* All interactive behaviors (click handling, keyboard navigation, etc.) are managed by the parent cc-range-selector component.
10+
*
11+
* @cssdisplay inline-flex
12+
*
13+
* @slot - Content displayed as the main part of the tile. This slot should contain the option's label or visual content and should not be empty.
14+
*/
15+
export class CcRangeSelectorOption extends LitElement {
16+
static get properties() {
17+
return {
18+
disabled: { type: Boolean, reflect: true },
19+
dragging: { type: Boolean, reflect: true },
20+
error: { type: Boolean, reflect: true },
21+
pointer: { type: Boolean, reflect: true },
22+
readonly: { type: Boolean, reflect: true },
23+
selected: { type: Boolean, reflect: true },
24+
};
25+
}
26+
27+
constructor() {
28+
super();
29+
30+
/** @type {boolean} Whether the component should be disabled (default: 'false') */
31+
this.disabled = false;
32+
33+
/** @type {boolean} Whether the option is within a drag selection range (default: 'false') */
34+
this.dragging = false;
35+
36+
/** @type {boolean} Whether the component should be in error mode when not disabled nor readonly (default: 'false') */
37+
this.error = false;
38+
39+
/** @type {boolean} Whether to show a pointer cursor for interactive states (default: 'false') */
40+
this.pointer = false;
41+
42+
/** @type {boolean} Whether the component should be readonly when not disabled (default: 'false') */
43+
this.readonly = false;
44+
45+
/** @type {boolean} Whether the option is currently selected when not dragging (default: 'false') */
46+
this.selected = false;
47+
}
48+
49+
/**
50+
* Renders the option with appropriate visual states based on its properties.
51+
* Calculates CSS classes for disabled, readonly, error, dragging, selected, and pointer states.
52+
* @return {import('lit').TemplateResult}
53+
*/
54+
render() {
55+
// Calculate CSS classes based on component state
56+
// State priority: disabled > readonly > error > dragging > selected
57+
// Note: dragging state takes visual priority over selected state during drag operations
58+
const classes = {
59+
disabled: this.disabled,
60+
readonly: !this.disabled && this.readonly,
61+
error: !this.disabled && !this.readonly && this.error,
62+
dragging: this.dragging,
63+
selected: !this.dragging && this.selected, // Selected styling is hidden during dragging
64+
};
65+
66+
return html`
67+
<div class="wrapper ${classMap(classes)}">
68+
<slot></slot>
69+
</div>
70+
`;
71+
}
72+
73+
static get styles() {
74+
return [
75+
// language=CSS
76+
css`
77+
/* region global */
78+
:host {
79+
--cc-icon-size: 1.25em;
80+
81+
border-radius: var(--cc-border-radius-default, 0.25em);
82+
display: inline-flex;
83+
overflow: hidden;
84+
width: fit-content;
85+
}
86+
87+
.wrapper {
88+
align-items: stretch;
89+
display: flex;
90+
flex: 1 1 auto;
91+
line-height: 1.5;
92+
}
93+
/* endregion */
94+
95+
/* region body section */
96+
::slotted(*) {
97+
background-color: var(--cc-color-bg-default, #fff);
98+
border: 0.125em solid var(--cc-color-border-neutral, #bfbfbf);
99+
color: var(--cc-color-text-default, #262626);
100+
display: inline-block;
101+
flex: 1 1 auto;
102+
padding: 0.25em 0.5em;
103+
}
104+
/* endregion */
105+
106+
/* region common states */
107+
.disabled ::slotted(*) {
108+
background-color: var(--cc-color-bg-neutral, #f5f5f5);
109+
border-color: var(--cc-color-bg-neutral, #f5f5f5);
110+
color: var(--cc-color-text-disabled, #595959);
111+
}
112+
113+
.readonly ::slotted(*) {
114+
background-color: var(--cc-color-bg-neutral-active, #d9d9d9);
115+
border-color: var(--cc-color-bg-neutral-active, #d9d9d9);
116+
}
117+
118+
.selected ::slotted(*) {
119+
background-color: var(--cc-color-bg-primary, #3569aa);
120+
border-color: var(--cc-color-bg-primary, #3569aa);
121+
color: var(--cc-color-text-inverted, #fff);
122+
}
123+
124+
.dragging ::slotted(*) {
125+
background-color: var(--cc-color-bg-primary-weaker, #e6eff8);
126+
border-color: var(--cc-color-bg-primary, #3569aa);
127+
border-style: dotted;
128+
color: var(--cc-color-text-primary-strong, #002c9d);
129+
user-select: none;
130+
}
131+
132+
.error ::slotted(*) {
133+
background-color: var(--cc-color-bg-danger-weaker, #ffe4e1);
134+
border-color: var(--cc-color-bg-danger-weaker, #ffe4e1);
135+
color: var(--cc-color-text-danger, #be242d);
136+
}
137+
/* endregion */
138+
139+
/* region selected & disabled */
140+
.selected.disabled ::slotted(*) {
141+
background-color: var(--color-grey-60, #737373);
142+
border-color: var(--color-grey-60, #737373);
143+
color: var(--cc-color-text-inverted, #fff);
144+
}
145+
/* endregion */
146+
147+
/* region selected & readonly */
148+
.selected.readonly ::slotted(*) {
149+
background-color: var(--cc-color-bg-primary-weak, #cedcff);
150+
border-color: var(--cc-color-bg-primary-weak, #cedcff);
151+
color: var(--cc-color-text-primary-strong, #002c9d);
152+
}
153+
/* endregion */
154+
155+
/* region selected & error */
156+
.selected.error ::slotted(*) {
157+
background-color: var(--cc-color-bg-danger, #be242d);
158+
border-color: var(--cc-color-bg-danger, #be242d);
159+
color: var(--cc-color-text-inverted, #fff);
160+
}
161+
/* endregion */
162+
163+
/* region dragging & error */
164+
.dragging.error ::slotted(*) {
165+
border-color: var(--cc-color-bg-danger, #be242d);
166+
}
167+
/* endregion */
168+
169+
/* region hover */
170+
/* Hover state only applies when option is in its default interactive state
171+
(not disabled, readonly, error, selected, or dragging) */
172+
.wrapper:not(.selected, .dragging, .disabled, .readonly, .error) :hover::slotted(*) {
173+
border-color: var(--cc-color-border-neutral-hovered, #595959);
174+
}
175+
176+
.wrapper.error:not(.selected, .dragging) :hover::slotted(*) {
177+
background-color: var(--cc-color-bg-danger-weak, #fbc8c2);
178+
border-color: var(--cc-color-bg-danger-weak, #fbc8c2);
179+
}
180+
/* endregion */
181+
182+
/* region pointer */
183+
:host([pointer]) {
184+
cursor: pointer;
185+
}
186+
/* endregion */
187+
`,
188+
];
189+
}
190+
}
191+
192+
window.customElements.define('cc-range-selector-option', CcRangeSelectorOption);
Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
import { makeStory } from '../../stories/lib/make-story.js';
2+
import './cc-range-selector-option.js';
3+
4+
export default {
5+
tags: ['autodocs'],
6+
title: '🧬 Atoms/<cc-range-selector-option>',
7+
component: 'cc-range-selector-option',
8+
};
9+
10+
const conf = {
11+
component: 'cc-range-selector-option',
12+
};
13+
14+
export const defaultStates = makeStory(conf, {
15+
displayMode: 'flex-wrap',
16+
items: [
17+
{
18+
pointer: true,
19+
innerHTML: `
20+
<span>Default</span>
21+
`,
22+
},
23+
{
24+
disabled: true,
25+
innerHTML: `
26+
<span>Disabled</span>
27+
`,
28+
},
29+
{
30+
readonly: true,
31+
innerHTML: `
32+
<span>Readonly</span>
33+
`,
34+
},
35+
{
36+
error: true,
37+
pointer: true,
38+
innerHTML: `
39+
<span>Error</span>
40+
`,
41+
},
42+
],
43+
});
44+
45+
export const selectedStates = makeStory(conf, {
46+
displayMode: 'flex-wrap',
47+
items: [
48+
{
49+
selected: true,
50+
pointer: true,
51+
innerHTML: `
52+
<span>Default</span>
53+
`,
54+
},
55+
{
56+
disabled: true,
57+
selected: true,
58+
innerHTML: `
59+
<span>Disabled</span>
60+
`,
61+
},
62+
{
63+
readonly: true,
64+
selected: true,
65+
innerHTML: `
66+
<span>Readonly</span>
67+
`,
68+
},
69+
{
70+
error: true,
71+
selected: true,
72+
innerHTML: `
73+
<span>Error</span>
74+
`,
75+
},
76+
],
77+
});
78+
79+
export const draggingStates = makeStory(conf, {
80+
displayMode: 'flex-wrap',
81+
items: [
82+
{
83+
dragging: true,
84+
innerHTML: `
85+
<span>Default</span>
86+
`,
87+
},
88+
{
89+
dragging: true,
90+
error: true,
91+
innerHTML: `
92+
<span>Error</span>
93+
`,
94+
},
95+
],
96+
});
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
import { CcEvent } from '../../lib/events.js';
2+
3+
/**
4+
* Dispatched when a range selection changes.
5+
* @extends {CcEvent<string[]>}
6+
*/
7+
export class CcRangeSelectEvent extends CcEvent {
8+
static TYPE = 'cc-range-select';
9+
10+
/**
11+
* @param {string[]} detail
12+
*/
13+
constructor(detail) {
14+
super(CcRangeSelectEvent.TYPE, detail);
15+
}
16+
}
17+
18+
/**
19+
* Dispatched when the custom option button is clicked.
20+
* The detail contains the current selection value (string for single mode, string array for range mode).
21+
* @extends {CcEvent<string|string[]>}
22+
*/
23+
export class CcRangeSelectorSelectCustom extends CcEvent {
24+
static TYPE = 'cc-range-selector-select-custom';
25+
26+
/**
27+
* @param {string|string[]} detail
28+
*/
29+
constructor(detail) {
30+
super(CcRangeSelectorSelectCustom.TYPE, detail);
31+
}
32+
}

0 commit comments

Comments
 (0)