Skip to content

Commit

Permalink
- refactor(foundation): refactor theme hierarchy
Browse files Browse the repository at this point in the history
- feat(core): added group channel labels
  • Loading branch information
bang9 committed Mar 18, 2022
1 parent 1320be1 commit 0ff1809
Show file tree
Hide file tree
Showing 17 changed files with 472 additions and 189 deletions.
97 changes: 94 additions & 3 deletions packages/uikit-react-native-core/src/localization/label.type.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
import type { Locale } from 'date-fns';
import type Sendbird from 'sendbird';

import type { PartialDeep } from '@sendbird/uikit-utils';
import type { PartialDeep, SendbirdMessage } from '@sendbird/uikit-utils';
import {
dateSeparator,
getGroupChannelLastMessage,
getGroupChannelPreviewTime,
getGroupChannelTitle,
messageTime,
truncate,
} from '@sendbird/uikit-utils';

export type LabelLocale = 'en';
Expand All @@ -20,10 +22,56 @@ export interface LabelSet {
FRAGMENT: {
/** @domain GroupChannel > Fragment > Header > Title */
HEADER_TITLE: (currentUserId: string, channel: Sendbird.GroupChannel) => string;
/** @domain GroupChannel > Fragment > List > Banner_Frozen */
/** @domain GroupChannel > Fragment > List > Banner frozen */
LIST_BANNER_FROZEN: string;
/** @domain GroupChannel > Fragment > List > Date_Separator */
/** @domain GroupChannel > Fragment > List > Date separator */
LIST_DATE_SEPARATOR: (date: Date, locale?: Locale) => string;
/** @domain GroupChannel > Fragment > List > Tooltip > New messages */
LIST_TOOLTIP_NEW_MSG: (newMessages: SendbirdMessage[]) => string;

/** @domain GroupChannel > Fragment > List > Message > Time */
LIST_MESSAGE_TIME: (message: SendbirdMessage, locale?: Locale) => string;
/** @domain GroupChannel > Fragment > List > Message > File title */
LIST_MESSAGE_FILE_TITLE: (message: Sendbird.FileMessage) => string;
/** @domain GroupChannel > Fragment > List > Message > Edited postfix */
LIST_MESSAGE_EDITED_POSTFIX: string;
/** @domain GroupChannel > Fragment > List > Message > Unknown title */
LIST_MESSAGE_UNKNOWN_TITLE: (message: SendbirdMessage) => string;
/** @domain GroupChannel > Fragment > List > Message > Unknown description */
LIST_MESSAGE_UNKNOWN_DESC: (message: SendbirdMessage) => string;

/** @domain GroupChannel > Fragment > Input > Placeholder active */
INPUT_PLACEHOLDER_ACTIVE: string;
/** @domain GroupChannel > Fragment > Input > Placeholder disabled */
INPUT_PLACEHOLDER_DISABLED: string;
/** @domain GroupChannel > Fragment > Input > Edit ok */
INPUT_EDIT_OK: string;
/** @domain GroupChannel > Fragment > Input > Edit cancel */
INPUT_EDIT_CANCEL: string;

/** @domain GroupChannel > Fragment > Dialog > Message > Copy */
DIALOG_MESSAGE_COPY: string;
/** @domain GroupChannel > Fragment > Dialog > Message > Edit */
DIALOG_MESSAGE_EDIT: string;
/** @domain GroupChannel > Fragment > Dialog > Message > Delete */
DIALOG_MESSAGE_DELETE: string;
/** @domain GroupChannel > Fragment > Dialog > Message > Delete > Confirm title */
DIALOG_MESSAGE_DELETE_CONFIRM_TITLE: string;
/** @domain GroupChannel > Fragment > Dialog > Message > Delete > Confirm ok */
DIALOG_MESSAGE_DELETE_CONFIRM_OK: string;
/** @domain GroupChannel > Fragment > Dialog > Message > Delete > Confirm cancel */
DIALOG_MESSAGE_DELETE_CONFIRM_CANCEL: string;
/** @domain GroupChannel > Fragment > Dialog > Message > Failed > Retry */
DIALOG_MESSAGE_FAILED_RETRY: string;
/** @domain GroupChannel > Fragment > Dialog > Message > Failed > Remove */
DIALOG_MESSAGE_FAILED_REMOVE: string;

/** @domain GroupChannel > Fragment > Dialog > Attachment > Camera */
DIALOG_ATTACHMENT_CAMERA: string;
/** @domain GroupChannel > Fragment > Dialog > Attachment > Photo */
DIALOG_ATTACHMENT_PHOTO_LIBRARY: string;
/** @domain GroupChannel > Fragment > Dialog > Attachment > Files */
DIALOG_ATTACHMENT_FILES: string;
};
};
GROUP_CHANNEL_LIST: {
Expand Down Expand Up @@ -61,7 +109,10 @@ export interface LabelSet {
HEADER_TITLE: string;
/** @domain InviteMembers > Header > Right */
HEADER_RIGHT: <T>(params: { selectedUsers: Array<T> }) => string;
/** @domain InviteMembers > User > No name */
USER_NO_NAME: string;
};
// UI
PLACEHOLDER: {
NO_BANNED_MEMBERS: string;
NO_CHANNELS: string;
Expand All @@ -73,6 +124,12 @@ export interface LabelSet {
RETRY_LABEL: string;
};
};
DIALOG: {
ALERT_DEFAULT_OK: string;
PROMPT_DEFAULT_OK: string;
PROMPT_DEFAULT_CANCEL: string;
PROMPT_DEFAULT_PLACEHOLDER: string;
};
}

