this.gui 1.0.16 → 1.0.17
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/README.md +191 -23
- package/dist/style.css +1 -1
- package/dist/this-gui.es.js +2777 -1923
- package/dist/this-gui.umd.js +23 -23
- package/package.json +2 -4
- package/src/App.jsx +4 -1
- package/src/Page.jsx +28 -0
- package/src/example.json +43 -0
- package/src/scripts/ComponentRegistry.js +70 -0
- package/src/scripts/postinstall.js +40 -1
- package/src/scripts/renderComponents.js +11 -0
- package/src/stories/Atoms/Badge/Badge.css +1 -0
- package/src/stories/Atoms/Badge/Badge.stories.jsx +1 -0
- package/src/stories/Atoms/Grid/Grid.css +160 -0
- package/src/stories/Atoms/Grid/Grid.jsx +43 -0
- package/src/stories/Atoms/Grid/Grid.stories.jsx +84 -0
- package/src/stories/Atoms/ProgressBar/ProgressBar.css +1 -1
- package/src/stories/Atoms/ProgressBar/ProgressBar.jsx +1 -1
- package/src/stories/Atoms/ProgressBar/ProgressBar.stories.jsx +1 -1
- package/src/stories/Atoms/Section/Section.css +268 -0
- package/src/stories/Atoms/Section/Section.jsx +63 -0
- package/src/stories/Atoms/Section/Section.stories.jsx +46 -0
- package/src/stories/Atoms/TextArea/TextArea.css +1 -0
- package/src/stories/Atoms/TextArea/TextArea.jsx +1 -0
- package/src/stories/Atoms/TextArea/TextArea.stories.jsx +1 -0
- package/src/stories/Atoms/TextInput/TextInput.css +1 -0
- package/src/stories/Atoms/TextInput/TextInput.jsx +1 -0
- package/src/stories/Atoms/TextInput/TextInput.stories.jsx +1 -0
- package/src/stories/Atoms/Toggle/Toggle.css +1 -0
- package/src/stories/Atoms/Toggle/Toggle.jsx +1 -0
- package/src/stories/Atoms/Toggle/Toggle.stories.jsx +1 -0
- package/src/stories/Atoms/Tooltip/Tooltip.css +1 -0
- package/src/stories/Atoms/Tooltip/Tooltip.jsx +1 -0
- package/src/stories/Atoms/Tooltip/Tooltip.stories.jsx +1 -0
- package/src/stories/Atoms/Video/Video.css +1 -0
- package/src/stories/Atoms/Video/Video.jsx +1 -0
- package/src/stories/Atoms/Video/Video.stories.jsx +1 -0
- package/src/stories/Atoms/index.js +4 -0
- package/src/stories/Atoms/meta_Atoms.js +4 -1
- package/src/stories/Layouts/Accordion/Accordion.css +285 -8
- package/src/stories/Layouts/Accordion/Accordion.jsx +62 -19
- package/src/stories/Layouts/Accordion/Accordion.stories.jsx +30 -19
- package/src/stories/Layouts/index.js +0 -6
- package/src/stories/Layouts/meta_Layouts.js +2 -5
- package/src/stories/Molecules/Accordion/Accordion.css +1 -1
- package/src/stories/Molecules/Accordion/Accordion.jsx +1 -1
- package/src/stories/Molecules/Accordion/Accordion.stories.jsx +1 -1
- package/src/stories/Molecules/AudioPlayer/AudioPlayer.css +95 -2
- package/src/stories/Molecules/AudioPlayer/AudioPlayer.jsx +232 -13
- package/src/stories/Molecules/AudioPlayer/AudioPlayer.stories.jsx +46 -11
- package/src/stories/Molecules/AvatarWithName/AvatarWithName.css +128 -2
- package/src/stories/Molecules/AvatarWithName/AvatarWithName.jsx +69 -14
- package/src/stories/Molecules/AvatarWithName/AvatarWithName.stories.jsx +12 -12
- package/src/stories/Molecules/Breadcrumbs/Breadcrumbs.css +145 -2
- package/src/stories/Molecules/Breadcrumbs/Breadcrumbs.jsx +39 -13
- package/src/stories/Molecules/Breadcrumbs/Breadcrumbs.stories.jsx +27 -11
- package/src/stories/Molecules/ButtonGroup/ButtonGroup.css +463 -2
- package/src/stories/Molecules/ButtonGroup/ButtonGroup.jsx +34 -12
- package/src/stories/Molecules/ButtonGroup/ButtonGroup.stories.jsx +36 -12
- package/src/stories/Molecules/Card/Card.css +39 -2
- package/src/stories/Molecules/Card/Card.jsx +80 -13
- package/src/stories/Molecules/Card/Card.stories.jsx +27 -13
- package/src/stories/Molecules/ComparisonTable/ComparisonTable.css +33 -2
- package/src/stories/Molecules/ComparisonTable/ComparisonTable.jsx +91 -12
- package/src/stories/Molecules/ComparisonTable/ComparisonTable.stories.jsx +73 -12
- package/src/stories/Molecules/Dropdown/Dropdown.css +192 -0
- package/src/stories/Molecules/Dropdown/Dropdown.jsx +96 -0
- package/src/stories/Molecules/Dropdown/Dropdown.stories.jsx +45 -0
- package/src/stories/Molecules/index.js +2 -1
- package/src/stories/Molecules/meta_Molecules.js +6 -3
- package/dist/Styles.md +0 -446
- package/dist/context.md +0 -942
- package/src/Theme.jsx +0 -28
- package/src/components/CodeBlock.jsx +0 -22
- package/src/components/ComponentFactory.jsx +0 -36
- package/src/components/ComponentRegistry.js +0 -21
- package/src/scripts/generateComponents.js +0 -166
- package/src/scripts/verifyLayouts.js +0 -175
- package/src/scripts/verifyMolecules.js +0 -158
- package/src/scripts/verifyTemplates.js +0 -154
- package/src/scripts/verify_and_install_atoms.js +0 -211
- package/src/stories/Layouts/DropdownMenu/DropdownMenu.css +0 -16
- package/src/stories/Layouts/DropdownMenu/DropdownMenu.jsx +0 -31
- package/src/stories/Layouts/DropdownMenu/DropdownMenu.stories.jsx +0 -28
- package/src/stories/Layouts/Grid/Grid.css +0 -4
- package/src/stories/Layouts/Grid/Grid.jsx +0 -13
- package/src/stories/Layouts/Grid/Grid.stories.jsx +0 -28
- package/src/stories/Layouts/Section/Section.css +0 -16
- package/src/stories/Layouts/Section/Section.jsx +0 -31
- package/src/stories/Layouts/Section/Section.stories.jsx +0 -28
- /package/src/themes/{README.md → README_Styles.md} +0 -0
|
@@ -1,25 +1,244 @@
|
|
|
1
|
-
|
|
2
|
-
import React from 'react';
|
|
1
|
+
//this.GUI/src/stories/Molecules/AudioPlayer/AudioPlayer.jsx
|
|
2
|
+
import React, { useRef, useState, useEffect } from 'react';
|
|
3
3
|
import PropTypes from 'prop-types';
|
|
4
|
+
import { FaPlay, FaPause, FaVolumeUp, FaVolumeMute, FaStepForward, FaStepBackward } from 'react-icons/fa';
|
|
4
5
|
import './AudioPlayer.css';
|
|
5
6
|
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
7
|
+
export const AudioPlayer = ({
|
|
8
|
+
playlist,
|
|
9
|
+
autoPlay = false,
|
|
10
|
+
loop = false,
|
|
11
|
+
muted = false,
|
|
12
|
+
size = 'medium',
|
|
13
|
+
color = 'classy-color-1',
|
|
14
|
+
className = '',
|
|
15
|
+
style = {},
|
|
16
|
+
showMedia = true,
|
|
17
|
+
...props
|
|
18
|
+
}) => {
|
|
19
|
+
const audioRef = useRef(null);
|
|
20
|
+
const [isPlaying, setIsPlaying] = useState(autoPlay);
|
|
21
|
+
const [isMuted, setIsMuted] = useState(muted);
|
|
22
|
+
const [volume, setVolume] = useState(muted ? 0 : 1);
|
|
23
|
+
const [currentTime, setCurrentTime] = useState(0);
|
|
24
|
+
const [duration, setDuration] = useState(0);
|
|
25
|
+
const [currentTrack, setCurrentTrack] = useState(playlist[0]);
|
|
26
|
+
const [currentTrackIndex, setCurrentTrackIndex] = useState(0);
|
|
27
|
+
|
|
28
|
+
useEffect(() => {
|
|
29
|
+
const audio = audioRef.current;
|
|
30
|
+
if (audio) {
|
|
31
|
+
audio.volume = volume;
|
|
32
|
+
audio.muted = isMuted;
|
|
33
|
+
if (autoPlay) {
|
|
34
|
+
audio.play().catch(() => setIsPlaying(false));
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
}, [volume, isMuted, autoPlay, currentTrack]);
|
|
38
|
+
|
|
39
|
+
const togglePlayPause = () => {
|
|
40
|
+
const audio = audioRef.current;
|
|
41
|
+
if (!audio) return;
|
|
42
|
+
|
|
43
|
+
if (isPlaying) {
|
|
44
|
+
audio.pause();
|
|
45
|
+
} else {
|
|
46
|
+
audio.play().catch(() => setIsPlaying(false));
|
|
47
|
+
}
|
|
48
|
+
setIsPlaying(!isPlaying);
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
const handleTimeUpdate = () => {
|
|
52
|
+
const audio = audioRef.current;
|
|
53
|
+
if (audio) {
|
|
54
|
+
setCurrentTime(audio.currentTime);
|
|
55
|
+
}
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
const handleLoadedMetadata = () => {
|
|
59
|
+
const audio = audioRef.current;
|
|
60
|
+
if (audio) {
|
|
61
|
+
setDuration(audio.duration);
|
|
62
|
+
}
|
|
63
|
+
};
|
|
64
|
+
|
|
65
|
+
const handleVolumeChange = (e) => {
|
|
66
|
+
const newVolume = parseFloat(e.target.value);
|
|
67
|
+
setVolume(newVolume);
|
|
68
|
+
setIsMuted(newVolume === 0);
|
|
69
|
+
};
|
|
70
|
+
|
|
71
|
+
const toggleMute = () => {
|
|
72
|
+
setIsMuted(!isMuted);
|
|
73
|
+
setVolume(!isMuted ? 0 : 1);
|
|
74
|
+
};
|
|
75
|
+
|
|
76
|
+
const handleSeekChange = (e) => {
|
|
77
|
+
const seekTime = parseFloat(e.target.value);
|
|
78
|
+
const audio = audioRef.current;
|
|
79
|
+
if (audio) {
|
|
80
|
+
audio.currentTime = seekTime;
|
|
81
|
+
setCurrentTime(seekTime);
|
|
82
|
+
}
|
|
83
|
+
};
|
|
84
|
+
|
|
85
|
+
const formatTime = (time) => {
|
|
86
|
+
if (isNaN(time)) return '00:00';
|
|
87
|
+
const minutes = Math.floor(time / 60);
|
|
88
|
+
const seconds = Math.floor(time % 60);
|
|
89
|
+
const paddedSeconds = seconds < 10 ? `0${seconds}` : seconds;
|
|
90
|
+
return `${minutes}:${paddedSeconds}`;
|
|
91
|
+
};
|
|
92
|
+
|
|
93
|
+
const nextTrack = () => {
|
|
94
|
+
const nextIndex = (currentTrackIndex + 1) % playlist.length;
|
|
95
|
+
setCurrentTrackIndex(nextIndex);
|
|
96
|
+
setCurrentTrack(playlist[nextIndex]);
|
|
97
|
+
};
|
|
98
|
+
|
|
99
|
+
const prevTrack = () => {
|
|
100
|
+
const prevIndex = (currentTrackIndex - 1 + playlist.length) % playlist.length;
|
|
101
|
+
setCurrentTrackIndex(prevIndex);
|
|
102
|
+
setCurrentTrack(playlist[prevIndex]);
|
|
103
|
+
};
|
|
104
|
+
|
|
105
|
+
const handleTrackClick = (track, index) => {
|
|
106
|
+
setCurrentTrack(track);
|
|
107
|
+
setCurrentTrackIndex(index);
|
|
108
|
+
setIsPlaying(false); // Reset play state to allow clicking a new track.
|
|
109
|
+
};
|
|
110
|
+
|
|
111
|
+
const variantClass = `audio-player--${size}`;
|
|
112
|
+
const colorClass = `audio-player--${color}`;
|
|
113
|
+
const combinedClassName = `audio-player ${variantClass} ${colorClass} ${className}`.trim();
|
|
114
|
+
|
|
10
115
|
return (
|
|
11
|
-
<div className=
|
|
12
|
-
{
|
|
116
|
+
<div className={combinedClassName} style={style} {...props}>
|
|
117
|
+
{showMedia && currentTrack.image && (
|
|
118
|
+
<div className="audio-player__media">
|
|
119
|
+
<img src={currentTrack.image} alt={currentTrack.title} />
|
|
120
|
+
</div>
|
|
121
|
+
)}
|
|
122
|
+
|
|
123
|
+
{/* Display Track Name Above Timeline */}
|
|
124
|
+
<div className="audio-player__track-name">{currentTrack.title}</div>
|
|
125
|
+
|
|
126
|
+
<div className="audio-player__controls">
|
|
127
|
+
<audio
|
|
128
|
+
ref={audioRef}
|
|
129
|
+
src={currentTrack.src}
|
|
130
|
+
loop={loop}
|
|
131
|
+
onTimeUpdate={handleTimeUpdate}
|
|
132
|
+
onLoadedMetadata={handleLoadedMetadata}
|
|
133
|
+
onEnded={nextTrack}
|
|
134
|
+
>
|
|
135
|
+
Your browser does not support the audio element.
|
|
136
|
+
</audio>
|
|
137
|
+
<button
|
|
138
|
+
className="audio-player__prev"
|
|
139
|
+
onClick={prevTrack}
|
|
140
|
+
aria-label="Previous Track"
|
|
141
|
+
>
|
|
142
|
+
<FaStepBackward />
|
|
143
|
+
</button>
|
|
144
|
+
<button
|
|
145
|
+
className="audio-player__play-pause"
|
|
146
|
+
onClick={togglePlayPause}
|
|
147
|
+
aria-label={isPlaying ? 'Pause' : 'Play'}
|
|
148
|
+
>
|
|
149
|
+
{isPlaying ? <FaPause /> : <FaPlay />}
|
|
150
|
+
</button>
|
|
151
|
+
<button
|
|
152
|
+
className="audio-player__next"
|
|
153
|
+
onClick={nextTrack}
|
|
154
|
+
aria-label="Next Track"
|
|
155
|
+
>
|
|
156
|
+
<FaStepForward />
|
|
157
|
+
</button>
|
|
158
|
+
</div>
|
|
159
|
+
|
|
160
|
+
{/* Timeline with Seek and Time */}
|
|
161
|
+
<div className="audio-player__seek">
|
|
162
|
+
<input
|
|
163
|
+
type="range"
|
|
164
|
+
className="audio-player__seek-slider"
|
|
165
|
+
min="0"
|
|
166
|
+
max={duration}
|
|
167
|
+
step="0.1"
|
|
168
|
+
value={currentTime}
|
|
169
|
+
onChange={handleSeekChange}
|
|
170
|
+
aria-label="Seek Slider"
|
|
171
|
+
/>
|
|
172
|
+
<div className="audio-player__time">{formatTime(currentTime)} / {formatTime(duration)}</div>
|
|
173
|
+
</div>
|
|
174
|
+
|
|
175
|
+
<div className="audio-player__volume-container">
|
|
176
|
+
<button
|
|
177
|
+
className="audio-player__mute"
|
|
178
|
+
onClick={toggleMute}
|
|
179
|
+
aria-label={isMuted ? 'Unmute' : 'Mute'}
|
|
180
|
+
>
|
|
181
|
+
{isMuted ? <FaVolumeMute /> : <FaVolumeUp />}
|
|
182
|
+
</button>
|
|
183
|
+
<input
|
|
184
|
+
type="range"
|
|
185
|
+
className="audio-player__volume-slider"
|
|
186
|
+
min="0"
|
|
187
|
+
max="1"
|
|
188
|
+
step="0.01"
|
|
189
|
+
value={isMuted ? 0 : volume}
|
|
190
|
+
onChange={handleVolumeChange}
|
|
191
|
+
aria-label="Volume Slider"
|
|
192
|
+
/>
|
|
193
|
+
</div>
|
|
194
|
+
|
|
195
|
+
{/* Playlist with Border */}
|
|
196
|
+
<div className="audio-player__playlist">
|
|
197
|
+
<ul>
|
|
198
|
+
{playlist.map((track, index) => (
|
|
199
|
+
<li
|
|
200
|
+
key={index}
|
|
201
|
+
className={`audio-player__track ${index === currentTrackIndex ? 'active' : ''}`}
|
|
202
|
+
onClick={() => handleTrackClick(track, index)}
|
|
203
|
+
>
|
|
204
|
+
{track.title}
|
|
205
|
+
</li>
|
|
206
|
+
))}
|
|
207
|
+
</ul>
|
|
208
|
+
</div>
|
|
13
209
|
</div>
|
|
14
210
|
);
|
|
15
211
|
};
|
|
16
212
|
|
|
17
213
|
AudioPlayer.propTypes = {
|
|
18
|
-
|
|
214
|
+
playlist: PropTypes.arrayOf(
|
|
215
|
+
PropTypes.shape({
|
|
216
|
+
src: PropTypes.string.isRequired,
|
|
217
|
+
title: PropTypes.string.isRequired,
|
|
218
|
+
image: PropTypes.string, // Optional album cover or media
|
|
219
|
+
})
|
|
220
|
+
).isRequired,
|
|
221
|
+
autoPlay: PropTypes.bool,
|
|
222
|
+
loop: PropTypes.bool,
|
|
223
|
+
muted: PropTypes.bool,
|
|
224
|
+
size: PropTypes.oneOf(['small', 'medium']),
|
|
225
|
+
color: PropTypes.oneOf([
|
|
226
|
+
'classy-color-1', 'classy-color-2', 'classy-color-3', 'classy-color-4', 'classy-color-5',
|
|
227
|
+
'small-switch-color-1', 'small-switch-color-2',
|
|
228
|
+
'natural-color-1', 'natural-color-2', 'natural-color-3',
|
|
229
|
+
'grey-friend-1', 'grey-friend-2',
|
|
230
|
+
'shade-1', 'shade-2', 'shade-3', 'shade-4',
|
|
231
|
+
]),
|
|
232
|
+
className: PropTypes.string,
|
|
233
|
+
style: PropTypes.object,
|
|
234
|
+
showMedia: PropTypes.bool,
|
|
19
235
|
};
|
|
20
236
|
|
|
21
237
|
AudioPlayer.defaultProps = {
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
238
|
+
autoPlay: false,
|
|
239
|
+
loop: false,
|
|
240
|
+
muted: false,
|
|
241
|
+
size: 'medium',
|
|
242
|
+
color: 'classy-color-1',
|
|
243
|
+
showMedia: true,
|
|
244
|
+
};
|
|
@@ -1,20 +1,55 @@
|
|
|
1
|
-
|
|
1
|
+
//this.GUI/src/stories/Molecules/AudioPlayer/AudioPlayer.stories.jsx
|
|
2
|
+
import React from 'react';
|
|
2
3
|
import { AudioPlayer } from './AudioPlayer';
|
|
4
|
+
import './AudioPlayer.css';
|
|
3
5
|
|
|
4
|
-
// Storybook configuration for AudioPlayer component
|
|
5
6
|
export default {
|
|
6
|
-
title: 'Molecules/
|
|
7
|
+
title: 'Molecules/Media/AudioPlayer',
|
|
7
8
|
component: AudioPlayer,
|
|
8
|
-
parameters: {
|
|
9
|
-
layout: 'centered',
|
|
10
|
-
},
|
|
11
9
|
argTypes: {
|
|
12
|
-
|
|
10
|
+
autoPlay: { control: 'boolean', defaultValue: false },
|
|
11
|
+
loop: { control: 'boolean', defaultValue: false },
|
|
12
|
+
muted: { control: 'boolean', defaultValue: false },
|
|
13
|
+
size: {
|
|
14
|
+
control: 'select',
|
|
15
|
+
options: ['small', 'medium'],
|
|
16
|
+
defaultValue: 'medium',
|
|
17
|
+
},
|
|
18
|
+
color: {
|
|
19
|
+
control: 'select',
|
|
20
|
+
options: [
|
|
21
|
+
'classy-color-1', 'classy-color-2', 'classy-color-3', 'classy-color-4', 'classy-color-5',
|
|
22
|
+
'small-switch-color-1', 'small-switch-color-2',
|
|
23
|
+
'natural-color-1', 'natural-color-2', 'natural-color-3',
|
|
24
|
+
'grey-friend-1', 'grey-friend-2',
|
|
25
|
+
'shade-1', 'shade-2', 'shade-3', 'shade-4',
|
|
26
|
+
],
|
|
27
|
+
defaultValue: 'classy-color-1',
|
|
28
|
+
},
|
|
29
|
+
showMedia: { control: 'boolean', defaultValue: true },
|
|
13
30
|
},
|
|
14
31
|
};
|
|
15
32
|
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
33
|
+
const playlist = [
|
|
34
|
+
{
|
|
35
|
+
src: 'https://www.soundhelix.com/examples/mp3/SoundHelix-Song-1.mp3',
|
|
36
|
+
title: 'Track 1',
|
|
37
|
+
image: 'https://via.placeholder.com/150?text=Track+1',
|
|
19
38
|
},
|
|
20
|
-
|
|
39
|
+
{
|
|
40
|
+
src: 'https://www.soundhelix.com/examples/mp3/SoundHelix-Song-2.mp3',
|
|
41
|
+
title: 'Track 2',
|
|
42
|
+
image: 'https://via.placeholder.com/150?text=Track+2',
|
|
43
|
+
},
|
|
44
|
+
{
|
|
45
|
+
src: 'https://www.soundhelix.com/examples/mp3/SoundHelix-Song-3.mp3',
|
|
46
|
+
title: 'Track 3',
|
|
47
|
+
image: 'https://via.placeholder.com/150?text=Track+3',
|
|
48
|
+
},
|
|
49
|
+
];
|
|
50
|
+
|
|
51
|
+
export const DefaultAudioPlayer = () => <AudioPlayer playlist={playlist} />;
|
|
52
|
+
export const SmallAudioPlayer = () => <AudioPlayer size="small" playlist={playlist} />;
|
|
53
|
+
export const AutoplayAudioPlayer = () => <AudioPlayer autoPlay playlist={playlist} />;
|
|
54
|
+
export const AudioPlayerWithoutMedia = () => <AudioPlayer showMedia={false} playlist={playlist} />;
|
|
55
|
+
export const LoopingAudioPlayer = () => <AudioPlayer loop playlist={playlist} />;
|
|
@@ -1,4 +1,130 @@
|
|
|
1
|
+
/* this.GUI/src/stories/Molecules/AvatarWithName/AvatarWithName.css */
|
|
2
|
+
/* Base Styles */
|
|
3
|
+
.avatar-with-name {
|
|
4
|
+
display: inline-flex; /* This ensures that it behaves like an inline element */
|
|
5
|
+
align-items: center;
|
|
6
|
+
gap: 8px; /* Reduced gap between the avatar and the name */
|
|
7
|
+
cursor: pointer;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
/* Ensure no auto-centering or alignment issues */
|
|
11
|
+
.avatar-with-name--below {
|
|
12
|
+
flex-direction: column; /* Stack the name below the avatar */
|
|
13
|
+
align-items: center; /* Keep the avatar and name centered with respect to each other */
|
|
14
|
+
text-align: center;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
.avatar-with-name--side {
|
|
18
|
+
flex-direction: row; /* Place the name next to the avatar */
|
|
19
|
+
text-align: left; /* Ensure text is aligned correctly when the name is to the side */
|
|
20
|
+
align-items: center; /* Keep the avatar and name aligned vertically */
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/* Size Variants */
|
|
24
|
+
.avatar-with-name--small .avatar-with-name__avatar img,
|
|
25
|
+
.avatar-with-name--small .avatar-with-name__avatar svg {
|
|
26
|
+
width: 40px;
|
|
27
|
+
height: 40px;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
.avatar-with-name--medium .avatar-with-name__avatar img,
|
|
31
|
+
.avatar-with-name--medium .avatar-with-name__avatar svg {
|
|
32
|
+
width: 80px;
|
|
33
|
+
height: 80px;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
.avatar-with-name--large .avatar-with-name__avatar img,
|
|
37
|
+
.avatar-with-name--large .avatar-with-name__avatar svg {
|
|
38
|
+
width: 120px;
|
|
39
|
+
height: 120px;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/* Name styling */
|
|
43
|
+
.avatar-with-name__name {
|
|
44
|
+
font-size: 1.1rem;
|
|
45
|
+
color: var(--text-color, #2C2C2C);
|
|
46
|
+
margin-top: 2px;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/* Avatar styling */
|
|
50
|
+
.avatar-with-name__avatar {
|
|
51
|
+
border-radius: 50%;
|
|
52
|
+
object-fit: cover;
|
|
53
|
+
display: flex;
|
|
54
|
+
align-items: center;
|
|
55
|
+
justify-content: center;
|
|
56
|
+
background-color: #f0f0f0;
|
|
57
|
+
overflow: hidden;
|
|
58
|
+
}
|
|
1
59
|
|
|
2
|
-
.
|
|
3
|
-
|
|
60
|
+
.avatar__default-icon {
|
|
61
|
+
width: 100%;
|
|
62
|
+
height: 100%;
|
|
4
63
|
}
|
|
64
|
+
|
|
65
|
+
/* Modal Styles */
|
|
66
|
+
.avatar-modal {
|
|
67
|
+
position: fixed;
|
|
68
|
+
top: 0;
|
|
69
|
+
left: 0;
|
|
70
|
+
width: 100%;
|
|
71
|
+
height: 100%;
|
|
72
|
+
display: flex;
|
|
73
|
+
align-items: center;
|
|
74
|
+
justify-content: center;
|
|
75
|
+
background-color: rgba(0, 0, 0, 0.5);
|
|
76
|
+
z-index: 1000;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
.avatar-modal__content {
|
|
80
|
+
background-color: white;
|
|
81
|
+
padding: 16px;
|
|
82
|
+
border-radius: 8px;
|
|
83
|
+
text-align: center;
|
|
84
|
+
position: relative;
|
|
85
|
+
max-width: 300px;
|
|
86
|
+
width: 100%;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
.avatar-modal__avatar-container {
|
|
90
|
+
display: flex;
|
|
91
|
+
justify-content: center; /* Center the avatar in the modal */
|
|
92
|
+
align-items: center;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
.avatar-modal__avatar {
|
|
96
|
+
width: 150px;
|
|
97
|
+
height: 150px;
|
|
98
|
+
border-radius: 50%;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
.avatar-modal__name {
|
|
102
|
+
margin-top: 16px;
|
|
103
|
+
font-size: 1.5rem;
|
|
104
|
+
font-weight: bold;
|
|
105
|
+
color: var(--text-color, #2C2C2C);
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
.avatar-modal__close {
|
|
109
|
+
position: absolute;
|
|
110
|
+
top: 10px;
|
|
111
|
+
right: 10px;
|
|
112
|
+
background: none;
|
|
113
|
+
border: none;
|
|
114
|
+
font-size: 1.5rem;
|
|
115
|
+
cursor: pointer;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
/* Responsive Adjustments */
|
|
119
|
+
@media (max-width: 768px) {
|
|
120
|
+
.avatar-with-name--large .avatar-with-name__avatar img,
|
|
121
|
+
.avatar-with-name--large .avatar-with-name__avatar svg {
|
|
122
|
+
width: 100px;
|
|
123
|
+
height: 100px;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
.avatar-modal__avatar {
|
|
127
|
+
width: 120px;
|
|
128
|
+
height: 120px;
|
|
129
|
+
}
|
|
130
|
+
}
|
|
@@ -1,25 +1,80 @@
|
|
|
1
|
-
|
|
2
|
-
import React from 'react';
|
|
1
|
+
//this.GUI/src/stories/Molecules/AvatarWithName/AvatarWithName.jsx
|
|
2
|
+
import React, { useState } from 'react';
|
|
3
3
|
import PropTypes from 'prop-types';
|
|
4
4
|
import './AvatarWithName.css';
|
|
5
5
|
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
6
|
+
const defaultAvatar = (
|
|
7
|
+
<svg
|
|
8
|
+
className="avatar__default-icon"
|
|
9
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
10
|
+
viewBox="0 0 24 24"
|
|
11
|
+
fill="none"
|
|
12
|
+
stroke="currentColor"
|
|
13
|
+
strokeWidth="2"
|
|
14
|
+
strokeLinecap="round"
|
|
15
|
+
strokeLinejoin="round"
|
|
16
|
+
>
|
|
17
|
+
<circle cx="12" cy="12" r="10"></circle>
|
|
18
|
+
<path d="M14.5 9a2.5 2.5 0 1 1-5 0 2.5 2.5 0 0 1 5 0z"></path>
|
|
19
|
+
<path d="M12 14c-3.03 0-5.47 1.21-6 3h12c-.53-1.79-2.97-3-6-3z"></path>
|
|
20
|
+
</svg>
|
|
21
|
+
);
|
|
22
|
+
|
|
23
|
+
export const AvatarWithName = ({ imageSrc, name, namePosition, size, onClick }) => {
|
|
24
|
+
const [showModal, setShowModal] = useState(false);
|
|
25
|
+
|
|
26
|
+
const handleModalToggle = () => {
|
|
27
|
+
setShowModal(!showModal);
|
|
28
|
+
};
|
|
29
|
+
|
|
10
30
|
return (
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
31
|
+
<>
|
|
32
|
+
<div
|
|
33
|
+
className={`avatar-with-name avatar-with-name--${namePosition} avatar-with-name--${size}`}
|
|
34
|
+
onClick={handleModalToggle}
|
|
35
|
+
>
|
|
36
|
+
<div className="avatar-with-name__avatar">
|
|
37
|
+
{imageSrc ? (
|
|
38
|
+
<img src={imageSrc} alt={name} />
|
|
39
|
+
) : (
|
|
40
|
+
defaultAvatar /* Display the default avatar if no imageSrc is provided */
|
|
41
|
+
)}
|
|
42
|
+
</div>
|
|
43
|
+
<div className="avatar-with-name__name">{name}</div>
|
|
44
|
+
</div>
|
|
45
|
+
|
|
46
|
+
{showModal && (
|
|
47
|
+
<div className="avatar-modal" onClick={handleModalToggle}>
|
|
48
|
+
<div className="avatar-modal__content" onClick={(e) => e.stopPropagation()}>
|
|
49
|
+
<button className="avatar-modal__close" onClick={handleModalToggle}>
|
|
50
|
+
×
|
|
51
|
+
</button>
|
|
52
|
+
<div className="avatar-modal__avatar-container">
|
|
53
|
+
<div className="avatar-modal__avatar">
|
|
54
|
+
{imageSrc ? (
|
|
55
|
+
<img src={imageSrc} alt={name} />
|
|
56
|
+
) : (
|
|
57
|
+
defaultAvatar /* Use the default avatar in the modal if no imageSrc is provided */
|
|
58
|
+
)}
|
|
59
|
+
</div>
|
|
60
|
+
</div>
|
|
61
|
+
<div className="avatar-modal__name">{name}</div>
|
|
62
|
+
</div>
|
|
63
|
+
</div>
|
|
64
|
+
)}
|
|
65
|
+
</>
|
|
14
66
|
);
|
|
15
67
|
};
|
|
16
68
|
|
|
17
69
|
AvatarWithName.propTypes = {
|
|
18
|
-
//
|
|
70
|
+
imageSrc: PropTypes.string, // Image URL for the avatar
|
|
71
|
+
name: PropTypes.string.isRequired, // The name to display
|
|
72
|
+
namePosition: PropTypes.oneOf(['below', 'side']), // Position of the name relative to the avatar
|
|
73
|
+
size: PropTypes.oneOf(['small', 'medium', 'large']), // Size of the avatar
|
|
74
|
+
onClick: PropTypes.func, // Function to execute on click
|
|
19
75
|
};
|
|
20
76
|
|
|
21
77
|
AvatarWithName.defaultProps = {
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
export default AvatarWithName;
|
|
78
|
+
namePosition: 'below',
|
|
79
|
+
size: 'medium',
|
|
80
|
+
};
|
|
@@ -1,20 +1,20 @@
|
|
|
1
|
-
|
|
1
|
+
//this.GUI/src/stories/Molecules/AvatarWithName/AvatarWithName.stories.jsx
|
|
2
|
+
import React from 'react';
|
|
2
3
|
import { AvatarWithName } from './AvatarWithName';
|
|
3
4
|
|
|
4
|
-
// Storybook configuration for AvatarWithName component
|
|
5
5
|
export default {
|
|
6
|
-
title: 'Molecules/
|
|
6
|
+
title: 'Molecules/Display/AvatarWithName',
|
|
7
7
|
component: AvatarWithName,
|
|
8
|
-
parameters: {
|
|
9
|
-
layout: 'centered',
|
|
10
|
-
},
|
|
11
8
|
argTypes: {
|
|
12
|
-
|
|
9
|
+
imageSrc: { control: 'text', defaultValue: 'https://via.placeholder.com/150' },
|
|
10
|
+
name: { control: 'text', defaultValue: 'John Doe' },
|
|
11
|
+
namePosition: { control: 'select', options: ['below', 'side'], defaultValue: 'below' },
|
|
12
|
+
size: { control: 'select', options: ['small', 'medium', 'large'], defaultValue: 'medium' },
|
|
13
13
|
},
|
|
14
14
|
};
|
|
15
15
|
|
|
16
|
-
export const Default = {
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
}
|
|
16
|
+
export const Default = (args) => <AvatarWithName {...args} />;
|
|
17
|
+
|
|
18
|
+
export const SideName = (args) => <AvatarWithName {...args} namePosition="side" />;
|
|
19
|
+
export const LargeAvatar = (args) => <AvatarWithName {...args} size="large" />;
|
|
20
|
+
export const SmallAvatar = (args) => <AvatarWithName {...args} size="small" />;
|