@charcoal-ui/styled 1.0.0-alpha.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/src/lib.ts ADDED
@@ -0,0 +1,178 @@
1
+ import { Key } from './types'
2
+ import { unreachable } from './util'
3
+
4
+ /**
5
+ * 配列で指定したプロパティを動的に生やす
6
+ *
7
+ * @param source 拡張するオブジェクト
8
+ * @param member オブジェクトに生やすプロパティ一覧
9
+ * @param chain プロパティに格納される値を生成する関数
10
+ *
11
+ * @example
12
+ *
13
+ * const o = factory({}, ['red', 'blue'],
14
+ * color => hex(color)
15
+ * )
16
+ *
17
+ * console.log(o.red) //=> #ff0000
18
+ */
19
+ export const factory = <TSource, TMember extends readonly Key[], TValue>(
20
+ source: TSource,
21
+ member: TMember,
22
+ chain: (key: TMember[number]) => TValue
23
+ ) =>
24
+ Object.defineProperties(
25
+ source,
26
+ Object.fromEntries(
27
+ member.map((key) => [
28
+ key,
29
+ { get: () => chain(key), enumerable: true, configurable: true },
30
+ ])
31
+ )
32
+ ) as TSource & { readonly [key in TMember[number]]: TValue }
33
+
34
+ /**
35
+ * 配列で指定した名前のメソッドを動的に生やす
36
+ *
37
+ * @param source 拡張するオブジェクト
38
+ * @param member オブジェクトに生やすメソッド名一覧
39
+ * @param chain メソッドの戻り値になる値を生成する関数
40
+ *
41
+ * @example
42
+ *
43
+ * const o = argumentedFactory({}, ['red', 'blue'],
44
+ * (color, alpha: number) => hex(color, alpha)
45
+ * )
46
+ *
47
+ * console.log(o.red(0.5)) //=> #ff000077
48
+ */
49
+ export const argumentedFactory = <
50
+ TSource,
51
+ TMember extends readonly string[],
52
+ TValue,
53
+ TArguments extends unknown[]
54
+ >(
55
+ source: TSource,
56
+ member: TMember,
57
+ chain: (key: TMember[number], ...args: TArguments) => TValue
58
+ ) =>
59
+ Object.defineProperties(
60
+ source,
61
+ Object.fromEntries(
62
+ member.map((key) => [
63
+ key,
64
+ {
65
+ value: (...args: TArguments) => chain(key, ...args),
66
+ enumerable: true,
67
+ configurable: true,
68
+ },
69
+ ])
70
+ )
71
+ ) as TSource & {
72
+ readonly [key in TMember[number]]: (...args: TArguments) => TValue
73
+ }
74
+
75
+ /**
76
+ * オブジェクトで指定したプロパティ名と値を動的に生やす
77
+ *
78
+ * @param source 拡張するオブジェクト
79
+ * @param def オブジェクトに生やす定義(プロパティ名と値)
80
+ *
81
+ * @example
82
+ *
83
+ * const o = constFactory({}, {
84
+ * red: '#f00',
85
+ * blue: '#00f',
86
+ * })
87
+ *
88
+ * console.log(o.red) //=> #f00
89
+ */
90
+ export const constFactory = <TSource, TDef extends { [key: string]: unknown }>(
91
+ source: TSource,
92
+ def: TDef
93
+ ) =>
94
+ factory(source, Object.keys(def), (key) => def[key]) as TSource &
95
+ Readonly<TDef>
96
+
97
+ /**
98
+ * 配列で指定したモディファイア(プロパティ)をチェーン可能な再帰オブジェクトを動的に生やす
99
+ *
100
+ * @param modifiers オブジェクトに生やすモディファイヤ一覧
101
+ * @param source 指定されたモディファイヤの一覧から値を生成する関数
102
+ *
103
+ * @example
104
+ *
105
+ * const o = modifiedArgumentedFactory(['red', 'blue'],
106
+ * modifiers => modifiers.map(color => hex(color)).join(',')
107
+ * )
108
+ *
109
+ * console.log(o.red.blue) => #f00,#00f
110
+ */
111
+ export const modifiedFactory = <TSource, T extends Key>(
112
+ modifiers: readonly T[],
113
+ source: (applied: readonly T[]) => TSource
114
+ ) =>
115
+ (function recursiveModifiedFactory(
116
+ applied: readonly T[]
117
+ ): Modified<TSource, T> {
118
+ const notApplied = modifiers.filter((v) => !applied.includes(v))
119
+ return factory(source(applied), notApplied, (modifier) =>
120
+ notApplied.length === 0
121
+ ? unreachable()
122
+ : recursiveModifiedFactory([...applied, modifier])
123
+ )
124
+ })([])
125
+
126
+ export type Modified<TSource, TModifiers extends Key> = TSource & {
127
+ readonly [key in TModifiers]: Modified<TSource, Exclude<TModifiers, key>>
128
+ }
129
+
130
+ /**
131
+ * 配列で指定したモディファイア(メソッド)をチェーン可能な再帰オブジェクトを動的に生やす
132
+ *
133
+ * @param modifiers オブジェクトに生やすモディファイヤ一覧
134
+ * @param source 指定されたモディファイヤの一覧から値を生成する関数
135
+ * @param _inferPhantom 関数形式のモディファイヤの引数型を推論するためのメタタイプ(引数の個数に合わせてタプルで指定する)
136
+ *
137
+ * @example
138
+ *
139
+ * const o = modifiedArgumentedFactory(['red', 'blue'],
140
+ * modifiers => modifiers.map(([color, alpha]) => hex(color, alpha)).join(',')
141
+ * , {} as [number])
142
+ *
143
+ * console.log(o.red(0.5).blue(1)) => #ff000077,#0000ffff
144
+ */
145
+ export const modifiedArgumentedFactory = <
146
+ TSource,
147
+ T extends string,
148
+ TArguments extends unknown[]
149
+ >(
150
+ modifiers: readonly T[],
151
+ source: (applied: readonly [T, ...TArguments][]) => TSource,
152
+ ..._inferPhantom: TArguments
153
+ ) =>
154
+ (function recursiveModifiedFactory(
155
+ applied: readonly [T, ...TArguments][]
156
+ ): ModifiedArgumented<TSource, T, TArguments> {
157
+ const notApplied = modifiers.filter(
158
+ (v) => !applied.map(([w]) => w).includes(v)
159
+ )
160
+ return argumentedFactory(
161
+ source(applied),
162
+ notApplied,
163
+ (modifier, ...args: TArguments) =>
164
+ notApplied.length === 0
165
+ ? unreachable()
166
+ : recursiveModifiedFactory([...applied, [modifier, ...args]])
167
+ )
168
+ })([])
169
+
170
+ export type ModifiedArgumented<
171
+ TSource,
172
+ TModifiers extends string,
173
+ TArguments extends unknown[]
174
+ > = TSource & {
175
+ readonly [key in TModifiers]: (
176
+ ...args: TArguments
177
+ ) => ModifiedArgumented<TSource, Exclude<TModifiers, key>, TArguments>
178
+ }
package/src/theme.ts ADDED
@@ -0,0 +1,107 @@
1
+ import { StyledTheme } from './types'
2
+ import {
3
+ BorderRadius,
4
+ Spacing,
5
+ Typography,
6
+ borderRadius,
7
+ spacing,
8
+ typography,
9
+ Breakpoint,
10
+ breakpoint,
11
+ } from '@charcoal-ui/foundation'
12
+ import { light as l, dark as d } from '@charcoal-ui/pixiv-theme'
13
+ import { Effect, Material, OpacityEffect, Theme } from '@charcoal-ui/theme'
14
+ import { applyEffect } from '@charcoal-ui/utils'
15
+
16
+ export interface ElementsTheme extends StyledTheme {
17
+ color: Theme['color']
18
+ gradientColor: Theme['gradientColor']
19
+ effect: {
20
+ hover: Effect
21
+ press: Effect
22
+ }
23
+ elementEffect: {
24
+ disabled: OpacityEffect
25
+ }
26
+ spacing: Spacing
27
+ typography: {
28
+ size: Typography
29
+ }
30
+ borderRadius: BorderRadius
31
+ border: {
32
+ default: {
33
+ color: Material
34
+ }
35
+ }
36
+ outline: {
37
+ default: {
38
+ color: string
39
+ weight: 4
40
+ }
41
+ assertive: {
42
+ color: string
43
+ weight: 4
44
+ }
45
+ }
46
+ breakpoint: Breakpoint
47
+ }
48
+
49
+ const outlineEffect = {
50
+ type: 'opacity',
51
+ opacity: 0.32,
52
+ } as const
53
+
54
+ const common = {
55
+ borderRadius,
56
+ spacing,
57
+ typography: {
58
+ size: typography,
59
+ },
60
+ breakpoint,
61
+ }
62
+
63
+ export const light: ElementsTheme = {
64
+ color: l.color,
65
+ gradientColor: l.gradientColor,
66
+ effect: l.effect,
67
+ elementEffect: l.elementEffect,
68
+ border: {
69
+ default: {
70
+ color: l.color.border,
71
+ },
72
+ },
73
+ outline: {
74
+ default: {
75
+ color: applyEffect(l.color.brand, outlineEffect),
76
+ weight: 4,
77
+ },
78
+ assertive: {
79
+ color: applyEffect(l.color.assertive, outlineEffect),
80
+ weight: 4,
81
+ },
82
+ },
83
+ ...common,
84
+ }
85
+
86
+ export const dark: ElementsTheme = {
87
+ color: d.color,
88
+ gradientColor: d.gradientColor,
89
+ effect: d.effect,
90
+ elementEffect: d.elementEffect,
91
+ border: {
92
+ default: {
93
+ color: d.color.border,
94
+ },
95
+ },
96
+ outline: {
97
+ default: {
98
+ color: applyEffect(d.color.brand, outlineEffect),
99
+ weight: 4,
100
+ },
101
+ assertive: {
102
+ color: applyEffect(d.color.assertive, outlineEffect),
103
+ weight: 4,
104
+ },
105
+ },
106
+ ...common,
107
+ }
package/src/types.ts ADDED
@@ -0,0 +1,43 @@
1
+ import { TypographyDescriptor } from '@charcoal-ui/foundation'
2
+ import {
3
+ Effect,
4
+ GradientMaterial,
5
+ Material,
6
+ OpacityEffect,
7
+ } from '@charcoal-ui/theme'
8
+
9
+ export type EffectType = 'hover' | 'press' | 'disabled'
10
+
11
+ export type Key = string | number | symbol
12
+
13
+ export type ColorStyleTheme = {
14
+ [key in Key]: Material
15
+ }
16
+
17
+ export interface StyledTheme {
18
+ color: ColorStyleTheme
19
+ gradientColor: { [key in Key]: GradientMaterial }
20
+ effect: { [key in EffectType]?: Effect }
21
+ elementEffect: { [key in EffectType]?: OpacityEffect }
22
+ spacing: { [key in Key]: number }
23
+ typography: {
24
+ size: { [key in Key]: TypographyDescriptor }
25
+ // TODO
26
+ // weight: { [key in Key]: string }
27
+ // variant: { [key in Key]: string }
28
+ }
29
+ borderRadius: { [key in Key]: number }
30
+ border: {
31
+ [key in Key]: {
32
+ color: Material
33
+ // TODO
34
+ // thickness: number
35
+ }
36
+ }
37
+ outline: {
38
+ [key in Key]: {
39
+ color: Material
40
+ weight: number
41
+ }
42
+ }
43
+ }
package/src/util.ts ADDED
@@ -0,0 +1,78 @@
1
+ /**
2
+ * Function used to assert a given code path is unreachable
3
+ */
4
+ export function unreachable(): never
5
+ /**
6
+ * Function used to assert a given code path is unreachable.
7
+ * Very useful for ensuring switches are exhaustive:
8
+ *
9
+ * ```ts
10
+ * switch (a.type) {
11
+ * case Types.A:
12
+ * case Types.B:
13
+ * break
14
+ * default:
15
+ * unreachable(a) // will cause a build error if there was
16
+ * // a Types.C that was not checked
17
+ * }
18
+ * ```
19
+ *
20
+ * @param value Value to be asserted as unreachable
21
+ */
22
+ // NOTE: Uses separate overloads, _not_ `value?: never`, to not allow `undefined` to be passed
23
+ // eslint-disable-next-line @typescript-eslint/unified-signatures
24
+ export function unreachable(value: never): never
25
+ export function unreachable(value?: never): never {
26
+ throw new Error(
27
+ arguments.length === 0
28
+ ? 'unreachable'
29
+ : `unreachable (${JSON.stringify(value)})`
30
+ )
31
+ }
32
+
33
+ /**
34
+ * Check whether a value is non-null and non-undefined
35
+ *
36
+ * @param value nullable
37
+ */
38
+ export const isPresent = <T>(value: T): value is NonNullable<T> => value != null
39
+
40
+ type Head<U> = U extends [infer T, ...any[]] ? T : never
41
+
42
+ type Tail<U> = U extends [any, any, ...any[]]
43
+ ? ((...args: U) => any) extends (head: any, ...args: infer T) => any
44
+ ? T
45
+ : never
46
+ : never
47
+ // Buggy at ts@4.0.0-dev20200506
48
+ // type Tail<U> = U extends [any, ...infer T] ? T : never
49
+
50
+ type RecursiveObjectAssign<T, S extends any[]> = {
51
+ 0: T & Head<S>
52
+ 1: RecursiveObjectAssign<T & Head<S>, Tail<S>>
53
+ }[Tail<S> extends never ? 0 : 1]
54
+
55
+ type ObjectAssign<T extends any[]> = RecursiveObjectAssign<
56
+ Record<string, unknown>,
57
+ T
58
+ >
59
+
60
+ export function objectAssign<T extends any[]>(...sources: T) {
61
+ return Object.assign({}, ...sources) as ObjectAssign<T>
62
+ }
63
+
64
+ export function objectKeys<V, K extends keyof V>(obj: V) {
65
+ return Object.keys(obj) as K[]
66
+ }
67
+
68
+ export interface ReadonlyArrayConstructor {
69
+ isArray(value: any): value is readonly any[]
70
+ }
71
+
72
+ export function extractNonNullKeys<V, K extends keyof V>(obj: {
73
+ [key in K]: V[key]
74
+ }) {
75
+ return Object.entries(obj)
76
+ .filter(([_, v]) => v !== null)
77
+ .map(([k]) => k) as { [key in K]: V[key] extends null ? never : key }[K][]
78
+ }