type LabelCreateOptions = {
Expand All @@ -93,6 +150,32 @@ export const createBaseLabel = ({ dateLocale, overrides }: LabelCreateOptions):
HEADER_TITLE: (currentUserId, channel) => getGroupChannelTitle(currentUserId, channel),
LIST_BANNER_FROZEN: 'Channel frozen',
LIST_DATE_SEPARATOR: (date, locale) => dateSeparator(date, locale),
LIST_TOOLTIP_NEW_MSG: (newMessages) => `${newMessages.length} new messages`,

LIST_MESSAGE_TIME: (message, locale) => messageTime(new Date(message.createdAt), locale),
LIST_MESSAGE_FILE_TITLE: (message) => truncate(message.name, { mode: 'mid', maxLen: 20 }),
LIST_MESSAGE_EDITED_POSTFIX: '(Edited)',
LIST_MESSAGE_UNKNOWN_TITLE: () => '(Unknown message type)',
LIST_MESSAGE_UNKNOWN_DESC: () => 'Cannot read this message.',

INPUT_PLACEHOLDER_ACTIVE: 'Enter message',
INPUT_PLACEHOLDER_DISABLED: 'Chat is unavailable in this channel',
INPUT_EDIT_OK: 'Save',
INPUT_EDIT_CANCEL: 'Cancel',

DIALOG_MESSAGE_COPY: 'Copy',
DIALOG_MESSAGE_EDIT: 'Edit',
DIALOG_MESSAGE_DELETE: 'Delete',
DIALOG_MESSAGE_DELETE_CONFIRM_TITLE: 'Delete message?',
DIALOG_MESSAGE_DELETE_CONFIRM_OK: 'Delete',
DIALOG_MESSAGE_DELETE_CONFIRM_CANCEL: 'Cancel',
DIALOG_MESSAGE_FAILED_RETRY: 'Retry',
DIALOG_MESSAGE_FAILED_REMOVE: 'Remove',

DIALOG_ATTACHMENT_CAMERA: 'Camera',
DIALOG_ATTACHMENT_PHOTO_LIBRARY: 'Photo library',
DIALOG_ATTACHMENT_FILES: 'Files',

...overrides?.GROUP_CHANNEL?.FRAGMENT,
},
},
Expand Down Expand Up @@ -129,6 +212,7 @@ export const createBaseLabel = ({ dateLocale, overrides }: LabelCreateOptions):
if (len === 0) return 'Create';
return `${len} Create`;
},
USER_NO_NAME: '(No name)',
...overrides?.INVITE_MEMBERS,
},
PLACEHOLDER: {
Expand All @@ -144,4 +228,11 @@ export const createBaseLabel = ({ dateLocale, overrides }: LabelCreateOptions):
...overrides?.PLACEHOLDER?.ERROR_SOMETHING_IS_WRONG,
},
},
DIALOG: {
ALERT_DEFAULT_OK: 'OK',
PROMPT_DEFAULT_OK: 'Submit',
PROMPT_DEFAULT_CANCEL: 'Cancel',
PROMPT_DEFAULT_PLACEHOLDER: 'Enter',
...overrides?.DIALOG,
},
});
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
import createAppearanceHelper from '../styles/appearanceHelper';
import createAppearanceHelper from '../styles/createAppearanceHelper';

