@ezpg/app-ui 0.8.0 → 0.9.0

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/CHANGELOG.md CHANGED
@@ -1,5 +1,11 @@
1
1
  # @ezpg/app-ui
2
2
 
3
+ ## 0.9.0
4
+
5
+ ### Minor Changes
6
+
7
+ - IntroTemplate(첫 실행 소개 넘김 화면 — 장마다 그림 · 제목, 넘김 점, 다음 · 시작하기 주 버튼, 마지막 장 전까지 작은 Skip)
8
+
3
9
  ## 0.8.0
4
10
 
5
11
  ### Minor Changes
@@ -3,6 +3,7 @@
3
3
  export { DetailTemplate, DashboardTemplate, FormTemplate, MenuTemplate } from "./scroll-templates.js";
4
4
  export { EditorTemplate } from "./editor-template.js";
5
5
  export { ListTemplate } from "./list-template.js";
6
+ export { IntroTemplate } from "./intro-template.js";
6
7
  export { ResultTemplate } from "./result-template.js";
7
8
  export { SystemTemplate } from "./system-template.js";
8
9
  export { ListSection } from "./parts.js";
@@ -1 +1 @@
1
- {"version":3,"names":["DetailTemplate","DashboardTemplate","FormTemplate","MenuTemplate","EditorTemplate","ListTemplate","ResultTemplate","SystemTemplate","ListSection","Group"],"sourceRoot":"../../../src","sources":["templates/index.ts"],"mappings":";;AAAA,SACEA,cAAc,EACdC,iBAAiB,EACjBC,YAAY,EACZC,YAAY,QACP,uBAAoB;AAO3B,SAASC,cAAc,QAAQ,sBAAmB;AAMlD,SAASC,YAAY,QAAQ,oBAAiB;AAE9C,SAASC,cAAc,QAAQ,sBAAmB;AAElD,SAASC,cAAc,QAAQ,sBAAmB;AAElD,SAASC,WAAW,QAAQ,YAAS;AACrC,SAASC,KAAK,QAAQ,YAAS","ignoreList":[]}
1
+ {"version":3,"names":["DetailTemplate","DashboardTemplate","FormTemplate","MenuTemplate","EditorTemplate","ListTemplate","IntroTemplate","ResultTemplate","SystemTemplate","ListSection","Group"],"sourceRoot":"../../../src","sources":["templates/index.ts"],"mappings":";;AAAA,SACEA,cAAc,EACdC,iBAAiB,EACjBC,YAAY,EACZC,YAAY,QACP,uBAAoB;AAO3B,SAASC,cAAc,QAAQ,sBAAmB;AAMlD,SAASC,YAAY,QAAQ,oBAAiB;AAE9C,SAASC,aAAa,QAAQ,qBAAkB;AAEhD,SAASC,cAAc,QAAQ,sBAAmB;AAElD,SAASC,cAAc,QAAQ,sBAAmB;AAElD,SAASC,WAAW,QAAQ,YAAS;AACrC,SAASC,KAAK,QAAQ,YAAS","ignoreList":[]}
@@ -0,0 +1,136 @@
1
+ "use strict";
2
+
3
+ import { useRef, useState } from 'react';
4
+ import { ScrollView, View } from 'react-native';
5
+ import { Button } from "../components/button/button.js";
6
+ import { Text } from "../components/text/text.js";
7
+ import { PressableBase } from "../internal/pressable-base.js";
8
+ import { useTheme } from "../theme/theme-provider.js";
9
+ import { makeStyles } from "../utils/styles.js";
10
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
11
+ const useStyles = makeStyles(t => ({
12
+ root: {
13
+ flex: 1,
14
+ backgroundColor: t.colors.background
15
+ },
16
+ top: {
17
+ height: t.minTouchSize,
18
+ flexDirection: 'row',
19
+ justifyContent: 'flex-end',
20
+ paddingHorizontal: t.layout.screenX
21
+ },
22
+ skip: {
23
+ minWidth: t.minTouchSize,
24
+ alignItems: 'flex-end',
25
+ justifyContent: 'center'
26
+ },
27
+ pager: {
28
+ flex: 1
29
+ },
30
+ page: {
31
+ flex: 1,
32
+ justifyContent: 'center',
33
+ gap: t.layout.titleGap,
34
+ paddingHorizontal: t.layout.screenX
35
+ },
36
+ title: {
37
+ textAlign: 'center'
38
+ },
39
+ footer: {
40
+ gap: t.layout.titleGap,
41
+ paddingHorizontal: t.layout.screenX,
42
+ paddingTop: t.layout.footerTop
43
+ },
44
+ dots: {
45
+ flexDirection: 'row',
46
+ justifyContent: 'center',
47
+ gap: t.space[2]
48
+ },
49
+ dot: {
50
+ width: t.space[2],
51
+ height: t.space[2],
52
+ borderRadius: t.radius.full,
53
+ backgroundColor: t.colors.input
54
+ },
55
+ dotOn: {
56
+ width: t.space[5],
57
+ backgroundColor: t.colors.primary
58
+ }
59
+ }));
60
+ export function IntroTemplate({
61
+ pages,
62
+ onDone,
63
+ nextLabel = '다음',
64
+ doneLabel = '시작하기',
65
+ skipLabel = 'Skip',
66
+ bottomInset = 0
67
+ }) {
68
+ const t = useTheme();
69
+ const s = useStyles();
70
+ const pager = useRef(null);
71
+ const [width, setWidth] = useState(0);
72
+ const [index, setIndex] = useState(0);
73
+ const last = index >= pages.length - 1;
74
+ const art = Math.max(0, width - t.layout.screenX * 2);
75
+ const next = () => {
76
+ if (last) {
77
+ onDone();
78
+ return;
79
+ }
80
+ pager.current?.scrollTo({
81
+ x: (index + 1) * width,
82
+ animated: true
83
+ });
84
+ setIndex(index + 1);
85
+ };
86
+ return /*#__PURE__*/_jsxs(View, {
87
+ style: s.root,
88
+ children: [/*#__PURE__*/_jsx(View, {
89
+ style: s.top,
90
+ children: last ? null : /*#__PURE__*/_jsx(PressableBase, {
91
+ onPress: onDone,
92
+ accessibilityLabel: skipLabel,
93
+ style: s.skip,
94
+ children: /*#__PURE__*/_jsx(Text, {
95
+ variant: "caption",
96
+ tone: "muted",
97
+ children: skipLabel
98
+ })
99
+ })
100
+ }), /*#__PURE__*/_jsx(ScrollView, {
101
+ ref: pager,
102
+ horizontal: true,
103
+ pagingEnabled: true,
104
+ showsHorizontalScrollIndicator: false,
105
+ onLayout: e => setWidth(e.nativeEvent.layout.width),
106
+ onMomentumScrollEnd: e => width && setIndex(Math.round(e.nativeEvent.contentOffset.x / width)),
107
+ style: s.pager,
108
+ children: pages.map(page => /*#__PURE__*/_jsxs(View, {
109
+ style: [s.page, {
110
+ width
111
+ }],
112
+ children: [art ? page.art(art) : null, /*#__PURE__*/_jsx(Text, {
113
+ variant: "screenTitle",
114
+ accessibilityRole: "header",
115
+ style: s.title,
116
+ children: page.title
117
+ })]
118
+ }, page.key))
119
+ }), /*#__PURE__*/_jsxs(View, {
120
+ style: [s.footer, {
121
+ paddingBottom: t.layout.footerBottom + bottomInset
122
+ }],
123
+ children: [/*#__PURE__*/_jsx(View, {
124
+ style: s.dots,
125
+ children: pages.map((page, i) => /*#__PURE__*/_jsx(View, {
126
+ style: [s.dot, i === index && s.dotOn]
127
+ }, page.key))
128
+ }), /*#__PURE__*/_jsx(Button, {
129
+ size: "lg",
130
+ onPress: next,
131
+ children: last ? doneLabel : nextLabel
132
+ })]
133
+ })]
134
+ });
135
+ }
136
+ //# sourceMappingURL=intro-template.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"names":["useRef","useState","ScrollView","View","Button","Text","PressableBase","useTheme","makeStyles","jsx","_jsx","jsxs","_jsxs","useStyles","t","root","flex","backgroundColor","colors","background","top","height","minTouchSize","flexDirection","justifyContent","paddingHorizontal","layout","screenX","skip","minWidth","alignItems","pager","page","gap","titleGap","title","textAlign","footer","paddingTop","footerTop","dots","space","dot","width","borderRadius","radius","full","input","dotOn","primary","IntroTemplate","pages","onDone","nextLabel","doneLabel","skipLabel","bottomInset","s","setWidth","index","setIndex","last","length","art","Math","max","next","current","scrollTo","x","animated","style","children","onPress","accessibilityLabel","variant","tone","ref","horizontal","pagingEnabled","showsHorizontalScrollIndicator","onLayout","e","nativeEvent","onMomentumScrollEnd","round","contentOffset","map","accessibilityRole","key","paddingBottom","footerBottom","i","size"],"sourceRoot":"../../../src","sources":["templates/intro-template.tsx"],"mappings":"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","ignoreList":[]}
@@ -4,6 +4,8 @@ export { EditorTemplate } from './editor-template.js';
4
4
  export type { EditorSheet, EditorTemplateProps, EditorTool, } from './editor-template.js';
