import { EventEmitter } from '../event'; import { IPrefersColorSchemeStatus } from './types'; /** * @typedef {String} PREFERS_COLOR_SCHEME * @alias PREFERS_COLOR_SCHEME * @memberof PrefersColorSchemeObserver * @property {String} LIGHT * @property {String} DARK */ // for jsdoc /** * @export * @readonly * @enum {PREFERS_COLOR_SCHEME} */ export enum PREFERS_COLOR_SCHEME { LIGHT = 'light', DARK = 'dark' } /** * PrefersColorSchemeObserverEvent Types * @event PrefersColorSchemeObserver#PREFERS_COLOR_SCHEME_EVENTS * @memberof PrefersColorSchemeObserver * @property {String} CHANGE - 변경 시점 * @property {String} GET_STATUS - 현재 상태값 체크 */ // for jsdoc /** * @export * @readonly * @enum {PREFERS_COLOR_SCHEME_EVENTS} */ export enum PREFERS_COLOR_SCHEME_EVENTS { CHANGE = 'PREFERS_COLOR_SCHEME_OBSERVER-EVENTS-CHANGE', GET_STATUS = 'PREFERS_COLOR_SCHEME_OBSERVER-EVENTS-GET_COLOR_SCHEME' } /** * prefers-color-scheme 상태 변화를 감지 <iframe src="https://codesandbox.io/embed/nonollcode-snippet-9gko8?autoresize=1&expanddevtools=1&fontsize=14&hidenavigation=1&initialpath=%2Fobsever-PrefersColorScheme.html&module=%2Fobsever-PrefersColorScheme.html&theme=dark" style="width:100%; height:500px; border:1px solid black; border-radius: 4px; overflow:hidden;" title="@nonoll/code-snippet" allow="geolocation; microphone; camera; midi; vr; accelerometer; gyroscope; payment; ambient-light-sensor; encrypted-media; usb" sandbox="allow-modals allow-forms allow-popups allow-scripts allow-same-origin" ></iframe> * @export * @class PrefersColorSchemeObserver * @alias observer/PrefersColorSchemeObserver * @extends EventEmitter * @throws {Error} browser 에서 지원하지 못하는 경우 - throw new Error('PrefersColorScheme 지원되지 않는 브라우저입니다.'); * @example import { PrefersColorSchemeObserver, PREFERS_COLOR_SCHEME_EVENTS } from "@nonoll/code-snippet/observer"; const createElement = ({ tag = 'div', id = '', style = '', value = '', text = '' }) => { const doc = window.document; const target = doc.createElement(tag); target.setAttribute('id', id); target.setAttribute('style', style); target.setAttribute('value', value); if (text) { target.textContent = text; } return target; } let observer; export const forExample = () => { if (observer) { console.log("already example"); return; } const doc = window.document; observer = new PrefersColorSchemeObserver(); observer.on(PREFERS_COLOR_SCHEME_EVENTS.CHANGE, ({ isDark, isLight }) => { console.log("change", isDark, isLight); }); observer.attach(); observer.emit(PREFERS_COLOR_SCHEME_EVENTS.GET_STATUS); const statusButton = createElement({ tag: "button", text: "status" }); doc.body.appendChild(statusButton); statusButton.addEventListener("click", e => { e.preventDefault(); console.log("statusButton clicked"); if (!observer) { return; } observer.emit(PREFERS_COLOR_SCHEME_EVENTS.GET_STATUS); }); }; forExample(); */ export class PrefersColorSchemeObserver extends EventEmitter { private checkColorScheme = `(prefers-color-scheme: ${PREFERS_COLOR_SCHEME.DARK})`; constructor() { super(); /* istanbul ignore next: for not support error */ if (!PrefersColorSchemeObserver.isSupport()) { throw new Error('PrefersColorScheme 지원되지 않는 브라우저입니다.'); } this.onPrefersColorSchemeChangeListener = this.onPrefersColorSchemeChangeListener.bind(this); } /** * prefers-color-scheme 지원 여부 반환 * @static * @returns {boolean} * @memberof PrefersColorSchemeObserver */ static isSupport(): boolean { return !!window.matchMedia; } /** * 이벤트 감지 설정 * @returns {PrefersColorSchemeObserver} * @memberof PrefersColorSchemeObserver */ public attach(): PrefersColorSchemeObserver { window.matchMedia(this.checkColorScheme).addEventListener('change', this.onPrefersColorSchemeChangeListener); this.on(PREFERS_COLOR_SCHEME_EVENTS.GET_STATUS, this.onPrefersColorSchemeChangeListener); return this; } /** * 이벤트 감지 해제 * @returns {PrefersColorSchemeObserver} * @memberof PrefersColorSchemeObserver */ public detach(): PrefersColorSchemeObserver { window.matchMedia(this.checkColorScheme).removeEventListener('change', this.onPrefersColorSchemeChangeListener); this.off(PREFERS_COLOR_SCHEME_EVENTS.GET_STATUS, this.onPrefersColorSchemeChangeListener); return this; } /** * 현재 상태를 반환 * @returns {IPrefersColorSchemeStatus} * @memberof PrefersColorSchemeObserver */ public getStatus(): IPrefersColorSchemeStatus { const { matches } = window.matchMedia(this.checkColorScheme); const status: IPrefersColorSchemeStatus = { isDark: matches, isLight: !matches }; return status; } /** * 변화 감지 이벤트 리스너 * @private * @memberof PrefersColorSchemeObserver * @fires PrefersColorSchemeObserver#PREFERS_COLOR_SCHEME_EVENTS */ private onPrefersColorSchemeChangeListener(): void { const status = this.getStatus(); this.emit(PREFERS_COLOR_SCHEME_EVENTS.CHANGE, status); } /** * destroy * @returns {PrefersColorSchemeObserver} * @memberof PrefersColorSchemeObserver */ public destroy(): PrefersColorSchemeObserver { this.detach(); return this; } }