diff --git a/packages/elemental-theme/src/custom-elements/ef-clock.less b/packages/elemental-theme/src/custom-elements/ef-clock.less index 13bd9c2350..c5ef53b67a 100644 --- a/packages/elemental-theme/src/custom-elements/ef-clock.less +++ b/packages/elemental-theme/src/custom-elements/ef-clock.less @@ -1,3 +1,13 @@ +// mixin to allow inherited themes able to customize color of svg +.default-analog-svg(@color) { + background: inline-svg('../resources/images/clock/default.svg', 'svg', 'color: @{color}') no-repeat center center; +} + +.small-analog-svg(@color) { + background: inline-svg('../resources/images/clock/small.svg', 'svg', 'color: @{color}') no-repeat center center / cover; +} +// + :host { font-size: 2.5em; user-select: none; @@ -18,8 +28,9 @@ } [part~='am-pm'] { - font-size: 35%; - line-height: 2em; + font-size: 60%; + line-height: 1.5em; + padding-left: 3px; } [part='increment-button'], [part='decrement-button'] { @@ -73,64 +84,108 @@ // Analogue clock &[analogue] { - min-width: 160px; + border-radius: 100%; + width: 160px; max-width: 200px; - background: inline-svg('../resources/images/clock/default.svg', 'svg', 'color: @{global-text-color}') no-repeat center center; - } + .default-analog-svg(@global-text-color); - [part~="hand"]::after { - content: ''; - position: absolute; - } + [part=digital] { + display: flex; + position: absolute; + top: 20%; + left: 0; + right: 0; + justify-content: center; + font-size: 0.3em; + [part~='am-pm'] { + font-size: 100%; + line-height: 1; + } + } - [part~="second"] { - &::after { - width: 1%; + [part=hands]::after { + content: ''; + position: absolute; + border-radius: 50%; + width: 9%; + height: 9%; + top: 45.5%; + left: 45.5%; + border: 2px solid @scheme-color-complementary; + box-sizing: border-box; + z-index: 1; + } + + [part~="hand"]::after { + content: ''; + position: absolute; + } + + [part~="second"] { + &::after { + width: 1%; + height: 43%; + margin-top: 3%; + margin-left: 49.5%; + background-color: @scheme-color-primary; + } + } + + [part~="minute"]::after { + width: 3%; height: 43%; margin-top: 3%; - margin-left: 49.5%; - background-color: @scheme-color-primary; + margin-left: 48.5%; + background-color: @scheme-color-complementary; } - } - [part~="minute"]::after { - width: 3%; - height: 43%; - margin-top: 3%; - margin-left: 48.5%; - background-color: @scheme-color-complementary; - } - - [part~="hour"] { - &::after { + [part~="hour"]::after { width: 3%; height: 28%; margin-top: 18%; margin-left: 48.5%; background-color: @scheme-color-complementary; } - } - [part=digital] { - display: flex; - position: absolute; - top: 20%; - left: 0; - right: 0; - justify-content: center; - font-size: 0.4em; - } + &[size="small"] { + font-size: 10px; + min-width: 50px; + .small-analog-svg(@global-text-color); + [part=hands]::after { + background-color: @scheme-color-complementary; + } - [part=hands]::after { - content: ''; - position: absolute; - border-radius: 50%; - width: 9%; - height: 9%; - top: 45.5%; - left: 45.5%; - border: 2px solid @scheme-color-complementary; - box-sizing: border-box; - z-index: 1; + [part~="second"] { + &::after { + width: 1.5%; + height: 32%; + margin-top: 16%; + } + } + + [part~="minute"]::after { + height: 37%; + margin-top: 12%; + } + + [part~="hour"] { + &::after { + height: 26%; + margin-top: 23%; + } + } + + [part~='am-pm'] { + padding-left: 0; + display: flex; + position: absolute; + left: 0; + right: 0; + bottom: 15%; + font-size: 100%; + justify-content: center; + } + } } + } diff --git a/packages/elemental-theme/src/resources/images/clock/small.svg b/packages/elemental-theme/src/resources/images/clock/small.svg new file mode 100644 index 0000000000..b2f9a1327c --- /dev/null +++ b/packages/elemental-theme/src/resources/images/clock/small.svg @@ -0,0 +1,4 @@ + + + + diff --git a/packages/elements/src/clock/__demo__/index.html b/packages/elements/src/clock/__demo__/index.html index 718d073ea3..c55d510e3e 100644 --- a/packages/elements/src/clock/__demo__/index.html +++ b/packages/elements/src/clock/__demo__/index.html @@ -62,7 +62,7 @@ - + @@ -77,8 +77,20 @@ - + + + + + + + + + + + + + diff --git a/packages/elements/src/clock/__snapshots__/Clock.md b/packages/elements/src/clock/__snapshots__/Clock.md index c25c6de3b5..e51ff6515d 100644 --- a/packages/elements/src/clock/__snapshots__/Clock.md +++ b/packages/elements/src/clock/__snapshots__/Clock.md @@ -46,3 +46,21 @@ ``` +#### `DOM structure of small size analogue is correct` + +```html +
+
+
+
+
+
+ +``` + diff --git a/packages/elements/src/clock/__test__/clock.analogue.test.js b/packages/elements/src/clock/__test__/clock.analogue.test.js index c44d618f95..ebe11bb671 100644 --- a/packages/elements/src/clock/__test__/clock.analogue.test.js +++ b/packages/elements/src/clock/__test__/clock.analogue.test.js @@ -1,4 +1,4 @@ -import { fixture, expect, elementUpdated } from '@refinitiv-ui/test-helpers'; +import { fixture, expect, elementUpdated, nextFrame } from '@refinitiv-ui/test-helpers'; import '@refinitiv-ui/elements/clock'; import '@refinitiv-ui/elemental-theme/light/ef-clock.js'; @@ -37,6 +37,7 @@ describe('clock/Analogue', () => { expect(getClockHand('second'), 'second hand should appear').to.be.not.null; }); + it('Shows correct second hand angle when time is set to 15:30:45', async () => { el.value = '15:30:45'; el.showSeconds = true; @@ -44,5 +45,59 @@ describe('clock/Analogue', () => { expect(getClockHand('second').style.transform, 'seconds hand should have 270 degrees angle').to.be.equal('rotate(270deg)'); }); + + it('Shows small size clock when width is less than 130px', async () => { + expect(el.shadowRoot.querySelector('[part="digital"]'), 'digital clock should display inside a default analog clock').not.to.be.null; + + // make size smaller than defined break point + el.style.width = '129px'; + await elementUpdated(el); + await nextFrame(); + + expect(el.shadowRoot.querySelector('[part="digital"]'), 'digital clock should not display inside small clock').to.be.null; + expect(el.amPm, 'am-pm should be hidden by default on small clock').to.be.equal(false); + expect(el.shadowRoot.querySelector('[part="segment am-pm"]'), 'AM/PM should not display by default in small clock').to.be.null; + }); + + it('Small size clock show AM/PM if it has attribute "am-pm"', async () => { + el.style.width = '129px'; + await elementUpdated(el); + await nextFrame(); + + // test default behavior + expect(el.hasAttribute('am-pm')).to.be.equal(false); + expect(el.amPm).to.be.equal(false); + expect(el.shadowRoot.querySelector('[part="segment am-pm"]')).to.be.null; + + // test when it has am-pm attribute + el = await fixture(''); + el.style.width = '129px'; + await elementUpdated(el); + await nextFrame(); + + expect(el.amPm, 'amPm property should be true if am-pm attribute is set').to.be.equal(true); + expect(el.shadowRoot.querySelector('[part="segment am-pm"]'), 'AM/PM should display on clock').not.to.be.null; + + // test when am-pm is set programmatically + el.amPm = false; + await elementUpdated(el); + await nextFrame(); + expect(el.shadowRoot.querySelector('[part="segment am-pm"]'), 'AM/PM should be hidden if set amPm to false').to.be.null; + }); + + it('Attribute "size=small" should not present if it is not analogue clock', async () => { + el = await fixture(''); + el.style.width = '150px'; + await elementUpdated(el); + await nextFrame(); + + expect(el.hasAttribute('size'), 'size attribute should not show if not analog').to.be.equal(false); + + el.style.width = '129px'; + await elementUpdated(el); + await nextFrame(); + + expect(el.hasAttribute('size'), 'size attribute should not show if not analog even size is small').to.be.equal(false); + }); }); }); diff --git a/packages/elements/src/clock/__test__/clock.test.js b/packages/elements/src/clock/__test__/clock.test.js index c92140b96b..1f8b6cb5ee 100644 --- a/packages/elements/src/clock/__test__/clock.test.js +++ b/packages/elements/src/clock/__test__/clock.test.js @@ -2,7 +2,8 @@ import { fixture, expect, elementUpdated, - aTimeout + aTimeout, + nextFrame } from '@refinitiv-ui/test-helpers'; import '@refinitiv-ui/elements/clock'; @@ -20,6 +21,14 @@ describe('clock/Clock', () => { const el = await fixture(''); expect(el).shadowDom.to.equalSnapshot(); }); + it('DOM structure of small size analogue is correct', async () => { + el = await fixture(''); + el.style.width = '129px'; + await elementUpdated(el); + await nextFrame(); + + expect(el).shadowDom.to.equalSnapshot(); + }); }); describe('Show seconds', () => { diff --git a/packages/elements/src/clock/index.ts b/packages/elements/src/clock/index.ts index a0364f8f42..fa358ac22e 100644 --- a/packages/elements/src/clock/index.ts +++ b/packages/elements/src/clock/index.ts @@ -4,9 +4,10 @@ import { TemplateResult, CSSResultGroup, PropertyValues, - BasicElement, WarningNotice, - TapEvent + TapEvent, + ResponsiveElement, + ElementSize } from '@refinitiv-ui/core'; import { customElement } from '@refinitiv-ui/core/lib/decorators/custom-element.js'; import { property } from '@refinitiv-ui/core/lib/decorators/property.js'; @@ -40,7 +41,7 @@ import { const UP = 'Up'; const DOWN = 'Down'; - +const SMALL_SIZE = 130; // Break point for small size clock face. type UpOrDown = typeof UP | typeof DOWN; /** @@ -51,7 +52,7 @@ type UpOrDown = typeof UP | typeof DOWN; @customElement('ef-clock', { alias: 'sapphire-clock' }) -export class Clock extends BasicElement { +export class Clock extends ResponsiveElement { /** * Element version number @@ -256,6 +257,12 @@ export class Clock extends BasicElement { @query('[part~=seconds]', true) private secondsPart!: HTMLDivElement; + /** + * Size of the clock. + */ + @property({ type: String, attribute: 'size', reflect: true }) + private size:null | 'small' = null; + /** * Get the display time in seconds. * This value includes any offsets applied. @@ -557,6 +564,16 @@ export class Clock extends BasicElement { return this.generateSegmentTemplate('seconds', this.displaySeconds); } + /** + * Called when the element's dimension have changed + * @param size Element size + * @returns {void} + */ + public resizedCallback (size: ElementSize): void { + // size should be set to small only if it's analog clock and it's smaller than defined break point + this.size = this.analogue && Math.min(size.width, size.height) < SMALL_SIZE ? 'small' : null; + } + /** * Called when the element has been appended to the DOM * @returns {void} @@ -614,7 +631,7 @@ export class Clock extends BasicElement { return html`
-
${this.digitalClockTemplate}
+ ${this.size === 'small' ? html`${this.amPm ? this.amPmTemplate : undefined}` : html`
${this.digitalClockTemplate}
`}
${this.showSeconds ? html`
` : undefined} diff --git a/packages/halo-theme/src/custom-elements/ef-clock.less b/packages/halo-theme/src/custom-elements/ef-clock.less index ae3fd27e41..b7415ff797 100644 --- a/packages/halo-theme/src/custom-elements/ef-clock.less +++ b/packages/halo-theme/src/custom-elements/ef-clock.less @@ -1 +1,12 @@ @import '@refinitiv-ui/elemental-theme/src/custom-elements/ef-clock'; + +:host { + [part~='seconds'] { + color: @color-grey; + } + + &[analogue] { + color: @color-grey; + .default-analog-svg(@color-grey); + } +} diff --git a/packages/solar-theme/src/custom-elements/ef-clock.less b/packages/solar-theme/src/custom-elements/ef-clock.less index ae3fd27e41..caf2e6d576 100644 --- a/packages/solar-theme/src/custom-elements/ef-clock.less +++ b/packages/solar-theme/src/custom-elements/ef-clock.less @@ -1 +1,34 @@ @import '@refinitiv-ui/elemental-theme/src/custom-elements/ef-clock'; + +:host { + // override analog clock color to use white/dark instead of using complementary color + &[analogue] { + [part=hands]::after { + border-color: @color-white; + & when (@variant = pearl) { + border-color: @color-black; + } + } + + [part~="minute"]::after, + [part~="hour"]::after { + background-color: @color-white; + & when (@variant = pearl) { + background-color: @color-black; + } + } + + &[size="small"] { + .small-analog-svg(@color-white); + & when (@variant = pearl) { + .small-analog-svg(@color-black); + } + [part=hands]::after { + background-color: @color-white; + & when (@variant = pearl) { + background-color: @color-black; + } + } + } + } +} \ No newline at end of file