5
5
  export { ListTemplate } from './list-template.js';
6
6
  export type { ListTemplateProps, ListTemplateStatus } from './list-template.js';
7
+ export { IntroTemplate } from './intro-template.js';
8
+ export type { IntroPage, IntroTemplateProps } from './intro-template.js';
7
9
  export { ResultTemplate } from './result-template.js';
8
10
  export type { ResultPreview, ResultTemplateProps } from './result-template.js';
9
11
  export { SystemTemplate } from './system-template.js';
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/templates/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,cAAc,EACd,iBAAiB,EACjB,YAAY,EACZ,YAAY,GACb,MAAM,uBAAoB,CAAC;AAC5B,YAAY,EACV,sBAAsB,EACtB,mBAAmB,EACnB,iBAAiB,EACjB,iBAAiB,GAClB,MAAM,uBAAoB,CAAC;AAC5B,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAmB,CAAC;AACnD,YAAY,EACV,WAAW,EACX,mBAAmB,EACnB,UAAU,GACX,MAAM,sBAAmB,CAAC;AAC3B,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAiB,CAAC;AAC/C,YAAY,EAAE,iBAAiB,EAAE,kBAAkB,EAAE,MAAM,oBAAiB,CAAC;AAC7E,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAmB,CAAC;AACnD,YAAY,EAAE,aAAa,EAAE,mBAAmB,EAAE,MAAM,sBAAmB,CAAC;AAC5E,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAmB,CAAC;AACnD,YAAY,EAAE,mBAAmB,EAAE,MAAM,sBAAmB,CAAC;AAC7D,OAAO,EAAE,WAAW,EAAE,MAAM,YAAS,CAAC;AACtC,OAAO,EAAE,KAAK,EAAE,MAAM,YAAS,CAAC;AAChC,YAAY,EAAE,UAAU,EAAE,MAAM,YAAS,CAAC;AAC1C,YAAY,EAAE,YAAY,EAAE,cAAc,EAAE,cAAc,EAAE,MAAM,YAAS,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/templates/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,cAAc,EACd,iBAAiB,EACjB,YAAY,EACZ,YAAY,GACb,MAAM,uBAAoB,CAAC;AAC5B,YAAY,EACV,sBAAsB,EACtB,mBAAmB,EACnB,iBAAiB,EACjB,iBAAiB,GAClB,MAAM,uBAAoB,CAAC;AAC5B,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAmB,CAAC;AACnD,YAAY,EACV,WAAW,EACX,mBAAmB,EACnB,UAAU,GACX,MAAM,sBAAmB,CAAC;AAC3B,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAiB,CAAC;AAC/C,YAAY,EAAE,iBAAiB,EAAE,kBAAkB,EAAE,MAAM,oBAAiB,CAAC;AAC7E,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAkB,CAAC;AACjD,YAAY,EAAE,SAAS,EAAE,kBAAkB,EAAE,MAAM,qBAAkB,CAAC;AACtE,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAmB,CAAC;AACnD,YAAY,EAAE,aAAa,EAAE,mBAAmB,EAAE,MAAM,sBAAmB,CAAC;AAC5E,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAmB,CAAC;AACnD,YAAY,EAAE,mBAAmB,EAAE,MAAM,sBAAmB,CAAC;AAC7D,OAAO,EAAE,WAAW,EAAE,MAAM,YAAS,CAAC;AACtC,OAAO,EAAE,KAAK,EAAE,MAAM,YAAS,CAAC;AAChC,YAAY,EAAE,UAAU,EAAE,MAAM,YAAS,CAAC;AAC1C,YAAY,EAAE,YAAY,EAAE,cAAc,EAAE,cAAc,EAAE,MAAM,YAAS,CAAC"}
@@ -0,0 +1,16 @@
1
+ import { type ReactNode } from 'react';
2
+ export type IntroPage = {
3
+ key: string;
4
+ title: string;
5
+ art: (width: number) => ReactNode;
6
+ };
7
+ export type IntroTemplateProps = {
8
+ pages: readonly IntroPage[];
9
+ onDone: () => void;
10
+ nextLabel?: string;
11
+ doneLabel?: string;
12
+ skipLabel?: string;
13
+ bottomInset?: number;
14
+ };
15
+ export declare function IntroTemplate({ pages, onDone, nextLabel, doneLabel, skipLabel, bottomInset, }: IntroTemplateProps): import("react").JSX.Element;
16
+ //# sourceMappingURL=intro-template.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"intro-template.d.ts","sourceRoot":"","sources":["../../../../src/templates/intro-template.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAuC,KAAK,SAAS,EAAE,MAAM,OAAO,CAAC;AAc5E,MAAM,MAAM,SAAS,GAAG;IACtB,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,EAAE,MAAM,CAAC;IACd,GAAG,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,SAAS,CAAC;CACnC,CAAC;AAEF,MAAM,MAAM,kBAAkB,GAAG;IAC/B,KAAK,EAAE,SAAS,SAAS,EAAE,CAAC;IAC5B,MAAM,EAAE,MAAM,IAAI,CAAC;IACnB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB,CAAC;AA0CF,wBAAgB,aAAa,CAAC,EAC5B,KAAK,EACL,MAAM,EACN,SAAgB,EAChB,SAAkB,EAClB,SAAkB,EAClB,WAAe,GAChB,EAAE,kBAAkB,+BA4EpB"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ezpg/app-ui",
3
- "version": "0.8.0",
3
+ "version": "0.9.0",
4
4
  "description": "React Native UI components for EZPG partner apps",
