@playkit-js/transcript 2.1.4 → 2.1.5-canary.16-1ac3e09
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 +9 -0
- package/LICENSE +5 -5
- package/README.md +122 -24
- package/dist/1501adfdf5c835667ce7.svg +9 -0
- package/dist/301e7a199b2cd06c2edf.svg +9 -0
- package/dist/33bce27c0f546e80478c.svg +36 -0
- package/dist/6a4867d3d9170cc2a24d.svg +9 -0
- package/dist/73bab0af28a1c7aed29f.svg +9 -0
- package/dist/84087eb1cff72e5e6bd3.svg +9 -0
- package/dist/95d7192dc427afb678d0.svg +9 -0
- package/dist/cea5d6a7f050cbd199a1.svg +9 -0
- package/dist/d93f06ff32cdfcd016df.svg +9 -0
- package/dist/e5496f4c01207db44ffc.svg +9 -0
- package/dist/playkit-transcript.js +1 -31
- package/dist/playkit-transcript.js.map +1 -1
- package/package.json +53 -50
- package/src/components/a11y-wrapper/a11y-wrapper.ts +26 -0
- package/src/components/a11y-wrapper/index.ts +1 -0
- package/src/components/caption/caption.scss +1 -1
- package/src/components/caption/caption.tsx +118 -140
- package/src/components/caption/index.ts +1 -1
- package/src/components/caption-list/captionList.scss +8 -8
- package/src/components/caption-list/captionList.tsx +115 -117
- package/src/components/caption-list/index.ts +1 -1
- package/src/components/close-button/close-button.scss +11 -0
- package/src/components/close-button/index.tsx +23 -0
- package/src/components/download-print-menu/download-print-menu.scss +49 -48
- package/src/components/download-print-menu/download-print-menu.tsx +147 -125
- package/src/components/download-print-menu/index.ts +1 -1
- package/src/components/icons/index.ts +11 -0
- package/src/components/plugin-button/plugin-button.scss +26 -0
- package/src/components/plugin-button/plugin-button.tsx +29 -0
- package/src/components/popover-menu/index.ts +1 -1
- package/src/components/popover-menu/popover-menu.scss +3 -3
- package/src/components/popover-menu/popover-menu.tsx +25 -25
- package/src/components/search/index.ts +1 -1
- package/src/components/search/search.scss +1 -1
- package/src/components/search/search.tsx +137 -144
- package/src/components/spinner/index.ts +1 -1
- package/src/components/spinner/spinner.scss +58 -50
- package/src/components/spinner/spinner.tsx +9 -9
- package/src/components/transcript/index.ts +1 -1
- package/src/components/transcript/transcript.scss +9 -33
- package/src/components/transcript/transcript.tsx +333 -454
- package/src/global.d.ts +6 -6
- package/src/index.ts +13 -1
- package/src/transcript-plugin.scss +3 -3
- package/src/transcript-plugin.tsx +210 -391
- package/src/types/index.ts +3 -0
- package/src/types/transcript-config.ts +11 -0
- package/src/types/transcript-item-data.ts +29 -0
- package/src/types/types-ui.ts +11 -0
- package/src/utils/debounce.ts +36 -0
- package/src/utils/index.ts +4 -0
- package/src/utils/object-utils.ts +34 -0
- package/src/utils/popover/popover.scss +30 -0
- package/src/utils/popover/popover.tsx +178 -0
- package/src/utils/utils.ts +86 -0
- package/src/variables.scss +14 -0
- package/src/assets/close.svg +0 -10
- package/src/utils.ts +0 -192
package/package.json
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@playkit-js/transcript",
|
|
3
|
-
"version": "2.1.
|
|
3
|
+
"version": "2.1.5-canary.16-1ac3e09",
|
|
4
|
+
"main": "dist/playkit-transcript.js",
|
|
4
5
|
"license": "AGPL-3.0",
|
|
5
6
|
"private": false,
|
|
6
7
|
"bugs": {
|
|
@@ -11,67 +12,69 @@
|
|
|
11
12
|
"type": "git",
|
|
12
13
|
"url": "git+https://github.com/kaltura/playkit-js-transcript.git"
|
|
13
14
|
},
|
|
14
|
-
"dependencies": {
|
|
15
|
-
"@playkit-js-contrib/cli": "1.1.0",
|
|
16
|
-
"@playkit-js-contrib/common": "^4.1.10",
|
|
17
|
-
"@playkit-js-contrib/plugin": "^4.1.13",
|
|
18
|
-
"@playkit-js-contrib/ui": "^4.1.13",
|
|
19
|
-
"classnames": "2.2.6",
|
|
20
|
-
"kaltura-typescript-client": "file:libs/kaltura-typescript-client-7.0.2-v20201028-171103.tgz",
|
|
21
|
-
"xml-js": "^1.6.11"
|
|
22
|
-
},
|
|
23
15
|
"devDependencies": {
|
|
24
|
-
"@
|
|
25
|
-
"@
|
|
26
|
-
"
|
|
27
|
-
"
|
|
28
|
-
"
|
|
29
|
-
"
|
|
30
|
-
"
|
|
31
|
-
"
|
|
16
|
+
"@typescript-eslint/eslint-plugin": "^5.23.0",
|
|
17
|
+
"@typescript-eslint/parser": "^5.23.0",
|
|
18
|
+
"conventional-github-releaser": "3.1.3",
|
|
19
|
+
"cross-env": "^7.0.3",
|
|
20
|
+
"css-loader": "^6.7.1",
|
|
21
|
+
"documentation": "^13.2.5",
|
|
22
|
+
"husky": "^8.0.1",
|
|
23
|
+
"jest": "^28.1.0",
|
|
24
|
+
"kaltura-player-js": "https://github.com/kaltura/kaltura-player-js.git#master",
|
|
25
|
+
"lint-staged": "^12.4.1",
|
|
26
|
+
"node-sass": "^7.0.1",
|
|
32
27
|
"preact": "^10.4.1",
|
|
33
|
-
"
|
|
34
|
-
"
|
|
28
|
+
"prettier": "^2.6.2",
|
|
29
|
+
"sass-loader": "^12.6.0",
|
|
30
|
+
"standard-version": "^9.3.2",
|
|
31
|
+
"style-loader": "1.3.0",
|
|
32
|
+
"ts-loader": "^9.3.0",
|
|
33
|
+
"tslint": "^6.1.3",
|
|
34
|
+
"typescript": "^4.6.4",
|
|
35
|
+
"uglifyjs-webpack-plugin": "^2.2.0",
|
|
36
|
+
"webpack": "^5.72.1",
|
|
37
|
+
"webpack-cli": "^4.9.2",
|
|
38
|
+
"webpack-dev-server": "^4.9.0"
|
|
39
|
+
},
|
|
40
|
+
"peerDependencies": {
|
|
41
|
+
"kaltura-player-js": "https://github.com/kaltura/kaltura-player-js.git#master"
|
|
35
42
|
},
|
|
36
43
|
"scripts": {
|
|
37
|
-
"clean": "rm -rf dist",
|
|
38
|
-
"
|
|
39
|
-
"build": "
|
|
40
|
-
"
|
|
41
|
-
"
|
|
42
|
-
"
|
|
43
|
-
"
|
|
44
|
-
"
|
|
45
|
-
"
|
|
46
|
-
"
|
|
47
|
-
"
|
|
48
|
-
"
|
|
49
|
-
"
|
|
50
|
-
"
|
|
51
|
-
"infra:next": "kcontrib infra --type=next",
|
|
52
|
-
"infra:local": "kcontrib infra --type=local",
|
|
53
|
-
"infra:add": "kcontrib infra --add"
|
|
44
|
+
"clean": "rm -rf ./dist",
|
|
45
|
+
"prebuild": "npm run clean",
|
|
46
|
+
"build": "webpack --mode production",
|
|
47
|
+
"dev": "webpack-dev-server --mode development",
|
|
48
|
+
"watch": "webpack --progress --color --watch --mode development",
|
|
49
|
+
"test": "jest",
|
|
50
|
+
"release": "standard-version",
|
|
51
|
+
"pushTaggedRelease": "git push --follow-tags --no-verify origin master",
|
|
52
|
+
"eslint": "eslint . --color",
|
|
53
|
+
"commit:dist": "git add --force --all dist && (git commit -m 'chore: update dist' || exit 0)",
|
|
54
|
+
"docs:generate": "documentation build flow-typed/** src/** -f md -o docs/configuration.md",
|
|
55
|
+
"docs:serve": "documentation serve flow-typed/** src/** --watch",
|
|
56
|
+
"precommit": "lint-staged",
|
|
57
|
+
"prettier:fix": "prettier --write ."
|
|
54
58
|
},
|
|
55
59
|
"publishConfig": {
|
|
56
60
|
"access": "public"
|
|
57
61
|
},
|
|
58
|
-
"browserslist": {
|
|
59
|
-
"production": [
|
|
60
|
-
">0.2%",
|
|
61
|
-
"not dead",
|
|
62
|
-
"not op_mini all"
|
|
63
|
-
],
|
|
64
|
-
"development": [
|
|
65
|
-
"last 1 chrome version",
|
|
66
|
-
"last 1 firefox version",
|
|
67
|
-
"last 1 safari version"
|
|
68
|
-
]
|
|
69
|
-
},
|
|
70
62
|
"files": [
|
|
71
63
|
"dist",
|
|
72
64
|
"LICENSE",
|
|
73
65
|
"README.md",
|
|
74
66
|
"CHANGELOG.md",
|
|
75
67
|
"src"
|
|
76
|
-
]
|
|
68
|
+
],
|
|
69
|
+
"keywords": [
|
|
70
|
+
"transcript",
|
|
71
|
+
"kaltura",
|
|
72
|
+
"player",
|
|
73
|
+
"playkit-js",
|
|
74
|
+
"playkit-js-transcript",
|
|
75
|
+
"html5 player"
|
|
76
|
+
],
|
|
77
|
+
"dependencies": {
|
|
78
|
+
"stream-browserify": "^3.0.0"
|
|
79
|
+
}
|
|
77
80
|
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import {cloneElement, VNode} from 'preact';
|
|
2
|
+
|
|
3
|
+
const {ENTER, SPACE} = KalturaPlayer.ui.utils.KeyMap;
|
|
4
|
+
|
|
5
|
+
export type OnClickEvent = KeyboardEvent | MouseEvent;
|
|
6
|
+
export type OnClick = (e: OnClickEvent, byKeyboard?: boolean) => void;
|
|
7
|
+
|
|
8
|
+
interface A11yWrapperProps {
|
|
9
|
+
children: VNode;
|
|
10
|
+
onClick: OnClick;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export const A11yWrapper = ({children, onClick}: A11yWrapperProps) => {
|
|
14
|
+
return cloneElement(children, {
|
|
15
|
+
onKeyDown: (e: KeyboardEvent) => {
|
|
16
|
+
if ([SPACE, ENTER].includes(e.keyCode)) {
|
|
17
|
+
e.preventDefault();
|
|
18
|
+
onClick(e, true);
|
|
19
|
+
}
|
|
20
|
+
},
|
|
21
|
+
onClick: (e: MouseEvent) => {
|
|
22
|
+
e.stopPropagation();
|
|
23
|
+
onClick(e);
|
|
24
|
+
}
|
|
25
|
+
});
|
|
26
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './a11y-wrapper';
|
|
@@ -1,161 +1,139 @@
|
|
|
1
|
-
import
|
|
2
|
-
import {
|
|
3
|
-
import
|
|
4
|
-
|
|
1
|
+
import * as styles from './caption.scss';
|
|
2
|
+
import {secontsToTime} from '../../utils';
|
|
3
|
+
import {CuePointData} from '../../types';
|
|
4
|
+
|
|
5
|
+
const {ENTER, Space} = KalturaPlayer.ui.utils.KeyMap;
|
|
6
|
+
|
|
7
|
+
import {Component, h} from 'preact';
|
|
5
8
|
|
|
6
9
|
export interface CaptionProps {
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
10
|
+
showTime: boolean;
|
|
11
|
+
searchLength: number;
|
|
12
|
+
scrollTo(el: HTMLElement): void;
|
|
13
|
+
scrollToSearchMatch(el: HTMLElement): void;
|
|
14
|
+
videoDuration: number;
|
|
12
15
|
}
|
|
13
16
|
|
|
14
17
|
interface ExtendedCaptionProps extends CaptionProps {
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
18
|
+
caption: CuePointData;
|
|
19
|
+
onClick: () => void;
|
|
20
|
+
highlighted: boolean;
|
|
21
|
+
shouldScroll: boolean;
|
|
22
|
+
shouldScrollToSearchMatch: boolean;
|
|
23
|
+
indexMap: Record<string, number> | undefined;
|
|
24
|
+
activeSearchIndex: number;
|
|
25
|
+
longerThanHour: boolean;
|
|
26
|
+
isAutoScrollEnabled: boolean;
|
|
24
27
|
}
|
|
25
28
|
|
|
26
29
|
export class Caption extends Component<ExtendedCaptionProps> {
|
|
27
|
-
|
|
30
|
+
private _hotspotRef: HTMLElement | null = null;
|
|
28
31
|
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
}
|
|
32
|
+
componentDidUpdate() {
|
|
33
|
+
if (this._hotspotRef && this.props.shouldScroll) {
|
|
34
|
+
this.props.scrollTo(this._hotspotRef);
|
|
35
|
+
} else if (this._hotspotRef && this.props.shouldScrollToSearchMatch) {
|
|
36
|
+
this.props.scrollToSearchMatch(this._hotspotRef);
|
|
35
37
|
}
|
|
38
|
+
}
|
|
36
39
|
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
isAutoScrollEnabled,
|
|
42
|
-
activeSearchIndex,
|
|
43
|
-
longerThanHour,
|
|
44
|
-
} = this.props;
|
|
45
|
-
if (longerThanHour !== nextProps.longerThanHour) {
|
|
46
|
-
return true;
|
|
47
|
-
}
|
|
48
|
-
if (highlighted !== nextProps.highlighted) {
|
|
49
|
-
return true;
|
|
50
|
-
}
|
|
51
|
-
if (highlighted && isAutoScrollEnabled !== nextProps.isAutoScrollEnabled) {
|
|
52
|
-
return true;
|
|
53
|
-
}
|
|
54
|
-
if (indexMap !== nextProps.indexMap) {
|
|
55
|
-
return true;
|
|
56
|
-
}
|
|
57
|
-
if (indexMap && nextProps.indexMap && indexMap[activeSearchIndex] !== nextProps.indexMap[nextProps.activeSearchIndex]) {
|
|
58
|
-
return true;
|
|
59
|
-
}
|
|
60
|
-
return false;
|
|
40
|
+
shouldComponentUpdate(nextProps: ExtendedCaptionProps) {
|
|
41
|
+
const {indexMap, highlighted, isAutoScrollEnabled, activeSearchIndex, longerThanHour, caption} = this.props;
|
|
42
|
+
if (longerThanHour !== nextProps.longerThanHour) {
|
|
43
|
+
return true;
|
|
61
44
|
}
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
const keyCode = event.which || event.keyCode;
|
|
65
|
-
if (
|
|
66
|
-
keyCode === KeyboardKeys.Enter ||
|
|
67
|
-
keyCode === KeyboardKeys.Space
|
|
68
|
-
) {
|
|
69
|
-
event.preventDefault();
|
|
70
|
-
this._gotoCurrentTime();
|
|
71
|
-
return;
|
|
72
|
-
}
|
|
45
|
+
if (highlighted !== nextProps.highlighted) {
|
|
46
|
+
return true;
|
|
73
47
|
}
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
48
|
+
if (highlighted && isAutoScrollEnabled !== nextProps.isAutoScrollEnabled) {
|
|
49
|
+
return true;
|
|
50
|
+
}
|
|
51
|
+
if (indexMap !== nextProps.indexMap) {
|
|
52
|
+
return true;
|
|
79
53
|
}
|
|
54
|
+
if (indexMap && nextProps.indexMap && indexMap[activeSearchIndex] !== nextProps.indexMap[nextProps.activeSearchIndex]) {
|
|
55
|
+
return true;
|
|
56
|
+
}
|
|
57
|
+
return false;
|
|
58
|
+
}
|
|
80
59
|
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
60
|
+
private _handleKeyPress = (event: KeyboardEvent) => {
|
|
61
|
+
const keyCode = event.which || event.keyCode;
|
|
62
|
+
if (keyCode === ENTER || keyCode === Space) {
|
|
63
|
+
event.preventDefault();
|
|
64
|
+
this._gotoCurrentTime();
|
|
65
|
+
return;
|
|
86
66
|
}
|
|
67
|
+
};
|
|
87
68
|
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
}
|
|
94
|
-
if (text.length === 0) {
|
|
95
|
-
return null;
|
|
96
|
-
}
|
|
97
|
-
return (
|
|
98
|
-
<span className={styles.captionSpan}>
|
|
99
|
-
{indexMap
|
|
100
|
-
? indexArray.map((el: string, index: number) => {
|
|
101
|
-
const preSelected = index === 0 ? text.substring(0, indexMap[el]) : "";
|
|
102
|
-
const selected = text.substring(
|
|
103
|
-
indexMap[el],
|
|
104
|
-
indexMap[el] + searchLength
|
|
105
|
-
);
|
|
106
|
-
const postSelected = text.substring(
|
|
107
|
-
indexMap[el] + searchLength,
|
|
108
|
-
index - 1 === indexArray.length
|
|
109
|
-
? text.length
|
|
110
|
-
: indexMap[indexArray[index + 1]]
|
|
111
|
-
);
|
|
112
|
-
return (
|
|
113
|
-
<span>
|
|
114
|
-
{preSelected}
|
|
115
|
-
<span
|
|
116
|
-
className={
|
|
117
|
-
Number(el) === activeSearchIndex
|
|
118
|
-
? styles.activeSearch
|
|
119
|
-
: styles.highlightSearch
|
|
120
|
-
}
|
|
121
|
-
>
|
|
122
|
-
{selected}
|
|
123
|
-
</span>
|
|
124
|
-
{postSelected}
|
|
125
|
-
</span>
|
|
126
|
-
);
|
|
127
|
-
})
|
|
128
|
-
: text}
|
|
129
|
-
</span>
|
|
130
|
-
);
|
|
131
|
-
};
|
|
69
|
+
private _handleClick = (event: MouseEvent) => {
|
|
70
|
+
event.stopPropagation();
|
|
71
|
+
this._hotspotRef?.focus();
|
|
72
|
+
this._gotoCurrentTime();
|
|
73
|
+
};
|
|
132
74
|
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
75
|
+
private _gotoCurrentTime = () => {
|
|
76
|
+
const {caption, onClick} = this.props;
|
|
77
|
+
if (caption.text.length) {
|
|
78
|
+
onClick();
|
|
79
|
+
}
|
|
80
|
+
};
|
|
136
81
|
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
>
|
|
146
|
-
{showTime && (
|
|
147
|
-
<div className={styles.captionTime}>
|
|
148
|
-
{secontsToTime(startTime, longerThanHour)}
|
|
149
|
-
</div>
|
|
150
|
-
)}
|
|
151
|
-
<div
|
|
152
|
-
onClick={this._handleClick}
|
|
153
|
-
className={`${styles.captionContent} ${highlighted ? styles.highlighted : ""} ${showTime ? "" : styles.withoutTime}`}
|
|
154
|
-
role="button"
|
|
155
|
-
>
|
|
156
|
-
{this._renderText(text)}
|
|
157
|
-
</div>
|
|
158
|
-
</div>
|
|
159
|
-
);
|
|
82
|
+
private _renderText = (text: string) => {
|
|
83
|
+
const {activeSearchIndex, searchLength, indexMap} = this.props;
|
|
84
|
+
let indexArray: string[] = [];
|
|
85
|
+
if (indexMap) {
|
|
86
|
+
indexArray = Object.keys(indexMap).sort((a, b) => Number(a) - Number(b));
|
|
87
|
+
}
|
|
88
|
+
if (text?.length === 0) {
|
|
89
|
+
return null;
|
|
160
90
|
}
|
|
91
|
+
return (
|
|
92
|
+
<span className={styles.captionSpan}>
|
|
93
|
+
{indexMap
|
|
94
|
+
? indexArray.map((el: string, index: number) => {
|
|
95
|
+
const preSelected = index === 0 ? text.substring(0, indexMap[el]) : '';
|
|
96
|
+
const selected = text.substring(indexMap[el], indexMap[el] + searchLength);
|
|
97
|
+
const postSelected = text.substring(
|
|
98
|
+
indexMap[el] + searchLength,
|
|
99
|
+
index - 1 === indexArray.length ? text.length : indexMap[indexArray[index + 1]]
|
|
100
|
+
);
|
|
101
|
+
return (
|
|
102
|
+
<span>
|
|
103
|
+
{preSelected}
|
|
104
|
+
<span className={Number(el) === activeSearchIndex ? styles.activeSearch : styles.highlightSearch}>{selected}</span>
|
|
105
|
+
{postSelected}
|
|
106
|
+
</span>
|
|
107
|
+
);
|
|
108
|
+
})
|
|
109
|
+
: text}
|
|
110
|
+
</span>
|
|
111
|
+
);
|
|
112
|
+
};
|
|
113
|
+
|
|
114
|
+
render() {
|
|
115
|
+
const {caption, highlighted, showTime, longerThanHour} = this.props;
|
|
116
|
+
const {startTime, id} = caption;
|
|
117
|
+
const isHighlighted = Object.keys(highlighted)[0] === id;
|
|
118
|
+
|
|
119
|
+
return (
|
|
120
|
+
<div
|
|
121
|
+
className={styles.caption}
|
|
122
|
+
tabIndex={1}
|
|
123
|
+
ref={node => {
|
|
124
|
+
this._hotspotRef = node;
|
|
125
|
+
}}
|
|
126
|
+
onKeyDown={this._handleKeyPress}
|
|
127
|
+
>
|
|
128
|
+
{showTime && <div className={styles.captionTime}>{secontsToTime(startTime, longerThanHour)}</div>}
|
|
129
|
+
<div
|
|
130
|
+
onClick={this._handleClick}
|
|
131
|
+
className={`${styles.captionContent} ${isHighlighted ? styles.highlighted : ''} ${showTime ? '' : styles.withoutTime}`}
|
|
132
|
+
role="button"
|
|
133
|
+
>
|
|
134
|
+
{this._renderText(caption.text)}
|
|
135
|
+
</div>
|
|
136
|
+
</div>
|
|
137
|
+
);
|
|
138
|
+
}
|
|
161
139
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export * from
|
|
1
|
+
export * from './caption';
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
.transcript-wrapper {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
}
|
|
2
|
+
position: absolute;
|
|
3
|
+
width: 100%;
|
|
4
|
+
display: flex;
|
|
5
|
+
flex-direction: column;
|
|
6
|
+
align-items: center;
|
|
7
|
+
justify-content: center;
|
|
8
|
+
padding-right: 9px;
|
|
9
|
+
}
|