Skip to content

Commit

Permalink
feat: add color input box
Browse files Browse the repository at this point in the history
  • Loading branch information
icelam committed Aug 30, 2020
1 parent c20d541 commit 5f43e68
Show file tree
Hide file tree
Showing 16 changed files with 579 additions and 36 deletions.
3 changes: 2 additions & 1 deletion .eslintrc
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,8 @@
"devDependencies": true,
"optionalDependencies": false,
"peerDependencies": false
}]
}],
"no-unused-expressions": ["off"]
},
"overrides": [
{
Expand Down
13 changes: 11 additions & 2 deletions src/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,12 @@ import {
} from 'electron';
import { debounce, listenToSystemThemeChange, getUserPreferedTheme } from '@utils';
import {
getWindowLocation, saveWindowPosition, getWindowPinStatus, saveWindowPinStatus, saveSelectedColor
getWindowLocation,
saveWindowPosition,
getWindowPinStatus,
saveWindowPinStatus,
saveSelectedColor,
saveColorInputMode
} from '@storage';
import { applicationMenu, settingMenu } from '@menus';
import {
Expand All @@ -16,7 +21,7 @@ import {
MAIN_WINDOW_WIDTH,
MAIN_WINDOW_HEIGHT
} from '@constants';
import { Position, AppThemeOptions } from '@types';
import { Position, AppThemeOptions, ColorInputMode } from '@types';

let mainWindow: BrowserWindow;

Expand Down Expand Up @@ -127,6 +132,10 @@ ipcMain.on('SAVE_SELECTED_COLOR', (_, value: string) => {
saveSelectedColor(value);
});

ipcMain.on('SAVE_COLOR_INPUT_MODE', (_, value: ColorInputMode) => {
saveColorInputMode(value);
});

ipcMain.on('COPY_COLOR_TO_CLIPBOARD', (_, value: string) => {
clipboard.writeText(value);
});
Expand Down
22 changes: 19 additions & 3 deletions src/preload.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import { ipcRenderer } from 'electron';
import { getUserPreferedTheme, getOsTheme } from '@utils/getThemes';
import { getWindowPinStatus, getSelectedColor } from '@storage';
import { getWindowPinStatus, getSelectedColor, getColorInputMode } from '@storage';
import { AppTheme } from '@types';
import { convertColorHexToRgbString } from '@utils/color';

const setInitialAppTheme = async () => {
const userPreference = await getUserPreferedTheme();
Expand All @@ -25,12 +26,27 @@ const restoreWindowPinStatus = async (): Promise<void> => {
window.document.getElementById('app')?.setAttribute('shouldPinFrame', isPinned ? 'true' : 'false');
};

const restoreLastSelectedColor = async (): Promise<void> => {
const restoreSelectedColorAndInputMode = async (): Promise<void> => {
const { inputMode } = await getColorInputMode();
if (inputMode) {
window.document.getElementById('app')?.setAttribute('colorInputMode', inputMode);
}

const { selectedColor } = await getSelectedColor();
if (selectedColor) {
window.document.getElementById('app')?.setAttribute('selectedColor', selectedColor);

// Format input value according to input mode
let inputValue = '';
if (inputMode === 'rgb') {
inputValue = convertColorHexToRgbString(selectedColor);
} else {
inputValue = selectedColor.replace('#', '');
}

inputValue && window.document.getElementById('app')?.setAttribute('colorInputValue', inputValue);
}
};

restoreWindowPinStatus();
restoreLastSelectedColor();
restoreSelectedColorAndInputMode();
122 changes: 120 additions & 2 deletions src/renderer/app.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,15 @@ import {
} from 'lit-element';
import '@components/Header/FrameHeader';
import '@pages/TintsShadesGenerator';
import { randomHexColor } from '@utils/color';
import {
randomHexColor,
isValidHexColor,
isValidRgbColor,
convertColorRgbToHex,
convertColorHexToRgbString
} from '@utils/color';
import debounce from '@utils/debounce';
import { ColorInputMode } from '@types';

/**
* Enrty point of the app
Expand Down Expand Up @@ -36,10 +44,25 @@ class GeneratorApp extends LitElement {
shouldPinFrame = false;

/**
* Current selectedColor from user
* Current color selected by user
*/
@property({ type: String }) selectedColor = '#46beb9';

/**
* Current color value input by user
*/
@property({ type: String }) colorInputValue = '46beb9';

/**
* Current input mode of color input text box
*/
@property({ type: String }) colorInputMode: ColorInputMode = 'hex';

/**
* Indicates if value of color input text box has error
*/
@property({ type: Boolean }) colorInputHasError = false;

render(): TemplateResult {
return html`
<frame-header
Expand All @@ -52,53 +75,148 @@ class GeneratorApp extends LitElement {
</frame-header>
<tints-shades-generator
.selectedColor=${this.selectedColor}
.colorInputValue=${this.colorInputValue}
.onColorPickerChange=${this.onColorPickerChange.bind(this)}
.onRandomizeColor=${this.onRandomizeColor.bind(this)}
.copyColorToClipboard=${this.copyColorToClipboard}
.colorInputMode=${this.colorInputMode}
.onColorInputChange=${debounce(this.onColorInputChange.bind(this), 500)}
.onColorInputModeClick=${this.onColorInputModeClick.bind(this)}
.onColorInputKeypress=${this.onColorInputKeypress.bind(this)}
.colorInputHasError=${this.colorInputHasError}
>
</tints-shades-generator>
`;
}

/**
* Pass signal to main process to close application window
*/
private closeFrame(): void {
window.ipcRenderer.send('QUIT_APP');
}

/**
* Pass signal to main process to open setting menu
*/
private openSettingMenu(event: MouseEvent): void {
window.ipcRenderer.send('OPEN_SETTING_MENU', {
x: event.clientX + 10,
y: event.clientY + 10
});
}

/**
* Pass signal to main process to minimize application window
*/
private minimizeFrame(): void {
window.ipcRenderer.send('MINIMIZE_APP');
}

/**
* Pass signal to main process to set application window on top
*/
private pinFrame(): void {
const newPinState = !this.shouldPinFrame;
this.shouldPinFrame = newPinState;
window.ipcRenderer.send('PIN_APP', newPinState);
}

/**
* Handle change of colour through color picker
*/
private onColorPickerChange(event: Event): void {
const newColor = (event.target as HTMLInputElement).value;
this.selectedColor = newColor;

// Format input value according to input mode
let inputValue = '';
if (this.colorInputMode === 'rgb') {
inputValue = convertColorHexToRgbString(newColor);
} else {
inputValue = newColor.replace('#', '');
}

this.colorInputValue = inputValue;
window.ipcRenderer.send('SAVE_SELECTED_COLOR', newColor);
}

/**
* Generate a random color
*/
private onRandomizeColor(): void {
const newColor = randomHexColor() ?? this.selectedColor;
this.selectedColor = newColor;
window.ipcRenderer.send('SAVE_SELECTED_COLOR', newColor);
}

/**
* Pass signal to main process to copy specified color to clipboard
*/
private copyColorToClipboard(event: MouseEvent): void {
const colorToCopy = (event?.target as HTMLButtonElement)?.value;
if (colorToCopy) {
window.ipcRenderer.send('COPY_COLOR_TO_CLIPBOARD', colorToCopy);
}
}

/**
* Handle change of colour through input box
*/
private onColorInputChange(value: string): void {
const isCurrentlyInHexMode = this.colorInputMode === 'hex';
const isColorValid = isCurrentlyInHexMode
? isValidHexColor
: isValidRgbColor;

const inputHasError = !isColorValid(value);
this.colorInputHasError = inputHasError;
this.colorInputValue = value;

if (!inputHasError) {
this.selectedColor = isCurrentlyInHexMode
? `#${value}`
: convertColorRgbToHex(value) as string;
}
}

/**
* Change input mode from hex to rgb, or rgb to hex and apply formatting to input value
* Input mode will be saved to storage and restore t next app launch
*/
private onColorInputModeClick(): void {
const isCurrentlyInHexMode = this.colorInputMode === 'hex';
const newColorInputMode = isCurrentlyInHexMode ? 'rgb' : 'hex';
this.colorInputMode = newColorInputMode;

// Convert input value to desired format
const isColorValid = isCurrentlyInHexMode
? isValidHexColor
: isValidRgbColor;

if (!isColorValid(this.colorInputValue)) {
this.colorInputValue = '';
} else if (isCurrentlyInHexMode) {
this.colorInputValue = convertColorHexToRgbString(this.colorInputValue);
} else {
const hexValue = convertColorRgbToHex(this.colorInputValue) as string;
this.colorInputValue = hexValue.replace('#', ''); // TODO: make a reusable utils for removing #
}

window.ipcRenderer.send('SAVE_COLOR_INPUT_MODE', newColorInputMode);
}

/**
* Prevent input of unwanted characters to color input field
*/
private onColorInputKeypress(event: KeyboardEvent): void {
const ALLOWED_CHARS = this.colorInputMode === 'hex'
? /[0-9A-Fa-f]+/
: /[0-9, ]+/;
if (!ALLOWED_CHARS.test(event.key)) {
event.preventDefault();
}
}
}

declare global {
Expand Down
11 changes: 11 additions & 0 deletions src/renderer/assets/images/icons/arrow.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
20 changes: 20 additions & 0 deletions src/renderer/assets/scss/_colors.scss
Original file line number Diff line number Diff line change
Expand Up @@ -40,12 +40,22 @@
--color-frame-header-icon-hover: var(--color-grey-40);
--color-input-border: var(--color-grey-91);
--color-body-icon: var(--color-grey-54);
--color-body-icon-hover: var(--color-grey-40);
--color-tooltip-background: var(--color-grey-40);
--color-tooltip-text: var(--color-white);
--color-color-steps-border: var(--color-grey-91);
--color-copy-button-background: var(--color-white);
--color-copy-button-text: var(--color-grey-54);
--color-copy-button-shadow: var(--color-light-shadow);
--color-input-background: var(--color-white);
--color-input-text: var(--color-grey-40);
--color-input-placeholder: var(--color-grey-67);
--color-input-border: var(--color-grey-91);
--color-input-border-error: var(--color-light-red);
--color-input-label-background: var(--color-grey-96);
--color-input-label-text: var(--color-grey-40);
--color-input-mode-icon: var(--color-grey-67);
--color-input-mode-icon-hover: var(--color-grey-54);
}

[data-theme^='dark'] {
Expand All @@ -61,10 +71,20 @@
--color-frame-header-icon-hover: var(--color-grey-60);
--color-input-border: var(--color-grey-28);
--color-body-icon: var(--color-grey-40);
--color-body-icon-hover: var(--color-grey-60);
--color-tooltip-background: var(--color-grey-28);
--color-tooltip-text: var(--color-grey-68);
--color-color-steps-border: var(--color-grey-28);
--color-copy-button-background: var(--color-grey-28);
--color-copy-button-text: var(--color-grey-54);
--color-copy-button-shadow: var(--color-dark-shadow);
--color-input-background: var(--color--grey-18);
--color-input-text: var(--color-grey-68);
--color-input-placeholder: var(--color-grey-28);
--color-input-border: var(--color-grey-28);
--color-input-border-error: var(--color-light-red);
--color-input-label-background: var(--color-grey-23);
--color-input-label-text: var(--color-grey-68);
--color-input-mode-icon: var(--color-grey-40);
--color-input-mode-icon-hover: var(--color-grey-60);
}
2 changes: 1 addition & 1 deletion src/renderer/components/Button/button.styles.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { css } from 'lit-element';

/**
* Shared styles for buttons.
* Shared styles for buttons
*/
const commonButtonStyles = css`
.button {
Expand Down
Loading

0 comments on commit 5f43e68

Please sign in to comment.