5
5
  "main": "./lib/module/index.js",
6
6
  "types": "./lib/typescript/src/index.d.ts",
@@ -18,6 +18,8 @@ export type {
18
18
  } from './editor-template';
19
19
  export { ListTemplate } from './list-template';
20
20
  export type { ListTemplateProps, ListTemplateStatus } from './list-template';
21
+ export { IntroTemplate } from './intro-template';
22
+ export type { IntroPage, IntroTemplateProps } from './intro-template';
21
23
  export { ResultTemplate } from './result-template';
22
24
  export type { ResultPreview, ResultTemplateProps } from './result-template';
23
25
  export { SystemTemplate } from './system-template';
@@ -0,0 +1,153 @@
1
+ import { useRef, useState, type ComponentRef, type ReactNode } from 'react';
2
+ import {
3
+ ScrollView,
4
+ View,
5
+ type LayoutChangeEvent,
6
+ type NativeScrollEvent,
7
+ type NativeSyntheticEvent,
8
+ } from 'react-native';
9
+ import { Button } from '../components/button/button';
10
+ import { Text } from '../components/text/text';
11
+ import { PressableBase } from '../internal/pressable-base';
12
+ import { useTheme } from '../theme/theme-provider';
13
+ import { makeStyles } from '../utils/styles';
14
+
15
+ export type IntroPage = {
16
+ key: string;
17
+ title: string;
18
+ art: (width: number) => ReactNode;
19
+ };
20
+
21
+ export type IntroTemplateProps = {
22
+ pages: readonly IntroPage[];
23
+ onDone: () => void;
24
+ nextLabel?: string;
25
+ doneLabel?: string;
26
+ skipLabel?: string;
27
+ bottomInset?: number;
28
+ };
29
+
30
+ const useStyles = makeStyles((t) => ({
31
+ root: { flex: 1, backgroundColor: t.colors.background },
32
+ top: {
33
+ height: t.minTouchSize,
34
+ flexDirection: 'row',
35
+ justifyContent: 'flex-end',
36
+ paddingHorizontal: t.layout.screenX,
37
+ },
38
+ skip: {
39
+ minWidth: t.minTouchSize,
40
+ alignItems: 'flex-end',
41
+ justifyContent: 'center',
42
+ },
43
+ pager: { flex: 1 },
44
+ page: {
45
+ flex: 1,
46
+ justifyContent: 'center',
47
+ gap: t.layout.titleGap,
48
+ paddingHorizontal: t.layout.screenX,
49
+ },
50
+ title: { textAlign: 'center' },
51
+ footer: {
52
+ gap: t.layout.titleGap,
53
+ paddingHorizontal: t.layout.screenX,
54
+ paddingTop: t.layout.footerTop,
55
+ },
56
+ dots: {
57
+ flexDirection: 'row',
58
+ justifyContent: 'center',
59
+ gap: t.space[2],
60
+ },
61
+ dot: {
62
+ width: t.space[2],
63
+ height: t.space[2],
64
+ borderRadius: t.radius.full,
65
+ backgroundColor: t.colors.input,
66
+ },
67
+ dotOn: { width: t.space[5], backgroundColor: t.colors.primary },
68
+ }));
69
+
70
+ export function IntroTemplate({
71
+ pages,
72
+ onDone,
73
+ nextLabel = '다음',
74
+ doneLabel = '시작하기',
75
+ skipLabel = 'Skip',
76
+ bottomInset = 0,
77
+ }: IntroTemplateProps) {
78
+ const t = useTheme();
79
+ const s = useStyles();
80
+ const pager = useRef<ComponentRef<typeof ScrollView>>(null);
81
+ const [width, setWidth] = useState(0);
82
+ const [index, setIndex] = useState(0);
83
+ const last = index >= pages.length - 1;
84
+ const art = Math.max(0, width - t.layout.screenX * 2);
85
+
86
+ const next = () => {
87
+ if (last) {
88
+ onDone();
89
+ return;
90
+ }
91
+ pager.current?.scrollTo({ x: (index + 1) * width, animated: true });
92
+ setIndex(index + 1);
93
+ };
94
+
95
+ return (
96
+ <View style={s.root}>
97
+ <View style={s.top}>
98
+ {last ? null : (
99
+ <PressableBase
100
+ onPress={onDone}
101
+ accessibilityLabel={skipLabel}
102
+ style={s.skip}
103
+ >
104
+ <Text variant="caption" tone="muted">
105
+ {skipLabel}
106
+ </Text>
107
+ </PressableBase>
108
+ )}
109
+ </View>
110
+ <ScrollView
111
+ ref={pager}
112
+ horizontal
113
+ pagingEnabled
114
+ showsHorizontalScrollIndicator={false}
115
+ onLayout={(e: LayoutChangeEvent) =>
116
+ setWidth(e.nativeEvent.layout.width)
117
+ }
118
+ onMomentumScrollEnd={(e: NativeSyntheticEvent<NativeScrollEvent>) =>
119
+ width && setIndex(Math.round(e.nativeEvent.contentOffset.x / width))
120
+ }
121
+ style={s.pager}
122
+ >
123
+ {pages.map((page) => (
124
+ <View key={page.key} style={[s.page, { width }]}>
125
+ {art ? page.art(art) : null}
126
+ <Text
127
+ variant="screenTitle"
128
+ accessibilityRole="header"
129
+ style={s.title}
130
+ >
131
+ {page.title}
132
+ </Text>
133
+ </View>
134
+ ))}
135
+ </ScrollView>
136
+ <View
137
+ style={[
138
+ s.footer,
139
+ { paddingBottom: t.layout.footerBottom + bottomInset },
140
+ ]}
141
+ >
142
+ <View style={s.dots}>
143
+ {pages.map((page, i) => (
144
+ <View key={page.key} style={[s.dot, i === index && s.dotOn]} />
145
+ ))}
146
+ </View>
147
+ <Button size="lg" onPress={next}>
148
+ {last ? doneLabel : nextLabel}
149
+ </Button>
150
+ </View>
151
+ </View>
152
+ );
153
+ }