describe('styles', function () {
test('createAppearanceHelper', function () {
const helper = createAppearanceHelper('light');
expect(typeof helper['select']).toBe('function');
const select = createAppearanceHelper('light');
expect(typeof select).toBe('function');

const selected1 = helper.select({ light: 'light-value', dark: 'dark-value', default: 'default-value' });
const selected2 = helper.select({ light: undefined, dark: 'dark-value' });
const selected3 = helper.select({ light: undefined, dark: undefined, default: 'default-value' });
const selected1 = select({ light: 'light-value', dark: 'dark-value', default: 'default-value' });
const selected2 = select({ light: undefined, dark: 'dark-value' });
const selected3 = select({ light: undefined, dark: undefined, default: 'default-value' });

expect(selected1).toBe('light-value');
expect(selected1).not.toBe('dark-value');
Expand All @@ -22,13 +22,13 @@ describe('styles', function () {
expect(selected3).not.toBe('light-value');

try {
helper.select({ light: undefined });
select({ light: undefined });
} catch (err) {
expect(err).not.toBeUndefined();
}

try {
helper.select({ dark: undefined });
select({ dark: undefined });
} catch (err) {
expect(err).not.toBeUndefined();
}
Expand Down
4 changes: 2 additions & 2 deletions packages/uikit-react-native-foundation/src/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,8 @@ export { default as TextInput } from './ui/TextInput';
/** Styles **/
export { default as useHeaderStyle } from './styles/useHeaderStyle';
export { default as getDefaultHeaderHeight } from './styles/getDefaultHeaderHeight';
export { default as appearanceHelper } from './styles/appearanceHelper';
export { default as scaleFactor } from './styles/scaleFactor';
export { default as createAppearanceHelper } from './styles/createAppearanceHelper';
export { default as createScaleFactor } from './styles/createScaleFactor';
export { default as createStyleSheet } from './styles/createStyleSheet';
export { HeaderStyleContext, HeaderStyleProvider } from './styles/HeaderStyleContext';

Expand Down
Original file line number Diff line number Diff line change
@@ -1,17 +1,17 @@
import type { AppearanceHelper, UIKitAppearance } from '../types';
import type { UIKitAppearance, UIKitTheme } from '../types';

/**
* Appearance helper factory function
* Return a function that returns a value matching with the current appearance among the input values.
* @param appearance
* @returns AppearanceHelper
* @returns Function
* */
const createAppearanceHelper = (appearance: UIKitAppearance): AppearanceHelper => ({
select(options) {
const createAppearanceHelper =
(appearance: UIKitAppearance): UIKitTheme['select'] =>
(options) => {
const value = options[appearance ?? 'default'] ?? options['light'] ?? options['dark'] ?? options['default'];
if (!value) throw Error('Not provided any selectable appearance values');
return value;
},
});
};

export default createAppearanceHelper;
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,4 @@ const createScaleFactor = (deviceWidth = DESIGNED_DEVICE_WIDTH) => {
return (dp: number) => PixelRatio.roundToNearestPixel(dp * ratio);
};

export const defaultScaleFactor = createScaleFactor();

export default createScaleFactor;
Original file line number Diff line number Diff line change
@@ -1,17 +1,18 @@
import { ImageStyle, StyleSheet, TextStyle, ViewStyle } from 'react-native';

import { defaultScaleFactor } from './scaleFactor';
import createScaleFactor from './createScaleFactor';

type Styles = ViewStyle | TextStyle | ImageStyle;
type StylePreprocessor<T extends Styles = Styles> = { [key in keyof T]: (val: NonNullable<T[key]>) => typeof val };

const SCALE_FACTOR = defaultScaleFactor;
const SCALE_FACTOR_WITH_STR = (val: string | number) => (typeof val === 'string' ? val : SCALE_FACTOR(val));
let UIKIT_INTERNAL_SCALE_FACTOR = createScaleFactor();
const SCALE_FACTOR_WITH_STR = (val: string | number) =>
typeof val === 'string' ? val : UIKIT_INTERNAL_SCALE_FACTOR(val);

const preProcessor: Partial<StylePreprocessor> = {
'fontSize': SCALE_FACTOR,
'lineHeight': SCALE_FACTOR,
'borderRadius': SCALE_FACTOR,
'fontSize': UIKIT_INTERNAL_SCALE_FACTOR,
'lineHeight': UIKIT_INTERNAL_SCALE_FACTOR,
'borderRadius': UIKIT_INTERNAL_SCALE_FACTOR,
'minWidth': SCALE_FACTOR_WITH_STR,
'minHeight': SCALE_FACTOR_WITH_STR,
'height': SCALE_FACTOR_WITH_STR,
Expand Down Expand Up @@ -57,4 +58,8 @@ const createStyleSheet = <T extends StyleSheet.NamedStyles<T>>(styles: T | Style
return StyleSheet.create<T>(styles);
};

createStyleSheet.updateScaleFactor = (scaleFactor: (dp: number) => number) => {
UIKIT_INTERNAL_SCALE_FACTOR = scaleFactor;
};

export default createStyleSheet;
32 changes: 32 additions & 0 deletions packages/uikit-react-native-foundation/src/styles/themeFactory.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import Palette from '../theme/Palette';
import { createTypography } from '../theme/Typography';
import type { UIKitColors, UIKitTheme } from '../types';
import createAppearanceHelper from './createAppearanceHelper';
import createScaleFactor from './createScaleFactor';
import createStyleSheet from './createStyleSheet';

type Options = {
appearance: UIKitTheme['appearance'];
palette?: UIKitTheme['palette'];
colors: (palette: UIKitTheme['palette']) => UIKitColors;
scaleFactorOption?: {
scaleFactor: UIKitTheme['scaleFactor'];
applyToCreateStyleSheet: boolean;
};
};

const defaultScaleFactor = createScaleFactor();

export const themeFactory = ({ appearance, scaleFactorOption, palette = Palette, colors }: Options): UIKitTheme => {
if (scaleFactorOption?.applyToCreateStyleSheet) createStyleSheet.updateScaleFactor(scaleFactorOption.scaleFactor);
const _scaleFactor = scaleFactorOption?.scaleFactor ?? defaultScaleFactor;

return {
appearance,
palette,
select: createAppearanceHelper(appearance),
colors: colors(palette),
scaleFactor: _scaleFactor,
typography: createTypography({ shared: { fontFamily: 'System' } }, _scaleFactor),
};
};
Loading

0 comments on commit 0ff1809

Please sign in to comment.