├── .github └── workflows │ └── gitee-sync.yml ├── .gitignore ├── .npmignore ├── .storybook ├── customize-theme.js ├── main.js ├── manager.js └── preview.tsx ├── Jenkinsfile_bitbucket.groovy ├── README.md ├── ci └── build │ ├── build_mac.groovy │ └── build_mac.sh ├── package.json ├── postcss.config.js ├── src ├── generated │ └── .gitkeep ├── infra │ ├── api │ │ ├── index.tsx │ │ ├── lock.ts │ │ ├── polyfills.ts │ │ ├── providers.tsx │ │ ├── rtc-extensions │ │ │ └── index.ts │ │ └── type.ts │ ├── capabilities │ │ ├── config.ts │ │ ├── containers │ │ │ ├── action-sheet-mobile │ │ │ │ ├── hands-up.mobile.tsx │ │ │ │ ├── index.mobile.css │ │ │ │ ├── mic.tsx │ │ │ │ └── share.mobile.tsx │ │ │ ├── aside │ │ │ │ └── index.tsx │ │ │ ├── award │ │ │ │ └── index.tsx │ │ │ ├── camera-preview │ │ │ │ └── index.tsx │ │ │ ├── cloud-driver │ │ │ │ ├── cloud-driver.tsx │ │ │ │ ├── cloud-help.tsx │ │ │ │ ├── cloud-minimize.tsx │ │ │ │ ├── cloud-more-menu.tsx │ │ │ │ ├── cloud-toolbar.tsx │ │ │ │ ├── index.css │ │ │ │ ├── index.tsx │ │ │ │ ├── person-resource.tsx │ │ │ │ └── public-resource.tsx │ │ │ ├── device-setting │ │ │ │ ├── index.css │ │ │ │ └── index.tsx │ │ │ ├── dialog │ │ │ │ ├── assets │ │ │ │ │ └── screen-share │ │ │ │ │ │ ├── student.png │ │ │ │ │ │ └── teacher.png │ │ │ │ ├── breakout-room │ │ │ │ │ ├── confirm-dialog.tsx │ │ │ │ │ ├── confirm-panel.tsx │ │ │ │ │ ├── fragments │ │ │ │ │ │ ├── group-select.tsx │ │ │ │ │ │ ├── index.css │ │ │ │ │ │ └── start.tsx │ │ │ │ │ ├── group.tsx │ │ │ │ │ ├── index.css │ │ │ │ │ ├── index.tsx │ │ │ │ │ ├── panel.tsx │ │ │ │ │ └── user.tsx │ │ │ │ ├── confirm.tsx │ │ │ │ ├── error-generic.tsx │ │ │ │ ├── index.tsx │ │ │ │ ├── kick-out.tsx │ │ │ │ ├── quit.tsx │ │ │ │ ├── remote-control-confirm │ │ │ │ │ ├── index.css │ │ │ │ │ └── index.tsx │ │ │ │ ├── screen-picker │ │ │ │ │ ├── index.css │ │ │ │ │ └── index.tsx │ │ │ │ ├── screen-share │ │ │ │ │ ├── index.css │ │ │ │ │ └── index.tsx │ │ │ │ └── video-gallery │ │ │ │ │ ├── hooks.ts │ │ │ │ │ ├── index.css │ │ │ │ │ ├── index.tsx │ │ │ │ │ ├── pager.css │ │ │ │ │ ├── pager.tsx │ │ │ │ │ ├── setting.css │ │ │ │ │ └── setting.tsx │ │ │ ├── fragments │ │ │ │ └── video-gallery │ │ │ │ │ ├── context.ts │ │ │ │ │ ├── hooks.ts │ │ │ │ │ ├── index.tsx │ │ │ │ │ └── renderer.tsx │ │ │ ├── hand-up │ │ │ │ ├── assets │ │ │ │ │ ├── gift.svg │ │ │ │ │ └── search.svg │ │ │ │ ├── index.css │ │ │ │ ├── index.tsx │ │ │ │ ├── invite-confirm.css │ │ │ │ ├── invite-confirm.tsx │ │ │ │ ├── invite-container.css │ │ │ │ ├── invite-container.tsx │ │ │ │ ├── invite-table.css │ │ │ │ ├── invite-table.tsx │ │ │ │ ├── manager.tsx │ │ │ │ ├── sender.tsx │ │ │ │ └── types.ts │ │ │ ├── loading │ │ │ │ ├── index.css │ │ │ │ └── index.tsx │ │ │ ├── nav │ │ │ │ ├── assets │ │ │ │ │ └── svga │ │ │ │ │ │ └── record-loading.svga │ │ │ │ ├── index.css │ │ │ │ └── index.tsx │ │ │ ├── pretest │ │ │ │ ├── assets │ │ │ │ │ ├── modal-bg.svg │ │ │ │ │ └── pretest-audio.mp3 │ │ │ │ ├── form-field │ │ │ │ │ ├── index.tsx │ │ │ │ │ ├── select.tsx │ │ │ │ │ └── style.css │ │ │ │ ├── index.css │ │ │ │ ├── index.tsx │ │ │ │ ├── pretest-audio.tsx │ │ │ │ ├── pretest-footer.tsx │ │ │ │ ├── pretest-stage.tsx │ │ │ │ ├── pretest-video.tsx │ │ │ │ └── volume.tsx │ │ │ ├── root-box │ │ │ │ ├── fixed-aspect-ratio.tsx │ │ │ │ ├── hooks.ts │ │ │ │ └── index.tsx │ │ │ ├── roster │ │ │ │ ├── button.tsx │ │ │ │ ├── index.tsx │ │ │ │ ├── lecture-user-list.tsx │ │ │ │ └── user-list.tsx │ │ │ ├── scene-switch │ │ │ │ ├── index.css │ │ │ │ └── index.tsx │ │ │ ├── scenes-controller │ │ │ │ ├── index.css │ │ │ │ └── index.tsx │ │ │ ├── screen-share │ │ │ │ ├── index.css │ │ │ │ ├── index.mobile.tsx │ │ │ │ └── index.tsx │ │ │ ├── stream-window │ │ │ │ ├── index.css │ │ │ │ └── index.tsx │ │ │ ├── stream │ │ │ │ ├── assets │ │ │ │ │ ├── audio │ │ │ │ │ │ └── reward.mp3 │ │ │ │ │ ├── cdn-placeholder.svg │ │ │ │ │ ├── room-placholder-bg.jpg │ │ │ │ │ └── svga │ │ │ │ │ │ ├── hands-up.svga │ │ │ │ │ │ ├── reward.svga │ │ │ │ │ │ ├── video-loadings.gif │ │ │ │ │ │ ├── video-play.svg │ │ │ │ │ │ └── wave-arm.gif │ │ │ │ ├── cdn-player.tsx │ │ │ │ ├── draggable-stream.tsx │ │ │ │ ├── index.css │ │ │ │ ├── index.mobile.css │ │ │ │ ├── index.mobile.tsx │ │ │ │ ├── index.tsx │ │ │ │ ├── room-1v1-player.tsx │ │ │ │ ├── room-big-class-player.mobile.tsx │ │ │ │ ├── room-big-player.tsx │ │ │ │ ├── room-mid-player.tsx │ │ │ │ ├── stream-tool.tsx │ │ │ │ ├── track-player.tsx │ │ │ │ └── video-live-player.tsx │ │ │ ├── toast │ │ │ │ ├── index.css │ │ │ │ ├── index.mobile.css │ │ │ │ ├── index.mobile.tsx │ │ │ │ └── index.tsx │ │ │ ├── toolbar │ │ │ │ ├── board-cleaners │ │ │ │ │ └── index.tsx │ │ │ │ ├── index.tsx │ │ │ │ ├── pens │ │ │ │ │ └── index.tsx │ │ │ │ ├── slice │ │ │ │ │ └── index.tsx │ │ │ │ └── tool-cabinet │ │ │ │ │ └── index.tsx │ │ │ └── widget │ │ │ │ ├── index.css │ │ │ │ ├── index.mobile.tsx │ │ │ │ ├── index.tsx │ │ │ │ ├── slots.tsx │ │ │ │ └── track.tsx │ │ └── scenarios │ │ │ ├── 1v1 │ │ │ └── index.tsx │ │ │ ├── big-class-mobile │ │ │ ├── after-class.png │ │ │ ├── generic-error.png │ │ │ ├── index.mobile.css │ │ │ └── index.mobile.tsx │ │ │ ├── big-class │ │ │ └── index.tsx │ │ │ ├── index.tsx │ │ │ ├── mid-class │ │ │ └── index.tsx │ │ │ └── room │ │ │ └── index.tsx │ ├── configs │ │ ├── base-theme.ts │ │ └── base-ui.ts │ ├── contexts │ │ ├── index.tsx │ │ └── ui-store-factory.ts │ ├── hooks │ │ ├── cabinet.ts │ │ ├── index.ts │ │ ├── ui-store.ts │ │ └── utilites.ts │ ├── protocol │ │ ├── board.ts │ │ ├── events.ts │ │ ├── index.ts │ │ └── type.ts │ ├── stores │ │ ├── common │ │ │ ├── base.ts │ │ │ ├── board │ │ │ │ └── index.ts │ │ │ ├── cloud-drive │ │ │ │ ├── helper.ts │ │ │ │ ├── index.ts │ │ │ │ ├── struct.ts │ │ │ │ └── type.ts │ │ │ ├── device-setting │ │ │ │ └── index.ts │ │ │ ├── getters.ts │ │ │ ├── group │ │ │ │ └── index.ts │ │ │ ├── hand-up │ │ │ │ ├── index.ts │ │ │ │ └── type.ts │ │ │ ├── index.ts │ │ │ ├── layout │ │ │ │ ├── helper.ts │ │ │ │ └── index.ts │ │ │ ├── nav │ │ │ │ └── index.ts │ │ │ ├── notification │ │ │ │ └── index.ts │ │ │ ├── pretest │ │ │ │ ├── helper.ts │ │ │ │ ├── index.ts │ │ │ │ └── type.ts │ │ │ ├── roster │ │ │ │ ├── index.ts │ │ │ │ └── type.ts │ │ │ ├── share │ │ │ │ └── index.ts │ │ │ ├── stream-window │ │ │ │ ├── helper.ts │ │ │ │ ├── index.ts │ │ │ │ ├── struct.ts │ │ │ │ └── type.ts │ │ │ ├── stream │ │ │ │ ├── index.ts │ │ │ │ ├── state-keeper.ts │ │ │ │ ├── struct.ts │ │ │ │ └── tool.ts │ │ │ ├── subscription │ │ │ │ ├── abstract.ts │ │ │ │ ├── index.ts │ │ │ │ ├── main-room.ts │ │ │ │ └── type.ts │ │ │ ├── toolbar │ │ │ │ ├── index.ts │ │ │ │ └── type.ts │ │ │ ├── type.ts │ │ │ ├── video-gallery │ │ │ │ └── index.ts │ │ │ └── widget │ │ │ │ └── index.ts │ │ ├── interactive │ │ │ ├── board.ts │ │ │ ├── index.ts │ │ │ └── stream.ts │ │ ├── lecture-mobile │ │ │ ├── board.ts │ │ │ ├── device-setting.ts │ │ │ ├── index.ts │ │ │ ├── layout.ts │ │ │ └── stream.ts │ │ ├── lecture │ │ │ ├── board.ts │ │ │ ├── index.ts │ │ │ ├── roster.ts │ │ │ ├── stream.ts │ │ │ └── toolbar.ts │ │ └── one-on-one │ │ │ ├── index.ts │ │ │ ├── stream.ts │ │ │ └── toolbar.ts │ ├── translate │ │ ├── en.ts │ │ └── zh.ts │ └── utils │ │ ├── async-queue.ts │ │ ├── board-utils.ts │ │ ├── colors.ts │ │ ├── config-loader.ts │ │ ├── error.ts │ │ ├── event-center.ts │ │ ├── extract.ts │ │ ├── index.ts │ │ ├── interaction.ts │ │ ├── ipc-channels.ts │ │ └── ipc.ts ├── react-app-env.d.ts └── ui-kit │ ├── components │ ├── button │ │ ├── abutton.css │ │ ├── abutton.tsx │ │ ├── index.css │ │ ├── index.stories.tsx │ │ └── index.tsx │ ├── card │ │ ├── index.css │ │ ├── index.stories.tsx │ │ └── index.tsx │ ├── checkbox │ │ ├── index.css │ │ ├── index.stories.tsx │ │ └── index.tsx │ ├── float │ │ └── index.tsx │ ├── index.ts │ ├── input-number │ │ ├── index.css │ │ └── index.tsx │ ├── input │ │ ├── ainput.css │ │ ├── ainput.tsx │ │ ├── index.css │ │ ├── index.stories.tsx │ │ └── index.tsx │ ├── layout │ │ ├── index.css │ │ └── index.tsx │ ├── loading │ │ ├── assets │ │ │ ├── circle-loading.gif │ │ │ └── loading.gif │ │ ├── index.css │ │ ├── index.stories.tsx │ │ └── index.tsx │ ├── modal │ │ ├── index.css │ │ ├── index.stories.tsx │ │ └── index.tsx │ ├── overlay-wrap │ │ ├── index.css │ │ └── index.tsx │ ├── pagination │ │ ├── index.css │ │ └── index.tsx │ ├── placeholder │ │ ├── assets │ │ │ ├── board-disconnected.png │ │ │ ├── camera-broken.png │ │ │ ├── camera-close.png │ │ │ ├── camera-disabled.png │ │ │ ├── empty-history.png │ │ │ ├── no-body.png │ │ │ ├── no-file.png │ │ │ └── noquestion.svg │ │ ├── index.css │ │ ├── index.stories.tsx │ │ └── index.tsx │ ├── popover │ │ ├── index.css │ │ ├── index.stories.tsx │ │ └── index.tsx │ ├── progress │ │ ├── index.css │ │ ├── index.stories.tsx │ │ └── index.tsx │ ├── radio │ │ └── index.tsx │ ├── root-box │ │ ├── index.css │ │ └── index.tsx │ ├── roster │ │ ├── assets │ │ │ └── loading.gif │ │ ├── carousel-setting.tsx │ │ ├── columns.tsx │ │ ├── hooks.tsx │ │ ├── index.css │ │ ├── index.tsx │ │ ├── roster.tsx │ │ └── table.tsx │ ├── select │ │ ├── assets │ │ │ └── arrow-icon.svg │ │ ├── index.css │ │ ├── index.stories.tsx │ │ └── index.tsx │ ├── slider │ │ ├── index.css │ │ └── index.tsx │ ├── sound-player │ │ └── index.tsx │ ├── svg-img │ │ ├── generate.ts │ │ ├── index.css │ │ ├── index.stories.tsx │ │ ├── index.tsx │ │ ├── paths │ │ │ ├── add-scene.tsx │ │ │ ├── add.tsx │ │ │ ├── alf.tsx │ │ │ ├── answer.tsx │ │ │ ├── arrow.tsx │ │ │ ├── ask-for-help.tsx │ │ │ ├── audio.tsx │ │ │ ├── authorized-solid.tsx │ │ │ ├── authorized.tsx │ │ │ ├── auto-play-failed.tsx │ │ │ ├── backward.tsx │ │ │ ├── bad-signal.tsx │ │ │ ├── beta.tsx │ │ │ ├── board-not-granted.tsx │ │ │ ├── buffing.tsx │ │ │ ├── calendar.tsx │ │ │ ├── camera-disabled.tsx │ │ │ ├── camera-enabled.tsx │ │ │ ├── camera-off-mobile.tsx │ │ │ ├── camera-on-mobile.tsx │ │ │ ├── camera.tsx │ │ │ ├── chat.tsx │ │ │ ├── checked.tsx │ │ │ ├── chevron-right.tsx │ │ │ ├── circle.tsx │ │ │ ├── clear.tsx │ │ │ ├── clicker.tsx │ │ │ ├── clock.tsx │ │ │ ├── close.tsx │ │ │ ├── cloud-file-help.tsx │ │ │ ├── cloud-more.tsx │ │ │ ├── cloud-refresh.tsx │ │ │ ├── cloud.tsx │ │ │ ├── collapse-stream-mobile.tsx │ │ │ ├── copy.tsx │ │ │ ├── countdown.tsx │ │ │ ├── delete.tsx │ │ │ ├── down.tsx │ │ │ ├── dropdown.tsx │ │ │ ├── edit.tsx │ │ │ ├── emoji.tsx │ │ │ ├── eraser.tsx │ │ │ ├── excel.tsx │ │ │ ├── exit.tsx │ │ │ ├── extension-actived.tsx │ │ │ ├── fcr-ppt-broken.tsx │ │ │ ├── forward.tsx │ │ │ ├── fullscreen-shrink.tsx │ │ │ ├── fullscreen.tsx │ │ │ ├── gooffstage.tsx │ │ │ ├── goonstage.tsx │ │ │ ├── group-discuss.tsx │ │ │ ├── h5.tsx │ │ │ ├── hand.tsx │ │ │ ├── hands-up-active.tsx │ │ │ ├── hands-up.tsx │ │ │ ├── id.tsx │ │ │ ├── image.tsx │ │ │ ├── indicator.tsx │ │ │ ├── invite-on-podium.tsx │ │ │ ├── invite-to-podium.tsx │ │ │ ├── kick-out.tsx │ │ │ ├── landscape.tsx │ │ │ ├── laser-pointer.tsx │ │ │ ├── line.tsx │ │ │ ├── link-solid.tsx │ │ │ ├── link.tsx │ │ │ ├── log.tsx │ │ │ ├── m-camera-off.tsx │ │ │ ├── m-camera.tsx │ │ │ ├── m-micphone-off.tsx │ │ │ ├── m-micphone.tsx │ │ │ ├── m-switch-camera.tsx │ │ │ ├── mark.tsx │ │ │ ├── matrix.tsx │ │ │ ├── max.tsx │ │ │ ├── maximize.tsx │ │ │ ├── menu-fold.tsx │ │ │ ├── menu-unfold.tsx │ │ │ ├── mic-disabled.tsx │ │ │ ├── mic-enabled.tsx │ │ │ ├── microphone-off-outline.tsx │ │ │ ├── microphone-off.tsx │ │ │ ├── microphone-on-outline.tsx │ │ │ ├── microphone-on.tsx │ │ │ ├── microphone.tsx │ │ │ ├── min.tsx │ │ │ ├── minimize.tsx │ │ │ ├── more.tsx │ │ │ ├── mute-mobile.tsx │ │ │ ├── no-authorized.tsx │ │ │ ├── no-discussion.tsx │ │ │ ├── none.tsx │ │ │ ├── normal-signal.tsx │ │ │ ├── on-podium.tsx │ │ │ ├── out.tsx │ │ │ ├── pdf.tsx │ │ │ ├── pen-arrow.tsx │ │ │ ├── pen-circle.tsx │ │ │ ├── pen-curve.tsx │ │ │ ├── pen-line.tsx │ │ │ ├── pen-pentagram.tsx │ │ │ ├── pen-rhombus.tsx │ │ │ ├── pen-square.tsx │ │ │ ├── pen-triangle.tsx │ │ │ ├── pen.tsx │ │ │ ├── pentagram.tsx │ │ │ ├── pic.tsx │ │ │ ├── pip-off.tsx │ │ │ ├── pip-on.tsx │ │ │ ├── placeholder-camera-broken.tsx │ │ │ ├── placeholder-camera-no-body.tsx │ │ │ ├── placeholder-camera-off.tsx │ │ │ ├── placeholder-no-ask.tsx │ │ │ ├── placeholder-no-file.tsx │ │ │ ├── placeholder-no-message.tsx │ │ │ ├── placeholder-no-search.tsx │ │ │ ├── placeholder-no-setup.tsx │ │ │ ├── placeholder-not-present.tsx │ │ │ ├── ppt.tsx │ │ │ ├── pretest-check.tsx │ │ │ ├── pretest-checked.tsx │ │ │ ├── pretest-speaker.tsx │ │ │ ├── record.tsx │ │ │ ├── recording.tsx │ │ │ ├── red-caution.tsx │ │ │ ├── redo.tsx │ │ │ ├── register.tsx │ │ │ ├── replay.tsx │ │ │ ├── reset.tsx │ │ │ ├── restore.tsx │ │ │ ├── review.tsx │ │ │ ├── reward.tsx │ │ │ ├── rhombus.tsx │ │ │ ├── room-label.tsx │ │ │ ├── ruddy.tsx │ │ │ ├── save-ghost.tsx │ │ │ ├── search.tsx │ │ │ ├── select.tsx │ │ │ ├── set-outline.tsx │ │ │ ├── set.tsx │ │ │ ├── settings.tsx │ │ │ ├── share-close.tsx │ │ │ ├── share-default.tsx │ │ │ ├── share-hover.tsx │ │ │ ├── share-mobile.tsx │ │ │ ├── share-screen.tsx │ │ │ ├── slice-window.tsx │ │ │ ├── slice.tsx │ │ │ ├── speaker.tsx │ │ │ ├── square.tsx │ │ │ ├── stage.tsx │ │ │ ├── star-outline.tsx │ │ │ ├── star.tsx │ │ │ ├── stream-window-on.tsx │ │ │ ├── switch-screen-share-active.tsx │ │ │ ├── switch-screen-share.tsx │ │ │ ├── text.tsx │ │ │ ├── toast-info.tsx │ │ │ ├── toast-success.tsx │ │ │ ├── tools.tsx │ │ │ ├── triangle-down.tsx │ │ │ ├── triangle-solid-down.tsx │ │ │ ├── triangle-solid-right.tsx │ │ │ ├── triangle-solid-up.tsx │ │ │ ├── triangle.tsx │ │ │ ├── txt.tsx │ │ │ ├── undo.tsx │ │ │ ├── unknown-signal.tsx │ │ │ ├── unknown.tsx │ │ │ ├── unmute-mobile.tsx │ │ │ ├── video-gallery.tsx │ │ │ ├── video-switch-mobile.tsx │ │ │ ├── video.tsx │ │ │ ├── vote.tsx │ │ │ ├── whiteboard.tsx │ │ │ ├── whitening.tsx │ │ │ ├── word.tsx │ │ │ ├── zoom-in.tsx │ │ │ └── zoom-out.tsx │ │ ├── svg-dict.tsx │ │ └── type.ts │ ├── svga-player │ │ ├── assets │ │ │ ├── audio │ │ │ │ └── reward.mp3 │ │ │ ├── star.gif │ │ │ └── svga │ │ │ │ ├── hands-up.svga │ │ │ │ └── reward.svga │ │ ├── index.tsx │ │ └── svga-types.ts │ ├── table │ │ ├── index.css │ │ ├── index.stories.tsx │ │ └── index.tsx │ ├── tabs │ │ ├── index.css │ │ └── index.tsx │ ├── toast │ │ ├── index.css │ │ ├── index.stories.tsx │ │ └── index.tsx │ ├── toolbar │ │ ├── assets │ │ │ ├── close-default.svg │ │ │ ├── close-hover.svg │ │ │ ├── icon-close-hover.svg │ │ │ ├── icon-close.svg │ │ │ ├── icon-open-hover.svg │ │ │ ├── icon-open.svg │ │ │ ├── open-default.svg │ │ │ └── open-hover.svg │ │ ├── board-cleaners.tsx │ │ ├── index.css │ │ ├── index.tsx │ │ ├── pens.tsx │ │ ├── slice.tsx │ │ ├── tool-cabinet.tsx │ │ ├── tool.tsx │ │ └── util.ts │ ├── tooltip │ │ ├── index.css │ │ ├── index.tsx │ │ └── placements.ts │ ├── tree │ │ ├── index.tsx │ │ └── style.css │ ├── util │ │ ├── colors.ts │ │ ├── getRenderPropValue.ts │ │ ├── motion.ts │ │ └── type.ts │ └── volume │ │ ├── index.css │ │ ├── index.stories.tsx │ │ └── index.tsx │ ├── index.ts │ ├── styles │ ├── global.css │ └── scenario.css │ └── utilities │ ├── hooks.ts │ ├── index.ts │ ├── state-color.ts │ ├── style-config.ts │ └── types.ts ├── tailwind.config.js ├── tsconfig.json ├── typedoc.json └── webpack.config.js /.github/workflows/gitee-sync.yml: -------------------------------------------------------------------------------- 1 | name: gitee-sync 2 | on: 3 | pull_request: 4 | workflow_dispatch: 5 | 6 | jobs: 7 | build: 8 | name: gitee-sync 9 | runs-on: ubuntu-latest 10 | 11 | concurrency: 12 | group: ${{ github.workflow }}-${{ github.ref }} 13 | cancel-in-progress: true 14 | if: github.actor != 'dependabot[bot]' 15 | steps: 16 | - name: Gitee sync repo 17 | uses: Yikun/hub-mirror-action@v1.3 18 | with: 19 | src: github/AgoraIO-Community 20 | dst: gitee/agoraio-community 21 | white_list: "CloudClass-Desktop" 22 | static_list: "CloudClass-Desktop" 23 | cache_path: "./cache" 24 | dst_key: ${{ secrets.GITEE_PI_SSH }} 25 | dst_token: ${{ secrets.GITEE_PRIVATE_TOKEN }} 26 | force_update: true 27 | account_type: org -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | .env 3 | .env-dev 4 | .env.dev 5 | .env.prod 6 | .DS_Store 7 | release 8 | build 9 | dist 10 | *.txt 11 | es 12 | lib 13 | *.zip 14 | .eslintcache 15 | packages/agora-log-sdk 16 | package-lock.json 17 | yarn.lock 18 | *.log 19 | .vscode/ 20 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | # folders 2 | examples/ 3 | packages/ 4 | public/ 5 | src/ 6 | .storybook/ 7 | app/ 8 | release/ 9 | main_land/ 10 | build/ 11 | scripts/ 12 | icons/ 13 | dist/ 14 | node_modules/ 15 | guides/ 16 | ci/ 17 | 18 | # files 19 | rollup.config.js 20 | ts.base.json 21 | ts.release.json 22 | tsconfig.json 23 | typedoc.json 24 | tailwind.config.js 25 | postcss.config.js 26 | edu-demo-tsconfig.json 27 | *.json 28 | 29 | *.sh 30 | *.sh~ 31 | .eslintcache 32 | .eslintrc.js 33 | .npmrc 34 | .env 35 | .env.* 36 | env.example 37 | config-overrides.js 38 | webpack 39 | entitlements.mac.plist 40 | *.map 41 | *.*~agoraAddonlog.txt 42 | docs 43 | Jenkinsfile_bitbucket.groovy 44 | webpack.config.js -------------------------------------------------------------------------------- /.storybook/customize-theme.js: -------------------------------------------------------------------------------- 1 | import { create } from '@storybook/theming'; 2 | 3 | export const theme = create({ 4 | base: 'light', 5 | brandTitle: 'CloudClass UI Preview', 6 | // brandUrl: 'https://github.com/agoraio-community/CloudClass-Desktop.git', 7 | fontBase: "'helvetica neue', 'arial', 'PingFangSC', 'microsoft yahei'", 8 | fontCode: 'microsoft yahei', 9 | }); 10 | -------------------------------------------------------------------------------- /.storybook/main.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | 3 | module.exports = { 4 | typescript: { 5 | reactDocgen: 'react-docgen', 6 | }, 7 | stories: ['../src/ui-kit/components/**/*.stories.@(ts|tsx)'], 8 | addons: [ 9 | '@storybook/addon-links', 10 | '@storybook/addon-essentials', 11 | { 12 | name: '@storybook/addon-postcss', 13 | options: { 14 | postcssLoaderOptions: { 15 | // When using postCSS 8 16 | implementation: require('postcss'), 17 | }, 18 | }, 19 | }, 20 | ], 21 | webpackFinal: async (config) => { 22 | config.resolve.extensions.push('.ts', '.tsx'); 23 | config.resolve.alias = { 24 | ...config.resolve.alias, 25 | 'agora-common-libs': path.resolve(__dirname, '../../../node_modules/agora-common-libs/lib'), 26 | }; 27 | 28 | config.module.rules.push({ 29 | test: /agora-common-libs\/.*\/(annotation)|(widget)|(agora-rte-sdk)/, 30 | use: { loader: 'null-loader' }, 31 | }); 32 | 33 | return config; 34 | }, 35 | }; 36 | -------------------------------------------------------------------------------- /.storybook/manager.js: -------------------------------------------------------------------------------- 1 | import { addons } from '@storybook/addons'; 2 | import { theme } from './customize-theme'; 3 | 4 | addons.setConfig({ 5 | theme: theme, 6 | }); 7 | -------------------------------------------------------------------------------- /Jenkinsfile_bitbucket.groovy: -------------------------------------------------------------------------------- 1 | 2 | @Library('agora-build-pipeline-library') _ 3 | 4 | pipelineLoad(this, "ClassroomSDK", "workflow", "", "", "cloudclass-desktop") 5 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ### agora-classroom-sdk 2 | 3 | For publishing npm package agora-classroom-sdk (Web & Electron). Get more information from https://docs.agora.io 4 | 5 | ### Requirements 6 | 7 | - TypeScript/JavaScript 8 | - React 9 | - Agora Developer Account 10 | - Chrome Web Browser / Electron Environment 11 | 12 | ### Platform Support 13 | 14 | - > = 66.0 Chrome Browser 15 | - > = Electron 7.1.2 16 | 17 | ### Question / Issue Report 18 | 19 | - [https://github.com/AgoraIO-Community/CloudClass-Desktop/issues/new](https://github.com/AgoraIO-Community/CloudClass-Desktop/issues/new) 20 | 21 | - [www.agora.io](https://www.agoar.io) 22 | -------------------------------------------------------------------------------- /ci/build/build_mac.sh: -------------------------------------------------------------------------------- 1 | source_root=$(pwd) 2 | ci_source_root=../apaas-cicd-web 3 | build_branch=$cloudclass_desktop_branch 4 | 5 | ci_script_version=v1 6 | 7 | . ../apaas-cicd-web/versions.sh 8 | . ../apaas-cicd-web/utilities/tools.sh 9 | . ../apaas-cicd-web/build/$ci_script_version/dependency.sh 10 | . ../apaas-cicd-web/build/$ci_script_version/build.sh 11 | 12 | # pick up agora-rte-sdk agora-edu-core agora-common-libs 13 | lib_dependencies=(${lib_dependencies[@]:0:3}) 14 | lib_versions=(${lib_versions[@]:0:3}) 15 | lib_branches=(${lib_branches[@]:0:3}) 16 | 17 | if [ "$debug" == "true" ]; then 18 | # show environment variables 19 | echo "------------- variables --------------------" 20 | set 21 | echo "--------------------------------------------" 22 | fi 23 | 24 | download_packages $source_root $build_branch "${lib_dependencies[*]}" "${lib_versions[*]}" "${lib_branches[*]}" 25 | 26 | make_monorepo $source_root 27 | 28 | install_packages $source_root 29 | 30 | build_lib $source_root $ci_source_root agora-classroom-sdk $build_branch 31 | -------------------------------------------------------------------------------- /postcss.config.js: -------------------------------------------------------------------------------- 1 | // eslint-disable-next-line @typescript-eslint/no-var-requires 2 | const autoprefixer = require('autoprefixer'); 3 | // eslint-disable-next-line @typescript-eslint/no-var-requires 4 | const tailwindcss = require('tailwindcss'); 5 | // eslint-disable-next-line @typescript-eslint/no-var-requires 6 | const tailwindConfig = require('./tailwind.config'); 7 | const postcssPxToViewport = require('agora-common-libs/presets/postcss-plugin/px-to-vw/index.js'); 8 | 9 | module.exports = { 10 | plugins: [ 11 | autoprefixer(), 12 | tailwindcss(tailwindConfig), 13 | postcssPxToViewport({ 14 | viewportWidth: 375, 15 | unitPrecision: 5, 16 | viewportUnit: 'vw', 17 | fontViewportUnit: 'vw', 18 | include: [/\/mobile\//, /\.mobile\./], 19 | exclude: [/\/node_modules\//i], 20 | landscape: true, // 是否处理横屏情况 21 | landscapeUnit: 'vw', // (String) 横屏时使用的单位 22 | landscapeWidth: 812, // (Number) 横屏时使用的视口宽度 23 | landscapeHeight: 375, // (Number) 横屏时使用的视口宽度 24 | }), 25 | ], 26 | }; 27 | -------------------------------------------------------------------------------- /src/generated/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AgoraIO-Community/CloudClass-Desktop/41651e4ae972718c8b5ac7674e97673f1a5a14b3/src/generated/.gitkeep -------------------------------------------------------------------------------- /src/infra/api/lock.ts: -------------------------------------------------------------------------------- 1 | declare global { 2 | interface Window { 3 | __agora__fcr__locked: boolean; 4 | } 5 | } 6 | export const lock = () => { 7 | window.__agora__fcr__locked = true; 8 | }; 9 | 10 | export const unlock = () => { 11 | window.__agora__fcr__locked = false; 12 | }; 13 | 14 | export const isLocked = () => { 15 | return !!window.__agora__fcr__locked; 16 | }; 17 | -------------------------------------------------------------------------------- /src/infra/api/polyfills.ts: -------------------------------------------------------------------------------- 1 | //@ts-ignore 2 | require('matchmedia-polyfill'); 3 | require('matchmedia-polyfill/matchMedia.addListener'); 4 | require('promise-polyfill/src/polyfill'); -------------------------------------------------------------------------------- /src/infra/api/providers.tsx: -------------------------------------------------------------------------------- 1 | import { FC, PropsWithChildren } from 'react'; 2 | import { ThemeProvider, FcrTheme, FcrUIConfig, UIConfigProvider } from 'agora-common-libs'; 3 | import { I18nProvider } from 'agora-common-libs'; 4 | 5 | export const Providers: FC< 6 | PropsWithChildren<{ language: string; uiConfig: FcrUIConfig; theme: FcrTheme }> 7 | > = ({ children, language, uiConfig, theme }) => { 8 | return ( 9 | 10 | 11 | {children} 12 | 13 | 14 | ); 15 | }; 16 | -------------------------------------------------------------------------------- /src/infra/capabilities/config.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * 教学区组件层级规则控制 3 | * -------- 顶层 --------- 4 | * 教师工具 5 | * 视频容器(摄像头/屏幕共享) 6 | * 在线课件(webview/youtube) 7 | * 白板 8 | * -------- 底层 --------- 9 | */ 10 | export enum ComponentLevelRules { 11 | WhiteBoard, 12 | ScreenShare, 13 | OnlineCourseware, 14 | StreamWindow, 15 | TeachTools, 16 | } 17 | export enum ComponentLevelRulesMobile { 18 | Level1 = 1, 19 | Level2 = 10, 20 | Level3 = 20, 21 | } 22 | -------------------------------------------------------------------------------- /src/infra/capabilities/containers/aside/index.tsx: -------------------------------------------------------------------------------- 1 | import { useStore } from '@classroom/infra/hooks/ui-store'; 2 | import { LectureRoomStreamUIStore } from '@classroom/infra/stores/lecture/stream'; 3 | import { OneToOneStreamUIStore } from '@classroom/infra/stores/one-on-one/stream'; 4 | import { observer } from 'mobx-react'; 5 | import { FC, PropsWithChildren } from 'react'; 6 | import { Aside } from '@classroom/ui-kit'; 7 | 8 | export const BigClassAside: FC = observer(({ children }) => { 9 | const { streamUIStore } = useStore(); 10 | return ( 11 | 15 | ); 16 | }); 17 | 18 | export const OneToOneClassAside: FC = observer(({ children }) => { 19 | const { streamUIStore } = useStore(); 20 | return ( 21 | 24 | ); 25 | }); 26 | -------------------------------------------------------------------------------- /src/infra/capabilities/containers/award/index.tsx: -------------------------------------------------------------------------------- 1 | import { useStore } from '@classroom/infra/hooks/ui-store'; 2 | import { observer } from 'mobx-react'; 3 | import { SvgaPlayer, SoundPlayer } from '@classroom/ui-kit'; 4 | 5 | import RewardSVGA from '../stream/assets/svga/reward.svga'; 6 | import RewardSound from '../stream/assets/audio/reward.mp3'; 7 | 8 | export const Award = observer(() => { 9 | const { 10 | layoutUIStore: { awardAnims, removeAward }, 11 | } = useStore(); 12 | 13 | return ( 14 |
15 | {awardAnims.map((anim) => { 16 | return ( 17 | { 22 | removeAward(anim.id); 23 | }}> 24 | ); 25 | })} 26 | {awardAnims.map((anim) => { 27 | return ; 28 | })} 29 |
30 | ); 31 | }); 32 | export default Award; 33 | -------------------------------------------------------------------------------- /src/infra/capabilities/containers/camera-preview/index.tsx: -------------------------------------------------------------------------------- 1 | import { useStore } from '@classroom/infra/hooks/ui-store'; 2 | import classNames from 'classnames'; 3 | import { observer } from 'mobx-react'; 4 | import { StreamPlayerCameraPlaceholder } from '../stream'; 5 | import { LocalTrackPlayer } from '../stream/track-player'; 6 | 7 | export const CameraPreview = observer(() => { 8 | const { videoGalleryUIStore, streamUIStore } = useStore(); 9 | const { localCameraStream, localPreview } = videoGalleryUIStore; 10 | 11 | const cls = classNames('fcr-w-full fcr-h-full fcr-overflow-hidden', { 12 | 'fcr-invisible': localCameraStream ? localCameraStream.isCameraMuted : false, 13 | }); 14 | 15 | return localPreview && localCameraStream ? ( 16 |
25 | 26 | {!streamUIStore.settingsOpened && } 27 |
28 | ) : null; 29 | }); 30 | 31 | export default CameraPreview; 32 | -------------------------------------------------------------------------------- /src/infra/capabilities/containers/cloud-driver/cloud-more-menu.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { transI18n } from 'agora-common-libs'; 3 | 4 | export type CloudMoreMenuProps = { 5 | resourceUuid: string; 6 | deleteResource: (uuid: string) => void; 7 | }; 8 | 9 | export default function CloudMoreMenu({ resourceUuid, deleteResource }: CloudMoreMenuProps) { 10 | return ( 11 |
12 |
{ 15 | deleteResource(resourceUuid); 16 | }}> 17 | {transI18n('cloud.delete')} 18 |
19 |
20 | ); 21 | } 22 | -------------------------------------------------------------------------------- /src/infra/capabilities/containers/cloud-driver/index.tsx: -------------------------------------------------------------------------------- 1 | import { observer } from 'mobx-react'; 2 | import { useState } from 'react'; 3 | import { ActiveKeyEnum, CloudDriver } from './cloud-driver'; 4 | 5 | export type CloudDriverContainerProps = { 6 | onClose?: () => void; 7 | onDelete?: (fileName: string) => void; 8 | }; 9 | 10 | export const CloudDriverContainer: React.FC = observer(({ onClose }) => { 11 | const [activeKey, setActiveKey] = useState(ActiveKeyEnum.public); 12 | 13 | const handleChange = (key: string) => { 14 | setActiveKey(key as ActiveKeyEnum); 15 | }; 16 | 17 | return ; 18 | }); 19 | -------------------------------------------------------------------------------- /src/infra/capabilities/containers/device-setting/index.tsx: -------------------------------------------------------------------------------- 1 | import { observer } from 'mobx-react'; 2 | import { useStore } from '@classroom/infra/hooks/ui-store'; 3 | import './index.css'; 4 | import { RoomPretest } from '../pretest'; 5 | import { useEffect } from 'react'; 6 | 7 | export const RoomDeviceSettingContainer = observer(({ id }: { id: string }) => { 8 | const { 9 | shareUIStore: { removeDialog }, 10 | streamUIStore: { setSettingsOpened }, 11 | deviceSettingUIStore: { deviceStage }, 12 | } = useStore(); 13 | 14 | useEffect(() => { 15 | setSettingsOpened(true); 16 | return () => { 17 | setSettingsOpened(false); 18 | }; 19 | }, []); 20 | 21 | return ( 22 | removeDialog(id)} 26 | onCancel={() => removeDialog(id)} 27 | /> 28 | ); 29 | }); 30 | -------------------------------------------------------------------------------- /src/infra/capabilities/containers/dialog/assets/screen-share/student.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AgoraIO-Community/CloudClass-Desktop/41651e4ae972718c8b5ac7674e97673f1a5a14b3/src/infra/capabilities/containers/dialog/assets/screen-share/student.png -------------------------------------------------------------------------------- /src/infra/capabilities/containers/dialog/assets/screen-share/teacher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AgoraIO-Community/CloudClass-Desktop/41651e4ae972718c8b5ac7674e97673f1a5a14b3/src/infra/capabilities/containers/dialog/assets/screen-share/teacher.png -------------------------------------------------------------------------------- /src/infra/capabilities/containers/dialog/error-generic.tsx: -------------------------------------------------------------------------------- 1 | import { FC } from 'react'; 2 | import { Button, Modal } from '@classroom/ui-kit'; 3 | import { BaseDialogProps } from '.'; 4 | 5 | export const GenericErrorDialog: FC< 6 | BaseDialogProps & { 7 | onOK: () => void; 8 | okBtnText: string; 9 | title: string; 10 | content: string; 11 | error: Error; 12 | } 13 | > = ({ onOK, title, content, okBtnText }) => { 14 | return ( 15 | 19 | {okBtnText} 20 | , 21 | ]} 22 | title={title}> 23 | {content} 24 | 25 | ); 26 | }; 27 | -------------------------------------------------------------------------------- /src/infra/capabilities/containers/dialog/remote-control-confirm/index.css: -------------------------------------------------------------------------------- 1 | .remote-control-confirm-container { 2 | width: 100%; 3 | height: 100%; 4 | background: rgba(0, 0, 0, 0.4); 5 | position: relative; 6 | } 7 | .remote-control-confirm-container .remote-control-confirm-content { 8 | position: absolute; 9 | top: 350px; 10 | left: 0; 11 | right: 0; 12 | margin: 0 auto; 13 | display: flex; 14 | flex-direction: column; 15 | align-items: center; 16 | } 17 | .remote-control-confirm-container .remote-control-confirm-content-text { 18 | font-size: 14px; 19 | color: #fff; 20 | padding-top: 20px; 21 | } 22 | -------------------------------------------------------------------------------- /src/infra/capabilities/containers/dialog/remote-control-confirm/index.tsx: -------------------------------------------------------------------------------- 1 | import { Button } from '@classroom/ui-kit'; 2 | import { useI18n } from 'agora-common-libs'; 3 | import './index.css'; 4 | interface IPropsTypes { 5 | onOK: () => void; 6 | } 7 | export const RemoteControlConfirm = (props: IPropsTypes) => { 8 | const t = useI18n(); 9 | return ( 10 |
11 |
12 |
13 | 16 |
17 |
18 | {t('fcr_share_teacher_requesting_share')} 19 |
20 |
21 |
22 | ); 23 | }; 24 | -------------------------------------------------------------------------------- /src/infra/capabilities/containers/dialog/video-gallery/pager.css: -------------------------------------------------------------------------------- 1 | .fcr-video-grid-pager { 2 | } 3 | 4 | .fcr-video-grid-pager__prev, 5 | .fcr-video-grid-pager__next { 6 | /* background: rgba(32, 32, 32, 0.5); */ 7 | backdrop-filter: blur(25px); 8 | border-radius: 14px; 9 | position: absolute; 10 | top: 50%; 11 | transform: translateY(-25%); 12 | display: flex; 13 | flex-direction: column; 14 | align-items: center; 15 | padding-bottom: 5px; 16 | z-index: 110; 17 | @apply fcr-bg-foreground; 18 | } 19 | 20 | .fcr-video-grid-pager__prev { 21 | left: 20px; 22 | } 23 | 24 | .fcr-video-grid-pager__next { 25 | right: 20px; 26 | } 27 | 28 | .fcr-video-grid-pager__button { 29 | backdrop-filter: blur(25px); 30 | border-radius: 14px; 31 | /* background: rgba(47, 47, 47, 0.95); */ 32 | width: 40px; 33 | height: 40px; 34 | display: flex; 35 | justify-content: center; 36 | align-items: center; 37 | cursor: pointer; 38 | @apply fcr-bg-background; 39 | } 40 | .fcr-video-grid-pager__button:hover { 41 | @apply fcr-bg-brand; 42 | } 43 | 44 | .fcr-video-grid-pager__label { 45 | font-size: 12px; 46 | @apply fcr-text-level1; 47 | } 48 | -------------------------------------------------------------------------------- /src/infra/capabilities/containers/fragments/video-gallery/context.ts: -------------------------------------------------------------------------------- 1 | import AgoraRtcEngine from 'agora-electron-sdk/types/Api'; 2 | import { createContext } from 'react'; 3 | 4 | export const RtcEngineContext = createContext<{ 5 | rtcEngine?: AgoraRtcEngine; 6 | }>({}); 7 | -------------------------------------------------------------------------------- /src/infra/capabilities/containers/hand-up/invite-confirm.css: -------------------------------------------------------------------------------- 1 | .inviteConfirm .content { 2 | text-align: center; 3 | font-size: 13px; 4 | font-weight: 400; 5 | @apply fcr-text-level2; 6 | line-height: 18px; 7 | width: 208px; 8 | margin: 0 auto; 9 | padding: 4px 0; 10 | } 11 | 12 | .inviteConfirm.h5 { 13 | width: 270px; 14 | border-radius: 12px; 15 | background-color: #fff; 16 | box-shadow: 0px 0px 2000px 2000px rgb(0 0 0 / 24%); 17 | } 18 | 19 | .inviteConfirm.h5 .title { 20 | line-height: 3; 21 | text-align: center; 22 | font-size: 17px; 23 | padding-top: 5px; 24 | } 25 | 26 | .inviteConfirm.h5 .content { 27 | padding-bottom: 26px; 28 | } 29 | 30 | .inviteConfirm.h5 .footer { 31 | display: flex; 32 | border-top: 1px solid rgba(0, 0, 0, 0.12); 33 | } 34 | 35 | .inviteConfirm.h5 .footer .button { 36 | width: 50%; 37 | width: 50%; 38 | font-size: 17px; 39 | text-align: center; 40 | line-height: 2.7; 41 | } 42 | 43 | .inviteConfirm.h5 .footer .button:hover { 44 | color: #357bf6; 45 | } 46 | 47 | .inviteConfirm.h5 .footer .button:first-child { 48 | border-right: 1px solid rgba(0, 0, 0, 0.12); 49 | } 50 | 51 | .inviteConfirm.h5 .footer .button.disable, 52 | .inviteConfirm.h5 .footer .button.disable :hover { 53 | color: rgb(187, 187, 187); 54 | pointer-events: none; 55 | cursor: not-allowed; 56 | } 57 | -------------------------------------------------------------------------------- /src/infra/capabilities/containers/hand-up/invite-container.css: -------------------------------------------------------------------------------- 1 | .inviteContainer { 2 | @apply fcr-bg-component; 3 | box-shadow: 0px 2px 6px 0px rgba(47, 65, 146, 0.15); 4 | border-radius: 6px; 5 | position: relative; 6 | overflow-y: hidden; 7 | width: 400px; 8 | /* color: #191919; */ 9 | } 10 | .inviteContainer .inviteTitle { 11 | @apply fcr-bg-icon-selected-color fcr-text-level1; 12 | position: relative; 13 | display: inline-flex; 14 | align-items: center; 15 | padding: 12px; 16 | font-size: 14px; 17 | border: 0; 18 | outline: 0; 19 | width: 100%; 20 | padding-left: 19px; 21 | } 22 | -------------------------------------------------------------------------------- /src/infra/capabilities/containers/hand-up/types.ts: -------------------------------------------------------------------------------- 1 | import { BaseProps } from '@classroom/ui-kit/components/util/type'; 2 | 3 | export interface BaseWaveArmProps extends BaseProps { 4 | width?: number; 5 | height?: number; 6 | borderRadius?: number; 7 | isH5?: boolean; 8 | isOnPodium?: boolean; 9 | onOffPodium?: () => void; 10 | } 11 | 12 | export type UserWaveArmInfo = { 13 | userUuid: string; 14 | userName: string; 15 | onPodium: boolean; 16 | }; 17 | -------------------------------------------------------------------------------- /src/infra/capabilities/containers/loading/index.css: -------------------------------------------------------------------------------- 1 | .page-loading { 2 | display: flex; 3 | justify-content: center; 4 | align-items: center; 5 | flex: 1; 6 | height: 100%; 7 | position: fixed; 8 | width: 100%; 9 | z-index: 999999; 10 | left: 0; 11 | top: 0; 12 | } 13 | .card-loading-position { 14 | position: relative; 15 | } 16 | .page-loading-text { 17 | @apply fcr-text-level1; 18 | position: absolute; 19 | bottom: -50px; 20 | white-space: nowrap; 21 | } 22 | -------------------------------------------------------------------------------- /src/infra/capabilities/containers/loading/index.tsx: -------------------------------------------------------------------------------- 1 | import { observer } from 'mobx-react'; 2 | import { Card, Loading } from '@classroom/ui-kit'; 3 | import { useStore } from '@classroom/infra/hooks/ui-store'; 4 | import './index.css'; 5 | 6 | export const LoadingContainer = observer(() => { 7 | const { layoutUIStore } = useStore(); 8 | const { loading, loadingText } = layoutUIStore; 9 | return loading ? : null; 10 | }); 11 | 12 | const PageLoading = (props: { loadingText?: string }) => { 13 | const { loadingText } = props; 14 | return ( 15 |
16 | 17 | 18 | {loadingText &&

{loadingText}

} 19 |
20 |
21 | ); 22 | }; 23 | -------------------------------------------------------------------------------- /src/infra/capabilities/containers/nav/assets/svga/record-loading.svga: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AgoraIO-Community/CloudClass-Desktop/41651e4ae972718c8b5ac7674e97673f1a5a14b3/src/infra/capabilities/containers/nav/assets/svga/record-loading.svga -------------------------------------------------------------------------------- /src/infra/capabilities/containers/pretest/assets/modal-bg.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /src/infra/capabilities/containers/pretest/assets/pretest-audio.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AgoraIO-Community/CloudClass-Desktop/41651e4ae972718c8b5ac7674e97673f1a5a14b3/src/infra/capabilities/containers/pretest/assets/pretest-audio.mp3 -------------------------------------------------------------------------------- /src/infra/capabilities/containers/pretest/volume.tsx: -------------------------------------------------------------------------------- 1 | import { FC, useMemo } from 'react'; 2 | 3 | interface VolumeProps { 4 | maxLength?: number; 5 | cursor: number; 6 | peek: number; 7 | } 8 | 9 | export const Volume: FC = ({ maxLength = 12, cursor, peek = 12 }) => { 10 | const activityIndex = useMemo(() => { 11 | const percentage = cursor / peek; 12 | return Math.round(maxLength * percentage); 13 | }, [maxLength, peek, cursor]); 14 | 15 | return ( 16 |
21 | {Array(maxLength) 22 | .fill('agora') 23 | .map((item, index) => ( 24 |
= index + 1 ? 1 : 0.1})`, 30 | transition: 'all 0.3s ease-in' 31 | }} 32 | key={`${item}-${index}`} 33 | /> 34 | ))} 35 |
36 | ); 37 | }; 38 | 39 | -------------------------------------------------------------------------------- /src/infra/capabilities/containers/root-box/index.tsx: -------------------------------------------------------------------------------- 1 | export { FixedAspectRatioRootBox, TrackArea } from './fixed-aspect-ratio'; 2 | -------------------------------------------------------------------------------- /src/infra/capabilities/containers/roster/index.tsx: -------------------------------------------------------------------------------- 1 | export { MidRosterBtn, BigRosterBtn } from './button'; 2 | 3 | export { RosterContainer } from './user-list'; 4 | -------------------------------------------------------------------------------- /src/infra/capabilities/containers/roster/user-list.tsx: -------------------------------------------------------------------------------- 1 | import { FC } from 'react'; 2 | import { observer } from 'mobx-react'; 3 | import { useStore } from '@classroom/infra/hooks/ui-store'; 4 | import { Roster, RosterTable } from '@classroom/ui-kit'; 5 | 6 | export type RosterContainerProps = { 7 | onClose: () => void; 8 | }; 9 | 10 | export const RosterContainer: FC = observer(({ onClose }) => { 11 | const { rosterUIStore } = useStore(); 12 | const { 13 | teacherName, 14 | searchKeyword, 15 | setSearchKeyword, 16 | rosterFunctions: functions, 17 | carouselProps, 18 | uiOverrides, 19 | } = rosterUIStore; 20 | 21 | const { width } = uiOverrides; 22 | return ( 23 | 32 | 33 | 34 | ); 35 | }); 36 | 37 | const RosterTableContainer: FC = observer(() => { 38 | const { rosterUIStore } = useStore(); 39 | const { rosterFunctions: functions, userList, clickRowAction } = rosterUIStore; 40 | 41 | return ; 42 | }); 43 | -------------------------------------------------------------------------------- /src/infra/capabilities/containers/scene-switch/index.css: -------------------------------------------------------------------------------- 1 | .scene-switch-loading { 2 | @apply fcr-bg-background; 3 | display: flex; 4 | justify-content: center; 5 | align-items: center; 6 | flex: 1; 7 | height: 100%; 8 | position: fixed; 9 | width: 100%; 10 | z-index: 999999; 11 | left: 0; 12 | top: 0; 13 | } 14 | -------------------------------------------------------------------------------- /src/infra/capabilities/containers/scenes-controller/index.css: -------------------------------------------------------------------------------- 1 | .scenes-controller-container { 2 | position: absolute; 3 | min-width: 170px; 4 | height: 34px !important; 5 | left: 10px; 6 | bottom: 18px; 7 | z-index: 2; 8 | width: auto !important; 9 | border-radius: 20px; 10 | @apply fcr-bg-component; 11 | } 12 | 13 | .scenes-controller-line { 14 | width: 1px; 15 | height: 16px; 16 | border-right: 1px solid #e5e5f0; 17 | margin-left: 6px; 18 | margin-right: 9px; 19 | } 20 | 21 | .scenes-controller-btn-list { 22 | display: flex; 23 | width: 100%; 24 | height: 100%; 25 | box-sizing: border-box; 26 | align-items: center; 27 | padding: 0 10px 0 16px; 28 | } 29 | 30 | .scenes-controller-info { 31 | @apply fcr-text-level2; 32 | font-size: 14px; 33 | user-select: none; 34 | margin: 0 6px; 35 | } 36 | -------------------------------------------------------------------------------- /src/infra/capabilities/containers/screen-share/index.css: -------------------------------------------------------------------------------- 1 | .remote-screen-share-container { 2 | position: absolute; 3 | width: 100%; 4 | } 5 | 6 | .local-screen-share-container { 7 | display: flex; 8 | justify-content: center; 9 | position: absolute; 10 | width: 100%; 11 | top: 5px; 12 | z-index: 2; 13 | pointer-events: none; 14 | } 15 | 16 | .local-screen-share-container .stop-button { 17 | font-size: 12px; 18 | width: 100%; 19 | height: 100%; 20 | display: flex; 21 | border-radius: 5px; 22 | color: #357bf6; 23 | background: white; 24 | box-shadow: 0px 2px 8px 0px rgb(47 65 146 / 15%); 25 | align-items: center; 26 | justify-content: center; 27 | outline: none; 28 | pointer-events: all; 29 | } 30 | 31 | .local-screen-share-container .stop-button:hover { 32 | color: white; 33 | background: #357bf6; 34 | } 35 | 36 | .local-screen-share-container .stop-button > span { 37 | display: flex; 38 | margin-left: 2px; 39 | } 40 | -------------------------------------------------------------------------------- /src/infra/capabilities/containers/screen-share/index.mobile.tsx: -------------------------------------------------------------------------------- 1 | import { useLectureH5UIStores } from '@classroom/infra/hooks/ui-store'; 2 | import classnames from 'classnames'; 3 | import { observer } from 'mobx-react'; 4 | import './index.css'; 5 | import { ScreenShareRemoteTrackPlayer } from '.'; 6 | 7 | export const ScreenShareContainerMobile = observer(() => { 8 | const { 9 | boardUIStore: { boardContainerHeight }, 10 | streamUIStore: { screenShareStream }, 11 | } = useLectureH5UIStores(); 12 | 13 | const remotecls = classnames('remote-screen-share-container', 'fcr-absolute', 'fcr-top-0'); 14 | 15 | return screenShareStream ? ( 16 |
17 | 18 |
19 | ) : null; 20 | }); 21 | -------------------------------------------------------------------------------- /src/infra/capabilities/containers/stream-window/index.css: -------------------------------------------------------------------------------- 1 | .stream-window { 2 | pointer-events: all; 3 | } 4 | -------------------------------------------------------------------------------- /src/infra/capabilities/containers/stream/assets/audio/reward.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AgoraIO-Community/CloudClass-Desktop/41651e4ae972718c8b5ac7674e97673f1a5a14b3/src/infra/capabilities/containers/stream/assets/audio/reward.mp3 -------------------------------------------------------------------------------- /src/infra/capabilities/containers/stream/assets/room-placholder-bg.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AgoraIO-Community/CloudClass-Desktop/41651e4ae972718c8b5ac7674e97673f1a5a14b3/src/infra/capabilities/containers/stream/assets/room-placholder-bg.jpg -------------------------------------------------------------------------------- /src/infra/capabilities/containers/stream/assets/svga/hands-up.svga: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AgoraIO-Community/CloudClass-Desktop/41651e4ae972718c8b5ac7674e97673f1a5a14b3/src/infra/capabilities/containers/stream/assets/svga/hands-up.svga -------------------------------------------------------------------------------- /src/infra/capabilities/containers/stream/assets/svga/reward.svga: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AgoraIO-Community/CloudClass-Desktop/41651e4ae972718c8b5ac7674e97673f1a5a14b3/src/infra/capabilities/containers/stream/assets/svga/reward.svga -------------------------------------------------------------------------------- /src/infra/capabilities/containers/stream/assets/svga/video-loadings.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AgoraIO-Community/CloudClass-Desktop/41651e4ae972718c8b5ac7674e97673f1a5a14b3/src/infra/capabilities/containers/stream/assets/svga/video-loadings.gif -------------------------------------------------------------------------------- /src/infra/capabilities/containers/stream/assets/svga/video-play.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 开始 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /src/infra/capabilities/containers/stream/assets/svga/wave-arm.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AgoraIO-Community/CloudClass-Desktop/41651e4ae972718c8b5ac7674e97673f1a5a14b3/src/infra/capabilities/containers/stream/assets/svga/wave-arm.gif -------------------------------------------------------------------------------- /src/infra/capabilities/containers/toast/index.css: -------------------------------------------------------------------------------- 1 | .toast-animation-exit { 2 | opacity: 0; 3 | } 4 | -------------------------------------------------------------------------------- /src/infra/capabilities/containers/toast/index.mobile.css: -------------------------------------------------------------------------------- 1 | .fcr-mobile-toast-container { 2 | position: absolute; 3 | 4 | bottom: 130px; 5 | left: 0; 6 | right: 0; 7 | margin: auto; 8 | transition: all 0.2s; 9 | pointer-events: none; 10 | text-align: center; 11 | } 12 | .fcr-mobile-toast-container.fcr-mobile-toast-container-landscape { 13 | bottom: 60px; 14 | } 15 | .fcr-mobile-toast-container > div { 16 | padding: 0 14px; 17 | font-weight: 500; 18 | font-size: 14px; 19 | box-shadow: 0px 0px 20px -8px rgba(0, 0, 0, 0.14); 20 | border-radius: 50px; 21 | min-height: 36px; 22 | color: #fff; 23 | display: inline-flex; 24 | align-items: center; 25 | } 26 | -------------------------------------------------------------------------------- /src/infra/capabilities/containers/toast/index.mobile.tsx: -------------------------------------------------------------------------------- 1 | import { useStore } from '@classroom/infra/hooks/ui-store'; 2 | import { Scheduler } from 'agora-rte-sdk'; 3 | import { observer } from 'mobx-react'; 4 | import { useEffect, useRef, useState } from 'react'; 5 | import { ComponentLevelRulesMobile } from '../../config'; 6 | import './index.mobile.css'; 7 | export const ToastContainerMobile = observer(() => { 8 | const { 9 | shareUIStore: { toastQueue, isLandscape }, 10 | } = useStore(); 11 | const currToast = toastQueue[Math.max(toastQueue.length - 1, 0)]; 12 | 13 | const [visible, setVisible] = useState(false); 14 | const delayTaskRef = useRef(null); 15 | useEffect(() => { 16 | if (currToast) { 17 | delayTaskRef.current?.stop(); 18 | setVisible(true); 19 | delayTaskRef.current = Scheduler.shared.addDelayTask(() => { 20 | setVisible(false); 21 | }, 3000); 22 | } 23 | }, [toastQueue, currToast]); 24 | return ( 25 |
33 |
34 | {currToast?.desc} 35 |
36 |
37 | ); 38 | }); 39 | -------------------------------------------------------------------------------- /src/infra/capabilities/containers/toast/index.tsx: -------------------------------------------------------------------------------- 1 | import { observer } from 'mobx-react'; 2 | import { Toast } from '@classroom/ui-kit'; 3 | import { useStore } from '@classroom/infra/hooks/ui-store'; 4 | import { TransitionGroup, CSSTransition } from 'react-transition-group'; 5 | import './index.css'; 6 | import { ToastType } from '@classroom/infra/stores/common/share'; 7 | 8 | export const ToastContainer = observer(() => { 9 | const { shareUIStore } = useStore(); 10 | const { toastQueue, removeToast } = shareUIStore; 11 | 12 | return ( 13 | 14 | {toastQueue.map((value: ToastType, idx: number) => ( 15 | 16 | { 20 | removeToast(value.id); 21 | }}> 22 | {value.desc} 23 | 24 | 25 | ))} 26 | 27 | ); 28 | }); 29 | -------------------------------------------------------------------------------- /src/infra/capabilities/containers/toolbar/board-cleaners/index.tsx: -------------------------------------------------------------------------------- 1 | import { observer } from 'mobx-react'; 2 | import { useStore } from '@classroom/infra/hooks/ui-store'; 3 | import { SvgImg, BoardCleaners, SvgIconEnum } from '@classroom/ui-kit'; 4 | import { useI18n } from 'agora-common-libs'; 5 | import { InteractionStateColors } from '@classroom/ui-kit/utilities/state-color'; 6 | 7 | export const BoardCleanersContainer = observer(() => { 8 | const { toolbarUIStore } = useStore(); 9 | const t = useI18n(); 10 | const { boardCleanerItems, handleBoradCleaner, activeTool } = toolbarUIStore; 11 | 12 | const mappedItems = boardCleanerItems.map((item) => { 13 | const { id, iconType, name } = item; 14 | const isActive = activeTool === id; 15 | 16 | return { 17 | id, 18 | icon: iconType ? ( 19 | 24 | ) : ( 25 | 26 | ), 27 | name, 28 | }; 29 | }); 30 | 31 | return ( 32 | 41 | ); 42 | }); 43 | -------------------------------------------------------------------------------- /src/infra/capabilities/containers/toolbar/slice/index.tsx: -------------------------------------------------------------------------------- 1 | import { observer } from 'mobx-react'; 2 | import { useStore } from '@classroom/infra/hooks/ui-store'; 3 | import { SvgImg, Slice, SvgIconEnum } from '@classroom/ui-kit'; 4 | import { useI18n } from 'agora-common-libs'; 5 | 6 | export const SliceContainer = observer(() => { 7 | const { toolbarUIStore } = useStore(); 8 | const t = useI18n(); 9 | const { sliceItems, handleSliceItem } = toolbarUIStore; 10 | 11 | const mappedItems = sliceItems.map((item) => { 12 | const { id, iconType, name } = item; 13 | return { 14 | id, 15 | icon: iconType ? : , 16 | name, 17 | }; 18 | }); 19 | 20 | return ( 21 | 28 | ); 29 | }); 30 | -------------------------------------------------------------------------------- /src/infra/capabilities/containers/widget/index.css: -------------------------------------------------------------------------------- 1 | .widget-container { 2 | top: 0; 3 | left: 0; 4 | width: 100%; 5 | height: 100%; 6 | position: absolute; 7 | pointer-events: none; 8 | } 9 | .widget-container * { 10 | pointer-events: all; 11 | } 12 | 13 | .widget-slot-chat { 14 | /* @apply bg-background; */ 15 | } 16 | 17 | .fcr-countdown-mobile-widget { 18 | position: relative; 19 | } 20 | .whiteboard-mobile-container .netless-whiteboard-wrapper { 21 | background-color: #fff; 22 | 23 | border-color: #fff; 24 | pointer-events: none; 25 | } 26 | -------------------------------------------------------------------------------- /src/infra/capabilities/scenarios/big-class-mobile/after-class.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AgoraIO-Community/CloudClass-Desktop/41651e4ae972718c8b5ac7674e97673f1a5a14b3/src/infra/capabilities/scenarios/big-class-mobile/after-class.png -------------------------------------------------------------------------------- /src/infra/capabilities/scenarios/big-class-mobile/generic-error.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AgoraIO-Community/CloudClass-Desktop/41651e4ae972718c8b5ac7674e97673f1a5a14b3/src/infra/capabilities/scenarios/big-class-mobile/generic-error.png -------------------------------------------------------------------------------- /src/infra/capabilities/scenarios/room/index.tsx: -------------------------------------------------------------------------------- 1 | import { useStore } from '@classroom/infra/hooks/ui-store'; 2 | import { useEffectOnce } from '@classroom/infra/hooks/utilites'; 3 | import { observer } from 'mobx-react'; 4 | import React, { FC } from 'react'; 5 | 6 | type Props = { 7 | children?: React.ReactNode; 8 | }; 9 | 10 | const Room: FC = observer(({ children }) => { 11 | const { join } = useStore(); 12 | 13 | useEffectOnce(() => { 14 | join(); 15 | }); 16 | 17 | return {children}; 18 | }); 19 | 20 | export default Room; 21 | -------------------------------------------------------------------------------- /src/infra/contexts/ui-store-factory.ts: -------------------------------------------------------------------------------- 1 | import { EduClassroomStore, EduRoomTypeEnum } from 'agora-edu-core'; 2 | import { EduClassroomUIStore } from '../stores/common'; 3 | import { EduInteractiveUIClassStore } from '../stores/interactive'; 4 | import { EduLectureUIStore } from '../stores/lecture'; 5 | import { EduLectureH5UIStore } from '../stores/lecture-mobile'; 6 | import { Edu1v1ClassUIStore } from '../stores/one-on-one'; 7 | 8 | export class EduUIStoreFactory { 9 | static createWithType(roomType: EduRoomTypeEnum, store: EduClassroomStore): EduClassroomUIStore { 10 | switch (roomType) { 11 | case EduRoomTypeEnum.Room1v1Class: 12 | return new Edu1v1ClassUIStore(store); 13 | case EduRoomTypeEnum.RoomSmallClass: 14 | return new EduInteractiveUIClassStore(store); 15 | case EduRoomTypeEnum.RoomBigClass: 16 | return new EduLectureUIStore(store); 17 | default: 18 | throw new Error(`No supported UIStore for room type: ${roomType}`); 19 | } 20 | } 21 | static createWithTypeH5( 22 | roomType: EduRoomTypeEnum, 23 | store: EduClassroomStore, 24 | ): EduClassroomUIStore { 25 | switch (roomType) { 26 | case EduRoomTypeEnum.RoomBigClass: 27 | return new EduLectureH5UIStore(store); 28 | default: 29 | throw new Error(`No supported UIStore for room type: ${roomType}`); 30 | } 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/infra/hooks/cabinet.ts: -------------------------------------------------------------------------------- 1 | import { useCallback } from 'react'; 2 | import { useStore } from './ui-store'; 3 | 4 | export const useExtensionCabinets = () => { 5 | const { widgetUIStore, classroomStore } = useStore(); 6 | 7 | const openExtensionCabinet = useCallback((id: string, remote = true) => { 8 | const nextZIndex = 9 | classroomStore.widgetStore.widgetController?.zIndexController.incrementZIndex(); 10 | 11 | const trackProps = { position: { xaxis: 0.5, yaxis: 0.5 }, zIndex: nextZIndex }; 12 | widgetUIStore.createWidget(id, { 13 | trackProperties: trackProps, 14 | userProperties: {}, 15 | properties: {}, 16 | }); 17 | 18 | if (remote) { 19 | classroomStore.widgetStore.widgetController?.setWidegtActive(id, trackProps); 20 | } 21 | }, []); 22 | 23 | const isInstalled = useCallback((id: string) => { 24 | const names = widgetUIStore.registeredWidgetNames; 25 | return names.includes(id); 26 | }, []); 27 | 28 | return { 29 | openExtensionCabinet, 30 | isInstalled, 31 | }; 32 | }; 33 | -------------------------------------------------------------------------------- /src/infra/hooks/index.ts: -------------------------------------------------------------------------------- 1 | import { DependencyList, useEffect, useMemo, useRef } from 'react'; 2 | import { clickAnywhere } from '../utils'; 3 | import { interactionThrottleHandler } from '../utils/interaction'; 4 | 5 | export const useInteractionThrottleHandler = ( 6 | func: T, 7 | options: { limitMs?: number; limitFunc?: () => void }, 8 | deps: DependencyList, 9 | ) => { 10 | return useMemo(() => { 11 | return interactionThrottleHandler(func, options.limitFunc || (() => {}), { 12 | limitMs: options.limitMs, 13 | }) as T; 14 | }, deps); 15 | }; 16 | 17 | export const useClickAnywhere = (cb: () => void) => { 18 | const ref = useRef(null); 19 | 20 | useEffect(() => { 21 | if (ref.current) { 22 | return clickAnywhere(ref.current, cb); 23 | } 24 | }, []); 25 | 26 | return ref; 27 | }; 28 | -------------------------------------------------------------------------------- /src/infra/hooks/utilites.ts: -------------------------------------------------------------------------------- 1 | import { useRef, useEffect } from 'react'; 2 | 3 | export const useInterval = (fun: CallableFunction, delay: number, start: boolean) => { 4 | const myRef = useRef(null); 5 | useEffect(() => { 6 | myRef.current = fun; 7 | }, [fun]); 8 | useEffect(() => { 9 | const timer = setInterval(() => { 10 | myRef.current(timer); 11 | }, delay); 12 | return () => clearInterval(timer); 13 | }, [start, delay]); 14 | }; 15 | 16 | export const useTimout = (fun: CallableFunction, delay: number, start: boolean) => { 17 | const myRef = useRef(null); 18 | useEffect(() => { 19 | myRef.current = fun; 20 | }, [fun]); 21 | useEffect(() => { 22 | let timer: null | ReturnType = null; 23 | if (start) { 24 | timer = setTimeout(() => { 25 | myRef.current(timer); 26 | }, delay); 27 | } 28 | return () => { 29 | timer && clearTimeout(timer); 30 | }; 31 | }, [start, delay]); 32 | }; 33 | 34 | export const useEffectOnce = (effect: any) => { 35 | useEffect(effect, []); 36 | }; 37 | -------------------------------------------------------------------------------- /src/infra/stores/common/cloud-drive/type.ts: -------------------------------------------------------------------------------- 1 | export interface CloudDriveResourceConvertProgress { 2 | prefix?: string; 3 | status: 'Waiting' | 'Converting' | 'Finished' | 'Fail'; 4 | totalPageSize: number; 5 | convertedPageSize: number; 6 | convertedPercentage: number; 7 | convertedFileList: { 8 | name?: string; 9 | ppt: { 10 | preview?: string; 11 | src: string; 12 | width: number; 13 | height: number; 14 | }; 15 | }[]; 16 | currentStep: string; 17 | previews: Record; 18 | images: Record< 19 | number, 20 | { 21 | width: number; 22 | height: number; 23 | url: string; 24 | } 25 | >; 26 | } 27 | -------------------------------------------------------------------------------- /src/infra/stores/common/hand-up/type.ts: -------------------------------------------------------------------------------- 1 | export enum OnPodiumStateEnum { 2 | /** 3 | * 正在台上 4 | */ 5 | onPodiuming = 1, 6 | /** 7 | * 正在挥手 8 | */ 9 | waveArming = 2, 10 | /** 11 | * 正在被邀请 12 | */ 13 | inviteding = 3, 14 | /** 15 | * 空闲中 16 | */ 17 | idleing = 4, 18 | } 19 | -------------------------------------------------------------------------------- /src/infra/stores/common/layout/helper.ts: -------------------------------------------------------------------------------- 1 | export const getRootDimensions = (containerNode: Window | HTMLElement) => { 2 | const height = 3 | containerNode instanceof Window ? containerNode.innerHeight : containerNode.clientHeight; 4 | const width = 5 | containerNode instanceof Window ? containerNode.innerWidth : containerNode.clientWidth; 6 | return { width, height }; 7 | }; 8 | -------------------------------------------------------------------------------- /src/infra/stores/common/pretest/helper.ts: -------------------------------------------------------------------------------- 1 | const virtualSoundCardPatternList = [/BlackHole/i, /SoundFlower/i, /AgoraALD/i]; 2 | 3 | export const matchVirtualSoundCardPattern = (deviceName: string) => { 4 | return virtualSoundCardPatternList.reduce((prev, pattern) => { 5 | pattern.test(deviceName) && (prev = true); 6 | return prev; 7 | }, false); 8 | }; 9 | -------------------------------------------------------------------------------- /src/infra/stores/common/pretest/type.ts: -------------------------------------------------------------------------------- 1 | export enum DeviceStateChangedReason { 2 | cameraFailed = 'pretest.device_not_working', 3 | micFailed = 'pretest.device_not_working', 4 | newDeviceDetected = 'new_device_detected', 5 | cameraUnplugged = 'pretest.camera_move_out', 6 | micUnplugged = 'pretest.mic_move_out', 7 | playbackUnplugged = 'pretest.playback_move_out', 8 | } 9 | -------------------------------------------------------------------------------- /src/infra/stores/common/roster/type.ts: -------------------------------------------------------------------------------- 1 | import { EduRoleTypeEnum } from 'agora-edu-core'; 2 | 3 | export enum DeviceState { 4 | // 设备开启 5 | enabled, 6 | // 设备关闭 7 | disabled, 8 | // 设备不可用 9 | unavailable, 10 | // 设备禁用 11 | unauthorized, 12 | } 13 | 14 | export type Operation = 15 | | 'podium' 16 | | 'grant-board' 17 | | 'camera' 18 | | 'microphone' 19 | | 'kick' 20 | | 'chat' 21 | | 'star' 22 | | 'supervise-student'; 23 | 24 | export type Operations = Partial>; 25 | 26 | export type Profile = { 27 | uid: string | number; 28 | isOnPodium: boolean; 29 | cameraState: DeviceState; 30 | microphoneState: DeviceState; 31 | }; 32 | 33 | /** 34 | * 分页查询用户参数 35 | */ 36 | export interface FetchUserParam { 37 | /** 38 | * 下一页的ID 39 | */ 40 | nextId: string | number | null | undefined; 41 | /** 42 | * 一页查询多少条 43 | */ 44 | count: number; 45 | /** 46 | * 筛选类型 0:全部 1:禁言 47 | */ 48 | type: FetchUserType; 49 | /** 50 | * 查询角色 51 | */ 52 | role: EduRoleTypeEnum; 53 | /** 54 | * 查询的用户名称,模糊查询 55 | */ 56 | userName?: string; 57 | } 58 | 59 | /** 60 | * 筛选用户类型 0:全部 1:禁言 61 | */ 62 | export enum FetchUserType { 63 | /** 64 | * 筛选全部的用户 65 | */ 66 | all = '0', 67 | /** 68 | * 筛选禁言的用户 69 | */ 70 | mute = '1', 71 | } 72 | -------------------------------------------------------------------------------- /src/infra/stores/common/stream-window/type.ts: -------------------------------------------------------------------------------- 1 | export interface StreamWindow { 2 | x: number; 3 | y: number; 4 | width: number; 5 | height: number; 6 | /** 7 | * view 层级关系 8 | */ 9 | zIndex: number; 10 | /** 11 | * 是否填充到多视频区域 12 | */ 13 | contain: boolean; 14 | } 15 | 16 | export type StreamWindowBounds = Omit; 17 | 18 | export interface WidgetTrackStruct { 19 | state: number; 20 | position: { xaxis: number; yaxis: number }; 21 | size: { width: number; height: number }; 22 | extra: { 23 | contain: boolean; 24 | zIndex: number; 25 | userUuid: string; 26 | [key: string]: any; 27 | }; 28 | } 29 | -------------------------------------------------------------------------------- /src/infra/stores/common/subscription/type.ts: -------------------------------------------------------------------------------- 1 | import { 2 | AgoraRteScene, 3 | AgoraRteVideoSourceType, 4 | AgoraStream, 5 | AGRtcConnectionType, 6 | } from 'agora-rte-sdk'; 7 | 8 | export type RemoteStreamMuteStatus = { 9 | muteVideo?: boolean; 10 | muteAudio?: boolean; 11 | }; 12 | 13 | export type LocalVideoStreamSubscribeOption = { 14 | mute: boolean; 15 | connectionType: AGRtcConnectionType; 16 | sourceType: AgoraRteVideoSourceType; 17 | }; 18 | -------------------------------------------------------------------------------- /src/infra/stores/common/type.ts: -------------------------------------------------------------------------------- 1 | export enum OrientationEnum { 2 | portrait = 'portrait', 3 | landscape = 'landscape', 4 | } 5 | 6 | export type ConfirmDialogAction = 'ok' | 'cancel'; 7 | 8 | export enum LayoutMaskCode { 9 | None = 0, 10 | StageVisible = 1, 11 | VideoGalleryVisible = 2, 12 | } 13 | -------------------------------------------------------------------------------- /src/infra/stores/interactive/board.ts: -------------------------------------------------------------------------------- 1 | import { BoardUIStore } from '../common/board'; 2 | 3 | export class InteractiveBoardUIStore extends BoardUIStore { 4 | protected get uiOverrides() { 5 | return { 6 | ...super.uiOverrides, 7 | heightRatio: 0.84, 8 | }; 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /src/infra/stores/interactive/index.ts: -------------------------------------------------------------------------------- 1 | import { EduClassroomStore } from 'agora-edu-core'; 2 | import { EduClassroomUIStore } from '../common'; 3 | import { InteractiveBoardUIStore } from './board'; 4 | import { InteractiveRoomStreamUIStore } from './stream'; 5 | 6 | export class EduInteractiveUIClassStore extends EduClassroomUIStore { 7 | constructor(store: EduClassroomStore) { 8 | super(store); 9 | this._streamUIStore = new InteractiveRoomStreamUIStore(store, this.shareUIStore, this._getters); 10 | this._boardUIStore = new InteractiveBoardUIStore(store, this.shareUIStore, this._getters); 11 | } 12 | 13 | get streamUIStore() { 14 | return this._streamUIStore as InteractiveRoomStreamUIStore; 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/infra/stores/lecture-mobile/index.ts: -------------------------------------------------------------------------------- 1 | import { EduClassroomStore } from 'agora-edu-core'; 2 | import { LectureH5BoardUIStore } from './board'; 3 | import { LectureH5RoomStreamUIStore } from './stream'; 4 | import { LectureH5LayoutUIStore } from './layout'; 5 | import { EduClassroomUIStore } from '../common'; 6 | import { LectureH5DeviceSettingUIStore } from './device-setting'; 7 | 8 | export class EduLectureH5UIStore extends EduClassroomUIStore { 9 | constructor(store: EduClassroomStore) { 10 | super(store); 11 | this._streamUIStore = new LectureH5RoomStreamUIStore(store, this.shareUIStore, this._getters); 12 | this._boardUIStore = new LectureH5BoardUIStore(store, this.shareUIStore, this._getters); 13 | this._layoutUIStore = new LectureH5LayoutUIStore(store, this.shareUIStore, this._getters); 14 | this._deviceSettingUIStore = new LectureH5DeviceSettingUIStore( 15 | store, 16 | this.shareUIStore, 17 | this._getters, 18 | ); 19 | } 20 | 21 | get streamUIStore() { 22 | return this._streamUIStore as LectureH5RoomStreamUIStore; 23 | } 24 | 25 | get boardUIStore() { 26 | return this._boardUIStore as LectureH5BoardUIStore; 27 | } 28 | 29 | get layoutUIStore() { 30 | return this._layoutUIStore as LectureH5LayoutUIStore; 31 | } 32 | get deviceSettingUIStore() { 33 | return this._deviceSettingUIStore as LectureH5DeviceSettingUIStore; 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/infra/stores/lecture/board.ts: -------------------------------------------------------------------------------- 1 | import { EduClassroomConfig } from 'agora-edu-core'; 2 | import { reaction } from 'mobx'; 3 | import { BoardUIStore } from '../common/board'; 4 | 5 | export class LectureBoardUIStore extends BoardUIStore { 6 | protected get uiOverrides() { 7 | return { 8 | ...super.uiOverrides, 9 | heightRatio: 1, 10 | }; 11 | } 12 | onInstall(): void { 13 | super.onInstall(); 14 | this._disposers.push( 15 | reaction( 16 | () => this.classroomStore.roomStore.acceptedList, 17 | (acceptedList) => { 18 | const { userUuid } = EduClassroomConfig.shared.sessionInfo; 19 | const isOnPodium = acceptedList.some((item) => item.userUuid === userUuid); 20 | const isGranted = this.boardApi.grantedUsers.has(userUuid); 21 | 22 | if (!isOnPodium && isGranted) { 23 | this.boardApi.grantPrivilege(userUuid, false); 24 | } 25 | }, 26 | ), 27 | ); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/infra/stores/lecture/index.ts: -------------------------------------------------------------------------------- 1 | import { EduClassroomStore } from 'agora-edu-core'; 2 | import { EduClassroomUIStore } from '../common'; 3 | import { LectureBoardUIStore } from './board'; 4 | import { LectureRosterUIStore } from './roster'; 5 | import { LectureRoomStreamUIStore } from './stream'; 6 | import { LectrueToolbarUIStore } from './toolbar'; 7 | 8 | export class EduLectureUIStore extends EduClassroomUIStore { 9 | constructor(store: EduClassroomStore) { 10 | super(store); 11 | this._streamUIStore = new LectureRoomStreamUIStore(store, this.shareUIStore, this._getters); 12 | this._rosterUIStore = new LectureRosterUIStore(store, this.shareUIStore, this._getters); 13 | this._boardUIStore = new LectureBoardUIStore(store, this.shareUIStore, this._getters); 14 | this._toolbarUIStore = new LectrueToolbarUIStore(store, this.shareUIStore, this._getters); 15 | } 16 | 17 | get streamUIStore() { 18 | return this._streamUIStore as LectureRoomStreamUIStore; 19 | } 20 | 21 | get rosterUIStore() { 22 | return this._rosterUIStore as LectureRosterUIStore; 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/infra/stores/one-on-one/index.ts: -------------------------------------------------------------------------------- 1 | import { EduClassroomStore } from 'agora-edu-core'; 2 | import { EduClassroomUIStore } from '../common'; 3 | import { OneToOneStreamUIStore } from './stream'; 4 | import { OneToOneToolbarUIStore } from './toolbar'; 5 | 6 | export class Edu1v1ClassUIStore extends EduClassroomUIStore { 7 | constructor(store: EduClassroomStore) { 8 | super(store); 9 | this._streamUIStore = new OneToOneStreamUIStore(store, this.shareUIStore, this._getters); 10 | this._toolbarUIStore = new OneToOneToolbarUIStore(store, this.shareUIStore, this._getters); 11 | } 12 | 13 | get streamUIStore() { 14 | return this._streamUIStore as OneToOneStreamUIStore; 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/infra/stores/one-on-one/stream.ts: -------------------------------------------------------------------------------- 1 | import { StreamUIStore } from '../common/stream'; 2 | 3 | export class OneToOneStreamUIStore extends StreamUIStore { 4 | private _teacherWidthRatio = 0.217; 5 | 6 | get toolbarPlacement(): 'bottom' | 'left' { 7 | return 'left'; 8 | } 9 | 10 | get toolbarOffset(): number[] { 11 | return [10, 0]; 12 | } 13 | 14 | get fullScreenToolbarOffset(): number[] { 15 | return [0, -58]; 16 | } 17 | 18 | get videoStreamSize() { 19 | const width = this.shareUIStore.classroomViewportSize.width * this._teacherWidthRatio; 20 | 21 | const height = (9 / 16) * width; 22 | 23 | return { width, height }; 24 | } 25 | 26 | onInstall(): void { 27 | super.onInstall(); 28 | 29 | this.classroomStore.mediaStore.setMirror(true); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/infra/utils/async-queue.ts: -------------------------------------------------------------------------------- 1 | import { bound, Log, Logger } from 'agora-rte-sdk'; 2 | import sortBy from 'lodash/sortBy'; 3 | 4 | export enum WorkPriority { 5 | high, 6 | normal, 7 | low, 8 | } 9 | 10 | type AsyncWork = { 11 | priority: WorkPriority; 12 | run: () => Promise; 13 | fail: (e: Error) => void; 14 | }; 15 | 16 | @Log.attach() 17 | class AsyncQueue { 18 | logger!: Logger; 19 | private _handle?: NodeJS.Timeout; 20 | private _queue: AsyncWork[] = []; 21 | 22 | runNextTick(run: () => Promise, fail: (e: Error) => void, priority = WorkPriority.normal) { 23 | this._queue.push({ run, fail, priority }); 24 | 25 | this.sortWorks(); 26 | 27 | if (this._handle) { 28 | clearTimeout(this._handle); 29 | } 30 | this._handle = setTimeout(this._execute); 31 | } 32 | 33 | sortWorks() { 34 | this._queue = sortBy(this._queue, ['priority']); 35 | } 36 | 37 | @bound 38 | private async _execute() { 39 | const copy = [...this._queue]; 40 | this._queue = []; 41 | 42 | for (let i = 0; i < copy.length; i++) { 43 | const { run, fail } = copy[i]; 44 | try { 45 | await run(); 46 | } catch (e) { 47 | fail(e as Error); 48 | break; 49 | } 50 | } 51 | this.logger.info('async works done'); 52 | } 53 | } 54 | 55 | export default new AsyncQueue(); 56 | -------------------------------------------------------------------------------- /src/infra/utils/error.ts: -------------------------------------------------------------------------------- 1 | import { AGError } from 'agora-rte-sdk'; 2 | import { transI18n } from 'agora-common-libs'; 3 | 4 | /** 5 | * 返回错误提示信息 6 | * @param error 7 | * @returns 8 | */ 9 | export const getEduErrorMessage = (error: Error) => { 10 | if (error instanceof AGError && error.codeList && error.codeList.length) { 11 | const code = error.codeList[error.codeList.length - 1]; 12 | 13 | if (error.servCode && error.servCode !== -1) { 14 | return transI18n(`edu_serv_error.${error.servCode}`); 15 | } else { 16 | return transI18n(`edu_error.${code}`); 17 | } 18 | } 19 | 20 | return null; 21 | }; 22 | 23 | /** 24 | * 如果Error中包含有服务端错误码,则返回服务端错误码 25 | * @param error 26 | * @returns 27 | */ 28 | export const getErrorServCode = (error: Error) => { 29 | if (error instanceof AGError) { 30 | return error.servCode; 31 | } 32 | }; 33 | -------------------------------------------------------------------------------- /src/infra/utils/event-center.ts: -------------------------------------------------------------------------------- 1 | import { AGEventEmitter } from 'agora-rte-sdk'; 2 | 3 | export const AgoraEduClassRoomUIType = 'classroom-ui-events'; 4 | 5 | export enum AgoraEduClassroomUIEvent { 6 | offStreamWindow = 'off-stream-window', 7 | toggleTeacherStreamWindow = 'toggle-teacher-stream-window', 8 | toggleWhiteboard = 'toggle-whiteboard', 9 | dragFileOverBoard = 'drag-file-over-board', 10 | dropFileOnBoard = 'drop-file-on-board', 11 | } 12 | 13 | type EventCallback = (type: AgoraEduClassroomUIEvent, ...args: any[]) => void; 14 | 15 | export class EduEventUICenter extends AGEventEmitter { 16 | static shared: EduEventUICenter = new EduEventUICenter(); 17 | private _callbacks: Set = new Set(); 18 | constructor() { 19 | super(); 20 | } 21 | 22 | emitClassroomUIEvents(type: AgoraEduClassroomUIEvent, ...args: any[]) { 23 | this.emit(AgoraEduClassRoomUIType, type, ...args); 24 | } 25 | 26 | onClassroomUIEvents(cb: EventCallback) { 27 | if (this._callbacks.has(cb)) { 28 | return; 29 | } 30 | this._callbacks.add(cb); 31 | this.on(AgoraEduClassRoomUIType, cb); 32 | } 33 | 34 | offClassroomUIEvents(cb: EventCallback) { 35 | this._callbacks.delete(cb); 36 | this.off(AgoraEduClassRoomUIType, cb); 37 | } 38 | 39 | cleanup() { 40 | this._callbacks.forEach((cb) => { 41 | this.offClassroomUIEvents(cb); 42 | }); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/infra/utils/extract.ts: -------------------------------------------------------------------------------- 1 | import { EduStream, EduUserStruct } from 'agora-edu-core'; 2 | import { AgoraRteVideoSourceType } from 'agora-rte-sdk'; 3 | 4 | /** 5 | * 提取流列表 6 | */ 7 | export const extractUserStreams = ( 8 | users: Map, 9 | streamByUserUuid: Map>, 10 | streamByStreamUuid: Map, 11 | sourceTypes: AgoraRteVideoSourceType[], 12 | ) => { 13 | const streams = new Set(); 14 | for (const user of users.values()) { 15 | const streamUuids = streamByUserUuid.get(user.userUuid) || new Set(); 16 | for (const streamUuid of streamUuids) { 17 | const stream = streamByStreamUuid.get(streamUuid); 18 | 19 | if (stream && sourceTypes.includes(stream.videoSourceType)) { 20 | streams.add(stream); 21 | } 22 | } 23 | } 24 | return streams; 25 | }; 26 | -------------------------------------------------------------------------------- /src/infra/utils/interaction.ts: -------------------------------------------------------------------------------- 1 | import { transI18n } from 'agora-common-libs'; 2 | 3 | type Delegate = (message: string) => void; 4 | 5 | export const interactionThrottleHandler = ( 6 | func: T, 7 | limitFunc: Delegate, 8 | options?: { 9 | limitMs?: number; 10 | message?: string; 11 | }, 12 | ) => { 13 | const { limitMs = 200, message = transI18n('toast.interaction_too_frequent') } = options || {}; 14 | 15 | let last = Date.now(); 16 | // @ts-ignore 17 | return ((...args: unknown[]) => { 18 | if (Date.now() - last < limitMs) { 19 | limitFunc(message); 20 | } else { 21 | // @ts-ignore 22 | func(...args); 23 | last = Date.now(); 24 | } 25 | }) as T; 26 | }; 27 | -------------------------------------------------------------------------------- /src/react-app-env.d.ts: -------------------------------------------------------------------------------- 1 | declare module '*.css'; 2 | declare module '*.svga'; 3 | declare module '*.svg'; 4 | declare module '*.json'; 5 | declare module '*.mp3'; 6 | declare module '*.mp4'; 7 | declare module '*.png'; 8 | declare module '*.gif'; 9 | declare module '*.jpg'; 10 | -------------------------------------------------------------------------------- /src/ui-kit/components/button/abutton.css: -------------------------------------------------------------------------------- 1 | .fcr-theme.ant-btn { 2 | border-radius: 8px; 3 | } 4 | -------------------------------------------------------------------------------- /src/ui-kit/components/button/abutton.tsx: -------------------------------------------------------------------------------- 1 | import Button, { ButtonProps } from 'antd/lib/button'; 2 | import { FC, PropsWithChildren } from 'react'; 3 | import './abutton.css'; 4 | type AButtonProps = Pick; 5 | 6 | export const AButton: FC> = ({ className = '', ...props }) => { 7 | return , ]}> 20 |

你确定要下课吗?

21 | 22 |
23 |
24 | test]}> 25 |

试用时间到,教室已解散!

26 |
27 |
28 |
29 | test, ]}> 33 |

课件未能加载成功,您可以点击重新加载重试,或者从云盘中播放课件

34 |
35 |
36 | 37 | ); 38 | 39 | Docs.args = { 40 | title: 'Modal Title', 41 | }; 42 | 43 | 44 | export default meta; 45 | -------------------------------------------------------------------------------- /src/ui-kit/components/overlay-wrap/index.css: -------------------------------------------------------------------------------- 1 | .overlay-wrap { 2 | display: flex; 3 | justify-content: center; 4 | align-items: center; 5 | width: 100%; 6 | height: 100%; 7 | } 8 | 9 | .overlay-wrap-content-ltr { 10 | justify-content: flex-start; 11 | align-items: flex-start; 12 | } 13 | 14 | .overlay-wrap-enter { 15 | opacity: 0; 16 | transform: translateY(20px); 17 | } 18 | .overlay-wrap-enter-active { 19 | opacity: 1; 20 | transform: translateY(0px); 21 | transition: 300ms; 22 | } 23 | .overlay-wrap-exit { 24 | transform: translateY(0px); 25 | opacity: 1; 26 | } 27 | .overlay-wrap-exit-active { 28 | opacity: 0; 29 | transform: translateY(-20px); 30 | transition: 300ms; 31 | } 32 | -------------------------------------------------------------------------------- /src/ui-kit/components/overlay-wrap/index.tsx: -------------------------------------------------------------------------------- 1 | import { FC, PropsWithChildren } from 'react'; 2 | import classnames from 'classnames'; 3 | import { BaseProps } from '../util/type'; 4 | import { CSSTransition } from 'react-transition-group'; 5 | import './index.css'; 6 | 7 | interface OverlayWrapProps extends BaseProps { 8 | opened?: boolean; 9 | centered?: boolean; 10 | onExited?: (() => void) | undefined; 11 | } 12 | 13 | /** 14 | * 15 | * @param param0 16 | * @returns 17 | */ 18 | export const OverlayWrap: FC> = ({ 19 | opened = true, 20 | onExited, 21 | className, 22 | children, 23 | centered = true, 24 | }) => { 25 | const cls = classnames('overlay-wrap', { 26 | 'overlay-wrap-content-ltr': !centered, 27 | [`${className}`]: !!className, 28 | }); 29 | return ( 30 | 31 |
{children}
32 |
33 | ); 34 | }; 35 | -------------------------------------------------------------------------------- /src/ui-kit/components/pagination/index.css: -------------------------------------------------------------------------------- 1 | .agora-pagination .rc-pagination-prev, 2 | .rc-pagination-item, 3 | .rc-pagination-next { 4 | @apply fcr-bg-component; 5 | border-radius: 6px; 6 | } 7 | 8 | .agora-pagination .rc-pagination-prev .rc-pagination-item-link, 9 | .rc-pagination-next .rc-pagination-item-link { 10 | border-radius: 6px; 11 | @apply fcr-text-level2 fcr-bg-component; 12 | } 13 | 14 | .agora-pagination .rc-pagination-item a { 15 | @apply fcr-text-level2; 16 | } 17 | 18 | .agora-pagination .rc-pagination-item-active { 19 | @apply fcr-bg-brand; 20 | } 21 | .agora-pagination .rc-pagination-item-active a { 22 | color: white; 23 | } 24 | -------------------------------------------------------------------------------- /src/ui-kit/components/pagination/index.tsx: -------------------------------------------------------------------------------- 1 | //@ts-nocheck 2 | import { FC } from 'react'; 3 | import RcPagination, { PaginationProps } from 'rc-pagination'; 4 | import 'rc-pagination/assets/index.css'; 5 | import './index.css'; 6 | 7 | export const Pagination: FC = (props) => { 8 | return ; 9 | }; 10 | -------------------------------------------------------------------------------- /src/ui-kit/components/placeholder/assets/board-disconnected.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AgoraIO-Community/CloudClass-Desktop/41651e4ae972718c8b5ac7674e97673f1a5a14b3/src/ui-kit/components/placeholder/assets/board-disconnected.png -------------------------------------------------------------------------------- /src/ui-kit/components/placeholder/assets/camera-broken.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AgoraIO-Community/CloudClass-Desktop/41651e4ae972718c8b5ac7674e97673f1a5a14b3/src/ui-kit/components/placeholder/assets/camera-broken.png -------------------------------------------------------------------------------- /src/ui-kit/components/placeholder/assets/camera-close.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AgoraIO-Community/CloudClass-Desktop/41651e4ae972718c8b5ac7674e97673f1a5a14b3/src/ui-kit/components/placeholder/assets/camera-close.png -------------------------------------------------------------------------------- /src/ui-kit/components/placeholder/assets/camera-disabled.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AgoraIO-Community/CloudClass-Desktop/41651e4ae972718c8b5ac7674e97673f1a5a14b3/src/ui-kit/components/placeholder/assets/camera-disabled.png -------------------------------------------------------------------------------- /src/ui-kit/components/placeholder/assets/empty-history.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AgoraIO-Community/CloudClass-Desktop/41651e4ae972718c8b5ac7674e97673f1a5a14b3/src/ui-kit/components/placeholder/assets/empty-history.png -------------------------------------------------------------------------------- /src/ui-kit/components/placeholder/assets/no-body.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AgoraIO-Community/CloudClass-Desktop/41651e4ae972718c8b5ac7674e97673f1a5a14b3/src/ui-kit/components/placeholder/assets/no-body.png -------------------------------------------------------------------------------- /src/ui-kit/components/placeholder/assets/no-file.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AgoraIO-Community/CloudClass-Desktop/41651e4ae972718c8b5ac7674e97673f1a5a14b3/src/ui-kit/components/placeholder/assets/no-file.png -------------------------------------------------------------------------------- /src/ui-kit/components/placeholder/index.css: -------------------------------------------------------------------------------- 1 | .placeholder { 2 | @apply fcr-w-full fcr-h-full fcr-flex fcr-flex-col fcr-items-center fcr-justify-center; 3 | } 4 | 5 | .placeholder-desc { 6 | margin-top: 46px; 7 | font-size: 13px; 8 | font-weight: 400; 9 | color: #7d8798; 10 | } 11 | 12 | .camera-placeholder { 13 | @apply fcr-bg-background; 14 | overflow: hidden; 15 | position: relative; 16 | height: 100%; 17 | width: 100%; 18 | display: flex; 19 | justify-content: center; 20 | align-items: center; 21 | flex-direction: column; 22 | } 23 | 24 | .camera-placeholder span { 25 | color: #677386; 26 | font-size: 12px; 27 | } 28 | 29 | .pretest .camera-placeholder { 30 | position: absolute; 31 | } 32 | 33 | .maxiumn-wrap { 34 | position: relative; 35 | display: flex; 36 | justify-content: center; 37 | align-items: center; 38 | width: 100%; 39 | height: 100%; 40 | background: #000; 41 | font-size: 13px; 42 | font-weight: 400; 43 | color: #ffffff; 44 | } 45 | 46 | .board-placeholder { 47 | display: flex; 48 | flex-direction: column; 49 | justify-content: center; 50 | align-items: center; 51 | width: 100%; 52 | height: 100%; 53 | } 54 | .reconnect-btn { 55 | margin-top: 30px; 56 | font-size: 16px; 57 | width: 136px; 58 | height: 44px; 59 | } 60 | -------------------------------------------------------------------------------- /src/ui-kit/components/placeholder/index.stories.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { Meta } from '@storybook/react'; 3 | import { Placeholder, CameraPlaceHolder } from '../placeholder'; 4 | 5 | const meta: Meta = { 6 | title: 'Components/Placeholder', 7 | component: Placeholder, 8 | }; 9 | 10 | type DocsProps = { 11 | placeholderDesc: string; 12 | }; 13 | 14 | export const Docs = ({ placeholderDesc }: DocsProps) => ( 15 | <> 16 |
17 | 18 |
19 |
20 | 21 |
22 |
23 | 24 |
25 |
26 | 27 |
28 |
29 | 30 |
31 |
32 | 33 |
34 |
35 | 36 |
37 | 38 | ); 39 | 40 | Docs.args = { 41 | placeholderDesc: '', 42 | }; 43 | 44 | export default meta; 45 | -------------------------------------------------------------------------------- /src/ui-kit/components/popover/index.stories.tsx: -------------------------------------------------------------------------------- 1 | import React, { FC } from 'react'; 2 | import { Meta } from '@storybook/react'; 3 | import { Button } from '../button'; 4 | import { Popover, PopoverProps } from '../popover'; 5 | 6 | const meta: Meta = { 7 | title: 'Components/Popover', 8 | component: Popover, 9 | }; 10 | 11 | export const Docs: FC = (props) => { 12 | return ( 13 | <> 14 |
15 | 16 | 17 | 18 |
19 |
20 | Hello world!} 25 | > 26 | 27 | 28 |
29 | 30 | ); 31 | }; 32 | 33 | export default meta; 34 | -------------------------------------------------------------------------------- /src/ui-kit/components/progress/index.css: -------------------------------------------------------------------------------- 1 | .bg-download-bg { 2 | background-color: #e3e3f0; 3 | } 4 | 5 | .bg-download-fg { 6 | background-color: #456dfd; 7 | } 8 | 9 | .progress-height { 10 | height: 0.5rem; 11 | border-radius: 0.25rem; 12 | display: flex; 13 | font-size: 0.75rem; 14 | line-height: 1rem; 15 | } 16 | 17 | 18 | 19 | .dialog-progress-container { 20 | box-sizing: border-box; 21 | margin: 0; 22 | padding: 8px; 23 | line-height: 1.5715; 24 | list-style: none; 25 | position: fixed; 26 | top: 45%; 27 | left: 0; 28 | z-index: 1010; 29 | width: 100%; 30 | pointer-events: none; 31 | text-align: center; 32 | } 33 | .dialog-progress-item { 34 | padding: 30px 25px; 35 | background: #ffffff; 36 | box-shadow: 0px 2px 6px 0px rgba(47, 65, 146, 0.15); 37 | border-radius: 12px; 38 | pointer-events: all; 39 | display: inline-block; 40 | } 41 | 42 | .dialog-progress-tip { 43 | font-size: 13px; 44 | font-weight: 400; 45 | color: #191919; 46 | margin-bottom: 20px; 47 | } 48 | .progress-percentage { 49 | font-size: 18px; 50 | color: #357bf6; 51 | line-height: 22px; 52 | } 53 | -------------------------------------------------------------------------------- /src/ui-kit/components/progress/index.stories.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { Progress } from '.'; 3 | 4 | export default { 5 | title: 'Components/Progress', 6 | }; 7 | 8 | export const ProgressShowCase = (props: any) => { 9 | return ; 10 | }; 11 | 12 | ProgressShowCase.args = { 13 | width: 100, 14 | progress: 100, 15 | }; 16 | -------------------------------------------------------------------------------- /src/ui-kit/components/root-box/index.css: -------------------------------------------------------------------------------- 1 | .root-box { 2 | height: 100%; 3 | overflow: hidden; 4 | } 5 | -------------------------------------------------------------------------------- /src/ui-kit/components/root-box/index.tsx: -------------------------------------------------------------------------------- 1 | import { FC, PropsWithChildren } from 'react'; 2 | import classnames from 'classnames'; 3 | import { BaseProps } from '../util/type'; 4 | import './index.css'; 5 | 6 | export type RootBoxProps = BaseProps; 7 | 8 | export const RootBox: FC> = ({ 9 | children, 10 | className, 11 | ...restProps 12 | }) => { 13 | const cls = classnames({ 14 | [`root-box`]: 1, 15 | [`${className}`]: !!className, 16 | }); 17 | return ( 18 |
19 | {children} 20 |
21 | ); 22 | }; 23 | -------------------------------------------------------------------------------- /src/ui-kit/components/roster/assets/loading.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AgoraIO-Community/CloudClass-Desktop/41651e4ae972718c8b5ac7674e97673f1a5a14b3/src/ui-kit/components/roster/assets/loading.gif -------------------------------------------------------------------------------- /src/ui-kit/components/roster/hooks.tsx: -------------------------------------------------------------------------------- 1 | import { useMemo } from 'react'; 2 | import { 3 | defaultColumns, 4 | kickOutColumn, 5 | podiumColumn, 6 | grantBoardColumn, 7 | starsColumn, 8 | superviseColumn, 9 | } from './columns'; 10 | import { Column } from './'; 11 | import { SupportedFunction } from '..'; 12 | import { sortBy } from 'lodash'; 13 | 14 | export const useColumns = (functions: SupportedFunction[]) => { 15 | const showKickOut = functions.includes('kick'); 16 | 17 | const cols = useMemo(() => { 18 | const cols = ([] as Column[]).concat(defaultColumns); 19 | if (functions.includes('kick')) { 20 | cols.push(kickOutColumn); 21 | } 22 | if (functions.includes('podium')) { 23 | cols.push(podiumColumn); 24 | } 25 | 26 | if (functions.includes('grant-board')) { 27 | cols.push(grantBoardColumn); 28 | } 29 | 30 | if (functions.includes('stars')) { 31 | cols.push(starsColumn); 32 | } 33 | 34 | // if (functions.includes('supervise-student')) { 35 | // cols.push(superviseColumn); 36 | // } 37 | 38 | return sortBy(cols, ['order']); 39 | }, [showKickOut]); 40 | 41 | return cols; 42 | }; 43 | -------------------------------------------------------------------------------- /src/ui-kit/components/select/assets/arrow-icon.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 编组 39备份 3 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /src/ui-kit/components/select/index.stories.tsx: -------------------------------------------------------------------------------- 1 | import React, { useState } from 'react'; 2 | import { Meta } from '@storybook/react'; 3 | import { Select } from '../select'; 4 | 5 | const meta: Meta = { 6 | title: 'Components/Select', 7 | component: Select, 8 | }; 9 | 10 | export const Docs = () => { 11 | const options = [ 12 | { value: 'chocolate', label: 'Chocolate' }, 13 | { value: 'strawberry', label: 'Strawberry' }, 14 | { value: 'vanilla', label: 'Vanilla' }, 15 | ]; 16 | const [selectedOption, setSelectedOption] = useState(''); 17 | return ( 18 | <> 19 |
20 | { 34 | console.log(value); 35 | setSelectedOption(value); 36 | }} 37 | options={options} 38 | prefix={food} 39 | /> 40 |
41 | 42 | ); 43 | }; 44 | 45 | export default meta; 46 | -------------------------------------------------------------------------------- /src/ui-kit/components/slider/index.css: -------------------------------------------------------------------------------- 1 | .fcr-theme.slider .ant-slider { 2 | margin: 0; 3 | } 4 | 5 | .fcr-theme.slider .ant-slider-track, 6 | .fcr-theme.slider .ant-slider-track:hover, 7 | .fcr-theme.slider .ant-slider:hover .ant-slider-track { 8 | background-color: #0073ff; 9 | } 10 | .fcr-theme.slider .ant-slider-handle, 11 | .fcr-theme.slider .ant-slider-handle:hover, 12 | .fcr-theme.slider .ant-slider:hover .ant-slider-handle:not(.ant-tooltip-open) { 13 | border-color: #0073ff; 14 | } 15 | -------------------------------------------------------------------------------- /src/ui-kit/components/sound-player/index.tsx: -------------------------------------------------------------------------------- 1 | import { FC, useRef, useEffect } from 'react'; 2 | 3 | type Props = { 4 | url: string; 5 | }; 6 | 7 | export const SoundPlayer: FC = ({ url }: Props) => { 8 | const audioRef = useRef(null); 9 | 10 | useEffect(() => { 11 | const audioElement = new Audio(url); 12 | audioElement.play(); 13 | audioRef.current = audioElement; 14 | 15 | return () => { 16 | if (audioRef.current) { 17 | audioRef.current.pause(); 18 | audioRef.current = null; 19 | } 20 | }; 21 | }, []); 22 | 23 | return null; 24 | }; 25 | -------------------------------------------------------------------------------- /src/ui-kit/components/svg-img/generate.ts: -------------------------------------------------------------------------------- 1 | // import fs from 'fs'; 2 | // import path from 'path'; 3 | 4 | // fs.readdirSync(path.resolve(__dirname, './paths')).forEach((d) => { 5 | // const value = d.replace('.tsx', ''); 6 | // const key = value.toUpperCase().replace(/-/g, '_'); 7 | 8 | // console.log(`${key} = '${value}',`); 9 | // }); 10 | -------------------------------------------------------------------------------- /src/ui-kit/components/svg-img/index.css: -------------------------------------------------------------------------------- 1 | .svg-story-div { 2 | display: flex; 3 | flex-direction: column; 4 | justify-content: center; 5 | align-items: center; 6 | margin-right: 25px; 7 | margin-bottom: 25px; 8 | } 9 | .my-svg-eraser { 10 | background: pink; 11 | cursor: pointer; 12 | transition: 0.5s; 13 | } 14 | .my-svg-eraser > .eraser { 15 | fill: yellow; 16 | } 17 | .my-svg-eraser:hover { 18 | background: #000; 19 | } 20 | .my-svg-eraser:hover > .eraser { 21 | fill: white; 22 | } 23 | 24 | .can-hover { 25 | cursor: pointer; 26 | border-radius: 50%; 27 | } 28 | 29 | .can-hover:hover { 30 | @apply fcr-bg-icon-selected-color; 31 | } 32 | -------------------------------------------------------------------------------- /src/ui-kit/components/svg-img/index.stories.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { Meta } from '@storybook/react'; 3 | import { SvgIcon } from './'; 4 | import { SvgIconEnum } from './type'; 5 | import './index.css'; 6 | 7 | const meta: Meta = { 8 | title: 'Components/SvgImg', 9 | component: SvgIcon, 10 | }; 11 | 12 | type DocsProps = { 13 | size: number; 14 | color: string; 15 | }; 16 | 17 | const keys = Object.keys(SvgIconEnum); 18 | export const Docs = ({ size, color }: DocsProps) => { 19 | return ( 20 |
21 |

Icon Gallery

22 |
23 | {keys.map((k) => { 24 | return ( 25 |
26 | 32 |
33 | ); 34 | })} 35 |
36 |
37 | ); 38 | }; 39 | 40 | Docs.args = { 41 | size: 100, 42 | color: '', 43 | }; 44 | 45 | export default meta; 46 | -------------------------------------------------------------------------------- /src/ui-kit/components/svg-img/paths/add-scene.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | import { PathOptions } from '../svg-dict'; 4 | 5 | export const path = ({ iconPrimary }: PathOptions) => 6 | 7 | 8 | 9 | 10 | 11 | 12 | export const viewBox = '0 0 24 24' -------------------------------------------------------------------------------- /src/ui-kit/components/svg-img/paths/add.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | import { PathOptions } from '../svg-dict'; 4 | 5 | export const path = ({ iconPrimary }: PathOptions) => ( 6 | 12 | ); 13 | 14 | export const viewBox = '0 0 40 40'; 15 | -------------------------------------------------------------------------------- /src/ui-kit/components/svg-img/paths/arrow.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | import { PathOptions } from '../svg-dict'; 4 | 5 | export const path = (props: PathOptions) => 6 | 9 | 10 | 11 | 16 | 17 | 18 | 19 | export const viewBox = '0 0 26 26' -------------------------------------------------------------------------------- /src/ui-kit/components/svg-img/paths/auto-play-failed.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | import { PathOptions } from '../svg-dict'; 4 | 5 | export const path = (props: PathOptions) => ( 6 | <> 7 | 8 | 14 | 15 | ); 16 | export const viewBox = '0 0 130 100'; 17 | -------------------------------------------------------------------------------- /src/ui-kit/components/svg-img/paths/backward.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | import { PathOptions } from '../svg-dict'; 4 | 5 | export const path = (props: PathOptions) => 9 | export const viewBox = '0 0 1024 1024' -------------------------------------------------------------------------------- /src/ui-kit/components/svg-img/paths/bad-signal.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | import { PathOptions } from '../svg-dict'; 4 | 5 | export const path = (props: PathOptions) => 9 | export const viewBox = '0 0 1024 1024' -------------------------------------------------------------------------------- /src/ui-kit/components/svg-img/paths/camera-enabled.tsx: -------------------------------------------------------------------------------- 1 | import React from "react" 2 | import { PathOptions } from "../svg-dict" 3 | 4 | export const path = (props: PathOptions) => 5 | 6 | 7 | 8 | 9 | 10 | 11 | export const viewBox = "0 0 24 24" -------------------------------------------------------------------------------- /src/ui-kit/components/svg-img/paths/camera-off-mobile.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | import { PathOptions } from '../svg-dict'; 4 | 5 | export const path = ({ iconPrimary }: PathOptions) => ( 6 | <> 7 | 13 | 19 | 20 | ); 21 | 22 | export const viewBox = '0 0 40 40'; 23 | -------------------------------------------------------------------------------- /src/ui-kit/components/svg-img/paths/camera-on-mobile.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | import { PathOptions } from '../svg-dict'; 4 | 5 | export const path = ({ iconPrimary }: PathOptions) => ( 6 | 13 | ); 14 | 15 | export const viewBox = '0 0 40 40'; 16 | -------------------------------------------------------------------------------- /src/ui-kit/components/svg-img/paths/camera.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | import { PathOptions } from '../svg-dict'; 4 | 5 | export const path = (props: PathOptions) => ( 6 | 13 | ); 14 | 15 | export const viewBox = '0 0 28 28'; 16 | -------------------------------------------------------------------------------- /src/ui-kit/components/svg-img/paths/checked.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | import { PathOptions } from '../svg-dict'; 4 | 5 | export const path = (props: PathOptions) => 9 | 10 | export const viewBox = '0 0 1024 1024' 11 | -------------------------------------------------------------------------------- /src/ui-kit/components/svg-img/paths/chevron-right.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | import { PathOptions } from '../svg-dict'; 4 | 5 | export const path = (props: PathOptions) => ( 6 | 10 | ); 11 | 12 | export const viewBox = '0 0 24 24'; 13 | -------------------------------------------------------------------------------- /src/ui-kit/components/svg-img/paths/circle.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | import { PathOptions } from '../svg-dict'; 4 | 5 | export const path = (props: PathOptions) => 9 | 10 | export const viewBox = '0 0 1060 1024' 11 | -------------------------------------------------------------------------------- /src/ui-kit/components/svg-img/paths/clear.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | import { PathOptions } from '../svg-dict'; 4 | 5 | export const path = (props: PathOptions) => 6 | 7 | 8 | 9 | 10 | 11 | export const viewBox = '0 0 24 24'; -------------------------------------------------------------------------------- /src/ui-kit/components/svg-img/paths/clicker.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | import { PathOptions } from '../svg-dict'; 4 | 5 | export const path = (props: PathOptions) => 6 | 7 | 8 | 9 | 10 | 11 | 12 | export const viewBox = '0 0 24 24'; -------------------------------------------------------------------------------- /src/ui-kit/components/svg-img/paths/clock.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | import { PathOptions } from '../svg-dict'; 4 | 5 | export const path = (props: PathOptions) => ( 6 | <> 7 | 16 | 22 | 23 | ); 24 | 25 | export const viewBox = '0 0 16 16'; 26 | -------------------------------------------------------------------------------- /src/ui-kit/components/svg-img/paths/close.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | import { PathOptions } from '../svg-dict'; 4 | 5 | export const path = (props: PathOptions) => 9 | 10 | export const viewBox = '0 0 1024 1024'; -------------------------------------------------------------------------------- /src/ui-kit/components/svg-img/paths/cloud-more.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { PathOptions } from '../svg-dict'; 3 | 4 | export const path = (props: PathOptions) => ( 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | ); 19 | 20 | export const viewBox = '0 0 32 32'; 21 | -------------------------------------------------------------------------------- /src/ui-kit/components/svg-img/paths/cloud.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | import { PathOptions } from '../svg-dict'; 4 | 5 | export const path = (props: PathOptions) => 6 | 7 | 8 | 9 | 10 | 11 | export const viewBox = '0 0 26 26'; -------------------------------------------------------------------------------- /src/ui-kit/components/svg-img/paths/copy.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | import { PathOptions } from '../svg-dict'; 4 | 5 | export const path = (props: PathOptions) => ( 6 | 7 | 12 | 21 | 22 | ); 23 | 24 | export const viewBox = '0 0 22 22'; 25 | -------------------------------------------------------------------------------- /src/ui-kit/components/svg-img/paths/delete.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | import { PathOptions } from '../svg-dict'; 4 | 5 | export const path = (props: PathOptions) => 9 | 10 | export const viewBox = '0 0 1024 1024'; -------------------------------------------------------------------------------- /src/ui-kit/components/svg-img/paths/down.tsx: -------------------------------------------------------------------------------- 1 | 2 | import React from 'react'; 3 | 4 | import { PathOptions } from '../svg-dict'; 5 | 6 | export const path = (props: PathOptions) => 7 | 8 | 9 | 10 | 11 | 12 | export const viewBox = '0 0 10 6'; 13 | -------------------------------------------------------------------------------- /src/ui-kit/components/svg-img/paths/dropdown.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | import { PathOptions } from '../svg-dict'; 4 | 5 | export const path = (props: PathOptions) => ( 6 | 7 | 13 | 14 | ); 15 | 16 | export const viewBox = '0 0 48 48'; 17 | -------------------------------------------------------------------------------- /src/ui-kit/components/svg-img/paths/edit.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | import { PathOptions } from '../svg-dict'; 4 | 5 | export const path = (props: PathOptions) => ( 6 | 7 | 13 | 17 | 18 | ); 19 | 20 | export const viewBox = '0 0 16 16'; 21 | -------------------------------------------------------------------------------- /src/ui-kit/components/svg-img/paths/emoji.tsx: -------------------------------------------------------------------------------- 1 | 2 | 3 | import React from 'react'; 4 | 5 | import { PathOptions } from '../svg-dict'; 6 | 7 | export const path = (props: PathOptions) => 8 | 9 | 10 | 11 | 12 | 13 | 14 | export const viewBox = '0 0 24 24'; 15 | 16 | -------------------------------------------------------------------------------- /src/ui-kit/components/svg-img/paths/eraser.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | import { PathOptions } from '../svg-dict'; 4 | 5 | export const path = (props: PathOptions) => 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | export const viewBox = '0 0 24 24'; -------------------------------------------------------------------------------- /src/ui-kit/components/svg-img/paths/excel.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | import { PathOptions } from '../svg-dict'; 4 | 5 | export const path = (props: PathOptions) => 9 | 10 | 11 | export const viewBox = '0 0 1024 1024'; -------------------------------------------------------------------------------- /src/ui-kit/components/svg-img/paths/forward.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | import { PathOptions } from '../svg-dict'; 4 | 5 | export const path = (props: PathOptions) => 9 | 10 | export const viewBox = '0 0 1024 1024'; -------------------------------------------------------------------------------- /src/ui-kit/components/svg-img/paths/fullscreen-shrink.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | import { PathOptions } from '../svg-dict'; 4 | 5 | export const path = (props: PathOptions) => ( 6 | 7 | 14 | 21 | 22 | ); 23 | 24 | export const viewBox = '0 0 48 48'; 25 | -------------------------------------------------------------------------------- /src/ui-kit/components/svg-img/paths/fullscreen.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | import { PathOptions } from '../svg-dict'; 4 | 5 | export const path = (props: PathOptions) => ( 6 | 7 | 14 | 21 | 22 | ); 23 | 24 | export const viewBox = '0 0 48 48'; 25 | -------------------------------------------------------------------------------- /src/ui-kit/components/svg-img/paths/goonstage.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | import { PathOptions } from '../svg-dict'; 4 | 5 | export const path = (props: PathOptions) => ( 6 | 7 | 13 | 14 | ); 15 | 16 | export const viewBox = '0 0 20 20'; 17 | -------------------------------------------------------------------------------- /src/ui-kit/components/svg-img/paths/hand.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | import { PathOptions } from '../svg-dict'; 4 | 5 | export const path = (props: PathOptions) => 6 | 7 | 8 | 9 | 10 | export const viewBox = '0 0 24 24'; -------------------------------------------------------------------------------- /src/ui-kit/components/svg-img/paths/hands-up.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | import { PathOptions } from '../svg-dict'; 4 | 5 | export const path = (props: PathOptions) => 6 | 7 | 8 | 9 | 10 | 11 | export const viewBox = '0 0 24 24'; -------------------------------------------------------------------------------- /src/ui-kit/components/svg-img/paths/id.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | import { PathOptions } from '../svg-dict'; 4 | 5 | export const path = (props: PathOptions) => ( 6 | 7 | 13 | 14 | ); 15 | 16 | export const viewBox = '0 0 48 48'; 17 | -------------------------------------------------------------------------------- /src/ui-kit/components/svg-img/paths/line.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | import { PathOptions } from '../svg-dict'; 4 | 5 | export const path = (props: PathOptions) => 9 | 10 | export const viewBox = '0 0 1060 1024'; -------------------------------------------------------------------------------- /src/ui-kit/components/svg-img/paths/log.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | import { PathOptions } from '../svg-dict'; 4 | 5 | export const path = (props: PathOptions) => 9 | 10 | export const viewBox = '0 0 1024 1024'; -------------------------------------------------------------------------------- /src/ui-kit/components/svg-img/paths/mark.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | import { PathOptions } from '../svg-dict'; 4 | 5 | export const path = (props: PathOptions) => ( 6 | 12 | ); 13 | 14 | export const viewBox = '0 0 48 48'; 15 | -------------------------------------------------------------------------------- /src/ui-kit/components/svg-img/paths/max.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | import { PathOptions } from '../svg-dict'; 4 | 5 | export const path = (props: PathOptions) => 9 | 10 | export const viewBox = '0 0 1024 1024'; -------------------------------------------------------------------------------- /src/ui-kit/components/svg-img/paths/mic-disabled.tsx: -------------------------------------------------------------------------------- 1 | import React from "react" 2 | import { PathOptions } from "../svg-dict" 3 | 4 | export const path = (props: PathOptions) => 5 | 6 | 7 | 8 | 9 | 10 | 11 | export const viewBox = "0 0 24 24" -------------------------------------------------------------------------------- /src/ui-kit/components/svg-img/paths/mic-enabled.tsx: -------------------------------------------------------------------------------- 1 | import React from "react" 2 | import { PathOptions } from "../svg-dict" 3 | 4 | export const path = (props: PathOptions) => 5 | 6 | 7 | 8 | 9 | 10 | export const viewBox = "0 0 24 24" -------------------------------------------------------------------------------- /src/ui-kit/components/svg-img/paths/microphone.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | import { PathOptions } from '../svg-dict'; 4 | 5 | export const path = (props: PathOptions) => ( 6 | 13 | ); 14 | 15 | export const viewBox = '0 0 28 28'; 16 | -------------------------------------------------------------------------------- /src/ui-kit/components/svg-img/paths/min.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | import { PathOptions } from '../svg-dict'; 4 | 5 | export const path = (props: PathOptions) => 9 | 10 | export const viewBox = '0 0 1024 1024'; -------------------------------------------------------------------------------- /src/ui-kit/components/svg-img/paths/more.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | import { PathOptions } from '../svg-dict'; 4 | 5 | export const path = (props: PathOptions) => 9 | 10 | export const viewBox = '0 0 1024 1024'; -------------------------------------------------------------------------------- /src/ui-kit/components/svg-img/paths/mute-mobile.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | import { PathOptions } from '../svg-dict'; 4 | 5 | export const path = ({ iconPrimary }: PathOptions) => ( 6 | <> 7 | 14 | 15 | ); 16 | 17 | export const viewBox = '0 0 40 40'; 18 | -------------------------------------------------------------------------------- /src/ui-kit/components/svg-img/paths/none.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | import { PathOptions } from '../svg-dict'; 4 | 5 | export const path = (props: PathOptions) => ( 6 | 12 | ); 13 | export const viewBox = '0 0 40 40'; 14 | -------------------------------------------------------------------------------- /src/ui-kit/components/svg-img/paths/normal-signal.tsx: -------------------------------------------------------------------------------- 1 | 2 | import React from 'react'; 3 | 4 | import { PathOptions } from '../svg-dict'; 5 | 6 | export const path = (props: PathOptions) => 10 | 11 | export const viewBox = '0 0 1024 1024' -------------------------------------------------------------------------------- /src/ui-kit/components/svg-img/paths/on-podium.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { PathOptions } from "../svg-dict" 3 | 4 | export const path = (props: PathOptions) => 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | export const viewBox = "0 0 22 22" -------------------------------------------------------------------------------- /src/ui-kit/components/svg-img/paths/pen-arrow.tsx: -------------------------------------------------------------------------------- 1 | 2 | 3 | import React from 'react'; 4 | 5 | import { PathOptions } from '../svg-dict'; 6 | 7 | export const path = ({ iconPrimary, penColor }: PathOptions) => 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | export const viewBox = '0 0 22 24'; 16 | -------------------------------------------------------------------------------- /src/ui-kit/components/svg-img/paths/pen-circle.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | import { PathOptions } from '../svg-dict'; 4 | 5 | export const path = ({ iconPrimary, penColor }: PathOptions) => 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | export const viewBox = '0 0 22 24'; 14 | -------------------------------------------------------------------------------- /src/ui-kit/components/svg-img/paths/pen-curve.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | import { PathOptions } from '../svg-dict'; 4 | 5 | export const path = ({ iconPrimary, penColor }: PathOptions) => 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | export const viewBox = '0 0 22 24'; -------------------------------------------------------------------------------- /src/ui-kit/components/svg-img/paths/pen-line.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | import { PathOptions } from '../svg-dict'; 4 | 5 | export const path = ({ iconPrimary, penColor }: PathOptions) => 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | export const viewBox = '0 0 22 24'; 14 | -------------------------------------------------------------------------------- /src/ui-kit/components/svg-img/paths/pen-rhombus.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | import { PathOptions } from '../svg-dict'; 4 | 5 | export const path = ({ iconPrimary, penColor }: PathOptions) => 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | export const viewBox = '0 0 22 24'; 14 | -------------------------------------------------------------------------------- /src/ui-kit/components/svg-img/paths/pen-square.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | import { PathOptions } from '../svg-dict'; 4 | 5 | export const path = ({ iconPrimary, penColor }: PathOptions) => 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | export const viewBox = '0 0 22 24'; 14 | -------------------------------------------------------------------------------- /src/ui-kit/components/svg-img/paths/pen-triangle.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | import { PathOptions } from '../svg-dict'; 4 | 5 | export const path = ({ iconPrimary, penColor }: PathOptions) => 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | export const viewBox = '0 0 22 24'; 14 | -------------------------------------------------------------------------------- /src/ui-kit/components/svg-img/paths/pen.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | import { PathOptions } from '../svg-dict'; 4 | 5 | export const path = (props: PathOptions) => 9 | 10 | export const viewBox = '0 0 1024 1024'; -------------------------------------------------------------------------------- /src/ui-kit/components/svg-img/paths/pentagram.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | import { PathOptions } from '../svg-dict'; 4 | 5 | export const path = (props: PathOptions) => 6 | 9 | 10 | 11 | 15 | 16 | 17 | 18 | export const viewBox = '0 0 26 26' -------------------------------------------------------------------------------- /src/ui-kit/components/svg-img/paths/pic.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | import { PathOptions } from '../svg-dict'; 4 | 5 | export const path = (props: PathOptions) => 6 | 7 | 8 | 9 | 10 | 11 | 12 | export const viewBox = '0 0 26 26' 13 | -------------------------------------------------------------------------------- /src/ui-kit/components/svg-img/paths/placeholder-no-setup.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | import { PathOptions } from '../svg-dict'; 4 | 5 | export const path = (props: PathOptions) => 6 | 7 | 8 | 13 | 14 | 15 | 16 | export const viewBox = '0 0 90 90'; -------------------------------------------------------------------------------- /src/ui-kit/components/svg-img/paths/pretest-check.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | import { PathOptions } from '../svg-dict'; 4 | 5 | export const path = (props: PathOptions) => 6 | 7 | 14 | 21 | 25 | 26 | 27 | 28 | export const viewBox = '0 0 30 30'; 29 | 30 | -------------------------------------------------------------------------------- /src/ui-kit/components/svg-img/paths/pretest-checked.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | import { PathOptions } from '../svg-dict'; 4 | 5 | export const path = (props: PathOptions) => 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | export const viewBox = '0 0 30 30'; 14 | 15 | -------------------------------------------------------------------------------- /src/ui-kit/components/svg-img/paths/record.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | import { PathOptions } from '../svg-dict'; 4 | 5 | export const path = (props: PathOptions) => 9 | 10 | export const viewBox = '0 0 1024 1024'; -------------------------------------------------------------------------------- /src/ui-kit/components/svg-img/paths/recording.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | import { PathOptions } from '../svg-dict'; 4 | 5 | export const path = (props: PathOptions) => 6 | 7 | 8 | 9 | 14 | 15 | 16 | 17 | export const viewBox = '0 0 24 24'; -------------------------------------------------------------------------------- /src/ui-kit/components/svg-img/paths/red-caution.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | import { PathOptions } from '../svg-dict'; 4 | 5 | export const path = (props: PathOptions) => 9 | export const viewBox = '0 0 1024 1024'; -------------------------------------------------------------------------------- /src/ui-kit/components/svg-img/paths/redo.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | import { PathOptions } from '../svg-dict'; 4 | 5 | export const path = (props: PathOptions) => 6 | 7 | 8 | 9 | 10 | 11 | export const viewBox = '0 0 24 24'; -------------------------------------------------------------------------------- /src/ui-kit/components/svg-img/paths/register.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | import { PathOptions } from '../svg-dict'; 4 | 5 | export const path = (props: PathOptions) => 6 | 7 | 8 | 9 | 10 | 11 | 12 | export const viewBox = '0 0 24 24'; -------------------------------------------------------------------------------- /src/ui-kit/components/svg-img/paths/reset.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | import { PathOptions } from '../svg-dict'; 4 | 5 | export const path = (props: PathOptions) => ( 6 | 11 | ); 12 | 13 | export const viewBox = '0 0 24 24'; 14 | -------------------------------------------------------------------------------- /src/ui-kit/components/svg-img/paths/review.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | import { PathOptions } from '../svg-dict'; 4 | 5 | export const path = (props: PathOptions) => 9 | export const viewBox = '0 0 1024 1024'; -------------------------------------------------------------------------------- /src/ui-kit/components/svg-img/paths/rhombus.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | import { PathOptions } from '../svg-dict'; 4 | 5 | export const path = (props: PathOptions) => 6 | 9 | 10 | 11 | 16 | 17 | 18 | 19 | export const viewBox = '0 0 26 26'; -------------------------------------------------------------------------------- /src/ui-kit/components/svg-img/paths/room-label.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | import { PathOptions } from '../svg-dict'; 4 | 5 | export const path = (props: PathOptions) => ( 6 | 7 | 13 | 14 | ); 15 | 16 | export const viewBox = '0 0 15 14'; 17 | -------------------------------------------------------------------------------- /src/ui-kit/components/svg-img/paths/save-ghost.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | import { PathOptions } from '../svg-dict'; 4 | 5 | export const path = (props: PathOptions) => 6 | 7 | 8 | 9 | 10 | 11 | export const viewBox = '0 0 24 24'; -------------------------------------------------------------------------------- /src/ui-kit/components/svg-img/paths/select.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | import { PathOptions } from '../svg-dict'; 4 | 5 | export const path = (props: PathOptions) => 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | export const viewBox = '0 0 24 24'; -------------------------------------------------------------------------------- /src/ui-kit/components/svg-img/paths/set.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | import { PathOptions } from '../svg-dict'; 4 | 5 | export const path = (props: PathOptions) => 6 | 7 | 8 | 9 | 10 | 11 | export const viewBox = '0 0 24 24'; -------------------------------------------------------------------------------- /src/ui-kit/components/svg-img/paths/settings.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | import { PathOptions } from '../svg-dict'; 4 | 5 | export const path = (props: PathOptions) => ( 6 | 10 | ); 11 | 12 | export const viewBox = '0 0 14 14'; 13 | -------------------------------------------------------------------------------- /src/ui-kit/components/svg-img/paths/share-mobile.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | import { PathOptions } from '../svg-dict'; 4 | 5 | export const path = (props: PathOptions) => ( 6 | <> 7 | 11 | 12 | ); 13 | 14 | export const viewBox = '0 0 24 24'; 15 | -------------------------------------------------------------------------------- /src/ui-kit/components/svg-img/paths/speaker.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | import { PathOptions } from '../svg-dict'; 4 | 5 | export const path = (props: PathOptions) => 9 | 10 | export const viewBox = '0 0 1024 1024'; -------------------------------------------------------------------------------- /src/ui-kit/components/svg-img/paths/square.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | import { PathOptions } from '../svg-dict'; 4 | 5 | export const path = (props: PathOptions) => 9 | 10 | export const viewBox = '0 0 1024 1024'; -------------------------------------------------------------------------------- /src/ui-kit/components/svg-img/paths/stage.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | import { PathOptions } from '../svg-dict'; 4 | 5 | export const path = (props: PathOptions) => ( 6 | 11 | ); 12 | 13 | export const viewBox = '0 0 28 28'; 14 | -------------------------------------------------------------------------------- /src/ui-kit/components/svg-img/paths/star-outline.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | import { PathOptions } from '../svg-dict'; 4 | 5 | export const path = (props: PathOptions) => 9 | 10 | export const viewBox = '0 0 1024 1024'; -------------------------------------------------------------------------------- /src/ui-kit/components/svg-img/paths/star.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | import { PathOptions } from '../svg-dict'; 4 | 5 | export const path = (props: PathOptions) => 9 | 10 | export const viewBox = '0 0 1024 1024'; -------------------------------------------------------------------------------- /src/ui-kit/components/svg-img/paths/stream-window-on.tsx: -------------------------------------------------------------------------------- 1 | import React from "react" 2 | import { PathOptions } from "../svg-dict"; 3 | 4 | 5 | export const path = (props: PathOptions) => 6 | 7 | 8 | 9 | 10 | 11 | 12 | export const viewBox = '0 0 24 24'; -------------------------------------------------------------------------------- /src/ui-kit/components/svg-img/paths/switch-screen-share.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | import { PathOptions } from '../svg-dict'; 4 | 5 | export const path = (props: PathOptions) => 6 | 7 | 8 | 13 | 14 | 15 | 20 | 26 | 27 | 28 | 29 | export const viewBox = '0 0 26 26'; -------------------------------------------------------------------------------- /src/ui-kit/components/svg-img/paths/text.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | import { PathOptions } from '../svg-dict'; 4 | 5 | export const path = (props: PathOptions) => 6 | 7 | 8 | 9 | 10 | 11 | export const viewBox = '0 0 24 24'; -------------------------------------------------------------------------------- /src/ui-kit/components/svg-img/paths/tools.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | import { PathOptions } from '../svg-dict'; 4 | 5 | export const path = (props: PathOptions) => 6 | 7 | 8 | 9 | 10 | 11 | export const viewBox = '0 0 24 24'; -------------------------------------------------------------------------------- /src/ui-kit/components/svg-img/paths/triangle-down.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | import { PathOptions } from '../svg-dict'; 4 | 5 | export const path = (props: PathOptions) => 6 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | export const viewBox = '0 0 6 6'; -------------------------------------------------------------------------------- /src/ui-kit/components/svg-img/paths/triangle-solid-down.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | import { PathOptions } from '../svg-dict'; 4 | 5 | export const path = (props: PathOptions) => 6 | 7 | 8 | 9 | 10 | 18 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | export const viewBox = '0 0 18 14'; -------------------------------------------------------------------------------- /src/ui-kit/components/svg-img/paths/triangle-solid-right.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | import { PathOptions } from '../svg-dict'; 4 | 5 | export const path = (props: PathOptions) => 6 | 7 | 8 | 9 | 10 | 18 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | export const viewBox = '0 0 18 18'; -------------------------------------------------------------------------------- /src/ui-kit/components/svg-img/paths/triangle-solid-up.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | import { PathOptions } from '../svg-dict'; 4 | 5 | export const path = (props: PathOptions) => 6 | 7 | 8 | 9 | 10 | 18 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | export const viewBox = '0 0 18 14'; -------------------------------------------------------------------------------- /src/ui-kit/components/svg-img/paths/triangle.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | import { PathOptions } from '../svg-dict'; 4 | 5 | export const path = (props: PathOptions) => 6 | 9 | 10 | 11 | 15 | 16 | 17 | 18 | 19 | export const viewBox = '0 0 26 26'; -------------------------------------------------------------------------------- /src/ui-kit/components/svg-img/paths/undo.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | import { PathOptions } from '../svg-dict'; 4 | 5 | export const path = (props: PathOptions) => 6 | 7 | 8 | 9 | 10 | 11 | export const viewBox = '0 0 24 24'; -------------------------------------------------------------------------------- /src/ui-kit/components/svg-img/paths/video-gallery.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | import { PathOptions } from '../svg-dict'; 4 | 5 | export const path = (props: PathOptions) => ( 6 | 12 | ); 13 | 14 | export const viewBox = '0 0 24 24'; 15 | -------------------------------------------------------------------------------- /src/ui-kit/components/svg-img/paths/video.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | import { PathOptions } from '../svg-dict'; 4 | 5 | export const path = (props: PathOptions) => 9 | 10 | export const viewBox = '0 0 1024 1024'; -------------------------------------------------------------------------------- /src/ui-kit/components/svg-img/paths/vote.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | import { PathOptions } from '../svg-dict'; 4 | 5 | export const path = (props: PathOptions) => 6 | 7 | 8 | 9 | 10 | 11 | export const viewBox = '0 0 24 24'; -------------------------------------------------------------------------------- /src/ui-kit/components/svg-img/paths/whiteboard.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | import { PathOptions } from '../svg-dict'; 4 | 5 | export const path = (props: PathOptions) => 6 | 7 | 8 | 9 | 10 | export const viewBox = '0 0 24 24'; -------------------------------------------------------------------------------- /src/ui-kit/components/svg-img/paths/word.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | import { PathOptions } from '../svg-dict'; 4 | 5 | export const path = (props: PathOptions) => 9 | 10 | export const viewBox = '0 0 1024 1024'; -------------------------------------------------------------------------------- /src/ui-kit/components/svg-img/paths/zoom-in.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | import { PathOptions } from '../svg-dict'; 4 | 5 | export const path = (props: PathOptions) => 9 | 10 | export const viewBox = '0 0 1024 1024'; -------------------------------------------------------------------------------- /src/ui-kit/components/svg-img/paths/zoom-out.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | import { PathOptions } from '../svg-dict'; 4 | 5 | export const path = (props: PathOptions) => ; 8 | 9 | export const viewBox = '0 0 1024 1024'; -------------------------------------------------------------------------------- /src/ui-kit/components/svg-img/svg-dict.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | 3 | const ctxRequire = require.context('./paths', false, /\.tsx$/); 4 | 5 | const paths: Record = {}; 6 | 7 | ctxRequire.keys().forEach(async (path) => { 8 | const m = ctxRequire(path) as SvgPath; 9 | const key = path.replace('./', '').replace('.tsx', ''); 10 | paths[key] = m; 11 | }); 12 | 13 | export type PathOptions = { 14 | iconPrimary: string, 15 | iconSecondary: string, 16 | [key: string]: string 17 | }; 18 | 19 | export type SvgPath = { 20 | path: (options: PathOptions) => React.ReactNode; 21 | viewBox?: string; 22 | }; 23 | 24 | export const getPath = (name: string, props: PathOptions) => { 25 | const svg = paths[name]; 26 | 27 | if (svg) { 28 | return svg.path(props); 29 | } 30 | 31 | return 32 | 33 | }; 34 | 35 | export const getViewBox = (name: string) => { 36 | const svg = paths[name]; 37 | 38 | if (svg) { 39 | return svg.viewBox; 40 | } 41 | 42 | return '0 0 0 0'; 43 | }; 44 | -------------------------------------------------------------------------------- /src/ui-kit/components/svga-player/assets/audio/reward.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AgoraIO-Community/CloudClass-Desktop/41651e4ae972718c8b5ac7674e97673f1a5a14b3/src/ui-kit/components/svga-player/assets/audio/reward.mp3 -------------------------------------------------------------------------------- /src/ui-kit/components/svga-player/assets/star.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AgoraIO-Community/CloudClass-Desktop/41651e4ae972718c8b5ac7674e97673f1a5a14b3/src/ui-kit/components/svga-player/assets/star.gif -------------------------------------------------------------------------------- /src/ui-kit/components/svga-player/assets/svga/hands-up.svga: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AgoraIO-Community/CloudClass-Desktop/41651e4ae972718c8b5ac7674e97673f1a5a14b3/src/ui-kit/components/svga-player/assets/svga/hands-up.svga -------------------------------------------------------------------------------- /src/ui-kit/components/svga-player/assets/svga/reward.svga: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AgoraIO-Community/CloudClass-Desktop/41651e4ae972718c8b5ac7674e97673f1a5a14b3/src/ui-kit/components/svga-player/assets/svga/reward.svga -------------------------------------------------------------------------------- /src/ui-kit/components/svga-player/svga-types.ts: -------------------------------------------------------------------------------- 1 | export type SvgaTypes = 'reward'; 2 | -------------------------------------------------------------------------------- /src/ui-kit/components/tabs/index.css: -------------------------------------------------------------------------------- 1 | /* fix bug: tabs 某些 css 找不到,所以手动补充下*/ 2 | .fcr-theme .ant-tabs-tabpane-hidden { 3 | display: none; 4 | } 5 | 6 | .whiteboard .tabs-top { 7 | width: 100%; 8 | } 9 | .whiteboard .tabs-top .tabs-nav-list { 10 | overflow-x: auto; 11 | } 12 | .whiteboard .tabs-top .tabs-nav-operations { 13 | display: none !important; 14 | } 15 | -------------------------------------------------------------------------------- /src/ui-kit/components/tabs/index.tsx: -------------------------------------------------------------------------------- 1 | import Tabs, { TabPaneProps, TabsProps } from 'antd/lib/tabs'; 2 | import classNames from 'classnames'; 3 | import React, { FC, PropsWithChildren } from 'react'; 4 | import { SvgIconEnum, SvgImg } from '../svg-img'; 5 | import './index.css'; 6 | 7 | export type ATabsProps = Pick< 8 | TabsProps, 9 | | 'className' 10 | | 'activeKey' 11 | | 'centered' 12 | | 'type' 13 | | 'onChange' 14 | | 'onEdit' 15 | | 'onTabClick' 16 | | 'animated' 17 | | 'moreIcon' 18 | | 'renderTabBar' 19 | | 'items' 20 | >; 21 | 22 | export const ATabs: FC> = ({ 23 | type, 24 | className, 25 | onEdit, 26 | centered, 27 | ...props 28 | }) => { 29 | const { moreIcon = } = props; 30 | 31 | return ( 32 | 33 | ); 34 | }; 35 | 36 | export type ATabPaneProps = Pick; 37 | 38 | export const ATabPane: FC> = ({ className, ...props }) => { 39 | return ; 40 | }; 41 | -------------------------------------------------------------------------------- /src/ui-kit/components/toast/index.css: -------------------------------------------------------------------------------- 1 | .toast { 2 | min-width: 100px; 3 | height: 34px; 4 | 5 | font-size: 14px; 6 | box-shadow: 0px 3px 8px 0px rgba(0, 0, 0, 0.1); 7 | border-radius: 4px; 8 | padding: 0 14px; 9 | animation: toastAnimation 0.2s; 10 | transition: top 0.3s, opacity 0.1s; 11 | @apply fcr-text-white fcr-cursor-pointer fcr-flex fcr-items-center fcr-justify-center fcr-box-border fcr-relative; 12 | } 13 | 14 | @keyframes toastAnimation { 15 | 0% { 16 | transform: scale(1); 17 | opacity: 0; 18 | } 19 | 50% { 20 | transform: scale(1.2); 21 | opacity: 0.5; 22 | } 23 | 100% { 24 | transform: scale(1); 25 | opacity: 1; 26 | } 27 | } 28 | .toast svg { 29 | margin-right: 6px; 30 | width: 21px; 31 | height: 21px; 32 | } 33 | 34 | .toast-success { 35 | @apply fcr-bg-safe fcr-border-safe fcr-border; 36 | } 37 | 38 | .toast-error { 39 | @apply fcr-bg-error fcr-border-error fcr-border; 40 | } 41 | 42 | .toast-warning { 43 | @apply fcr-bg-warning fcr-border-warning fcr-border; 44 | } 45 | 46 | .toast .toast-progress { 47 | position: absolute; 48 | width: 100%; 49 | height: 3px; 50 | background-color: #ccc; 51 | bottom: 0; 52 | left: 0; 53 | } 54 | .toast .toast-current { 55 | height: 3px; 56 | background-color: red; 57 | } 58 | -------------------------------------------------------------------------------- /src/ui-kit/components/toolbar/util.ts: -------------------------------------------------------------------------------- 1 | import { SvgIconEnum } from '../svg-img'; 2 | 3 | export const getPenIcon = (penTool: string) => { 4 | switch (penTool) { 5 | case 'square': 6 | return SvgIconEnum.PEN_SQUARE; 7 | case 'circle': 8 | return SvgIconEnum.PEN_CIRCLE; 9 | case 'line': 10 | return SvgIconEnum.PEN_LINE; 11 | case 'arrow': 12 | return SvgIconEnum.PEN_ARROW; 13 | case 'pentagram': 14 | return SvgIconEnum.PEN_PENTAGRAM; 15 | case 'rhombus': 16 | return SvgIconEnum.PEN_RHOMBUS; 17 | case 'triangle': 18 | return SvgIconEnum.PEN_TRIANGLE; 19 | case 'pen': 20 | default: 21 | return SvgIconEnum.PEN_CURVE; 22 | } 23 | }; 24 | 25 | export const getPenShapeIcon = (penTool: string) => { 26 | switch (penTool) { 27 | case 'square': 28 | return SvgIconEnum.SQUARE; 29 | case 'circle': 30 | return SvgIconEnum.CIRCLE; 31 | case 'line': 32 | return SvgIconEnum.LINE; 33 | case 'arrow': 34 | return SvgIconEnum.ARROW; 35 | case 'pentagram': 36 | return SvgIconEnum.PENTAGRAM; 37 | case 'rhombus': 38 | return SvgIconEnum.RHOMBUS; 39 | case 'triangle': 40 | return SvgIconEnum.TRIANGLE; 41 | case 'pen': 42 | default: 43 | return SvgIconEnum.PEN; 44 | } 45 | }; 46 | -------------------------------------------------------------------------------- /src/ui-kit/components/tree/style.css: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AgoraIO-Community/CloudClass-Desktop/41651e4ae972718c8b5ac7674e97673f1a5a14b3/src/ui-kit/components/tree/style.css -------------------------------------------------------------------------------- /src/ui-kit/components/util/colors.ts: -------------------------------------------------------------------------------- 1 | import { ElementOf, tuple } from './type'; 2 | 3 | // eslint-disable-next-line import/prefer-default-export 4 | export const PresetColorTypes = tuple( 5 | 'pink', 6 | 'red', 7 | 'yellow', 8 | 'orange', 9 | 'cyan', 10 | 'green', 11 | 'blue', 12 | 'purple', 13 | 'geekblue', 14 | 'magenta', 15 | 'volcano', 16 | 'gold', 17 | 'lime', 18 | ); 19 | 20 | export type PresetColorType = ElementOf; 21 | -------------------------------------------------------------------------------- /src/ui-kit/components/util/getRenderPropValue.ts: -------------------------------------------------------------------------------- 1 | import { ReactNode } from 'react'; 2 | 3 | export type RenderFunction = () => ReactNode; 4 | 5 | export const getRenderPropValue = (propValue?: ReactNode | RenderFunction): ReactNode => { 6 | if (!propValue) { 7 | return null; 8 | } 9 | 10 | const isRenderFunction = typeof propValue === 'function'; 11 | if (isRenderFunction) { 12 | return (propValue as RenderFunction)(); 13 | } 14 | 15 | return propValue; 16 | }; 17 | -------------------------------------------------------------------------------- /src/ui-kit/components/util/motion.ts: -------------------------------------------------------------------------------- 1 | import { CSSMotionProps, MotionEventHandler, MotionEndEventHandler } from 'rc-motion'; 2 | 3 | // ================== Collapse Motion ================== 4 | const getCollapsedHeight: MotionEventHandler = () => ({ 5 | height: 0, 6 | opacity: 0, 7 | }); 8 | const getRealHeight: MotionEventHandler = (node) => ({ 9 | height: node.scrollHeight, 10 | opacity: 1, 11 | }); 12 | const getCurrentHeight: MotionEventHandler = (node) => ({ 13 | height: node.offsetHeight, 14 | }); 15 | const skipOpacityTransition: MotionEndEventHandler = (_, event) => 16 | (event as TransitionEvent).propertyName === 'height'; 17 | 18 | const collapseMotion: CSSMotionProps = { 19 | motionName: 'ant-motion-collapse', 20 | onAppearStart: getCollapsedHeight, 21 | onEnterStart: getCollapsedHeight, 22 | onAppearActive: getRealHeight, 23 | onEnterActive: getRealHeight, 24 | onLeaveStart: getCurrentHeight, 25 | onLeaveActive: getCollapsedHeight, 26 | onAppearEnd: skipOpacityTransition, 27 | onEnterEnd: skipOpacityTransition, 28 | onLeaveEnd: skipOpacityTransition, 29 | motionDeadline: 500, 30 | }; 31 | 32 | const getTransitionName = (rootPrefixCls: string, motion: string, transitionName?: string) => { 33 | if (transitionName !== undefined) { 34 | return transitionName; 35 | } 36 | return `${rootPrefixCls}-${motion}`; 37 | }; 38 | export { getTransitionName }; 39 | export default collapseMotion; 40 | -------------------------------------------------------------------------------- /src/ui-kit/components/util/type.ts: -------------------------------------------------------------------------------- 1 | import { CSSProperties } from 'react'; 2 | import { SvgIconEnum } from '../svg-img'; 3 | 4 | export interface BaseProps { 5 | style?: CSSProperties; 6 | className?: string; 7 | id?: string; 8 | } 9 | 10 | export const tuple = (...args: T) => args; 11 | 12 | export type ElementOf = T extends (infer E)[] ? E : T extends readonly (infer F)[] ? F : never; 13 | 14 | export type IconWithState = { 15 | icon: SvgIconEnum; 16 | color?: string; 17 | }; 18 | -------------------------------------------------------------------------------- /src/ui-kit/components/volume/index.stories.tsx: -------------------------------------------------------------------------------- 1 | import React, { useState, useEffect } from 'react'; 2 | import { Meta } from '@storybook/react'; 3 | import { Volume, AudioVolume } from '../volume'; 4 | 5 | const meta: Meta = { 6 | title: 'Components/Volume', 7 | component: Volume, 8 | }; 9 | 10 | type DocsProps = { 11 | width: number; 12 | height: number; 13 | currentVolume: number; 14 | maxLength: number; 15 | }; 16 | 17 | export const Docs = () => { 18 | const [currentVolume, setCurrentVolume] = useState(0); 19 | let timer; 20 | useEffect(() => { 21 | timer = setInterval(() => { 22 | const number = (Math.random() * 100) | 0; 23 | setCurrentVolume(number); 24 | }, 1000); 25 | return () => { 26 | clearInterval(timer); 27 | }; 28 | }, []); 29 | return ( 30 | <> 31 |
32 | no volume 33 | 34 |
35 |
36 | has volume 37 | 38 |
39 |
40 | isMicMuted 41 | 42 |
43 | 44 | ); 45 | }; 46 | 47 | Docs.args = { 48 | width: 3, 49 | height: 12, 50 | currentVolumn: 0, 51 | maxLength: 20, 52 | }; 53 | 54 | export default meta; 55 | -------------------------------------------------------------------------------- /src/ui-kit/index.ts: -------------------------------------------------------------------------------- 1 | export * from './components'; 2 | export * from './utilities'; 3 | -------------------------------------------------------------------------------- /src/ui-kit/styles/global.css: -------------------------------------------------------------------------------- 1 | @tailwind base; 2 | 3 | @tailwind components; 4 | 5 | @tailwind utilities; 6 | 7 | @tailwind screens; 8 | 9 | html, 10 | html body { 11 | @apply fcr-font-scenario; 12 | } 13 | -------------------------------------------------------------------------------- /src/ui-kit/utilities/index.ts: -------------------------------------------------------------------------------- 1 | import { Z_INDEX_RULES } from './style-config'; 2 | 3 | export type I18nLanguage = 'zh' | 'en'; 4 | 5 | export const getOS = () => { 6 | let ua = navigator.userAgent, 7 | isWindowsPhone = /(?:Windows Phone)/.test(ua), 8 | isSymbian = /(?:SymbianOS)/.test(ua) || isWindowsPhone, 9 | isAndroid = /(?:Android)/.test(ua), 10 | isFireFox = /(?:Firefox)/.test(ua), 11 | isChrome = /(?:Chrome|CriOS)/.test(ua), 12 | isTablet = 13 | /(?:iPad|PlayBook)/.test(ua) || 14 | (isAndroid && !/(?:Mobile)/.test(ua)) || 15 | (isFireFox && /(?:Tablet)/.test(ua)) || 16 | (navigator.maxTouchPoints && 17 | navigator.maxTouchPoints > 2 && 18 | /MacIntel/.test(navigator.platform)) || 19 | 'ontouchend' in document, 20 | isPhone = /(?:iPhone)/.test(ua) && !isTablet, 21 | isPc = !isPhone && !isAndroid && !isSymbian; 22 | return { 23 | isTablet: isTablet, 24 | isPhone: isPhone, 25 | isPc: isPc, 26 | }; 27 | }; 28 | 29 | export const Z_INDEX_CONST = Z_INDEX_RULES; 30 | -------------------------------------------------------------------------------- /src/ui-kit/utilities/state-color.ts: -------------------------------------------------------------------------------- 1 | export const NetworkStateColors = { 2 | normal: '', 3 | bad: '', 4 | down: '', 5 | unknown: '', 6 | }; 7 | 8 | export const InteractionStateColors = { 9 | allow: '#357BF6', 10 | disallow: '#F04C36', 11 | half: '#B3D6FF', 12 | disabled: '#7C7C7C66', 13 | normal: '#D8D8D8', 14 | }; 15 | 16 | export const PlaceholderStateColors = { 17 | normal: '#97BAF9', 18 | disabled: '#C7D3ED', 19 | }; 20 | -------------------------------------------------------------------------------- /src/ui-kit/utilities/types.ts: -------------------------------------------------------------------------------- 1 | export type OnChangeEvents = { 2 | [Property in keyof Type as `onChange${Capitalize}`]: ( 3 | newValue: Type[Property], 4 | ) => void; 5 | }; 6 | 7 | export type HomeModule = OnChangeEvents & 8 | CustomizeVanillaType; 9 | 10 | export type IncludeUIKitSetter = { 11 | [Property in keyof Type as `set${Capitalize}`]: ( 12 | newValue: Type[Property], 13 | ) => void; 14 | }; 15 | 16 | export type UIKitModule = IncludeUIKitSetter & EntityType; 17 | -------------------------------------------------------------------------------- /tailwind.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | presets: [require('agora-common-libs/presets/tailwind.config.js')], 3 | }; 4 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es6", 4 | "experimentalDecorators": true, 5 | "module": "esnext", 6 | "lib": [ 7 | "dom", 8 | "dom.iterable", 9 | "esnext", 10 | "webworker" 11 | ], 12 | "outDir": "lib", 13 | "noImplicitAny": true, 14 | "noImplicitThis": true, 15 | "strictNullChecks": true, 16 | "allowJs": true, 17 | "skipLibCheck": true, 18 | "declaration": true, 19 | "esModuleInterop": true, 20 | "allowSyntheticDefaultImports": true, 21 | "strict": true, 22 | "forceConsistentCasingInFileNames": true, 23 | "noEmitHelpers": true, 24 | "noEmitOnError": false, 25 | "emitDeclarationOnly": true, 26 | "moduleResolution": "node", 27 | "resolveJsonModule": true, 28 | "isolatedModules": true, 29 | "jsx": "react-jsx", 30 | "noFallthroughCasesInSwitch": true, 31 | "baseUrl": ".", 32 | "paths": { 33 | "@classroom/*": ["src/*"], 34 | "agora-classroom-sdk": ["src/infra/api"] 35 | } 36 | }, 37 | "include": [ 38 | "src" 39 | ], 40 | "exclude": [ 41 | "node_modules", 42 | "**/*/*.stories.tsx" 43 | ], 44 | } 45 | -------------------------------------------------------------------------------- /typedoc.json: -------------------------------------------------------------------------------- 1 | { 2 | "entryPoints": ["./src/infra/api"], 3 | "out": "./docs", 4 | "exclude": ["**/node_modules/**"], 5 | "excludeExternals": true, 6 | "excludePrivate": true, 7 | "hideGenerator": true 8 | } 9 | -------------------------------------------------------------------------------- /webpack.config.js: -------------------------------------------------------------------------------- 1 | const webpackMerge = require('webpack-merge'); 2 | const path = require('path'); 3 | const baseConfig = require('agora-common-libs/presets/webpack.config.base.js'); 4 | const ROOT_PATH = path.resolve(__dirname, './'); 5 | const config = { 6 | entry: { 7 | edu_sdk: './src/infra/api/index.tsx', 8 | }, 9 | output: { 10 | path: path.resolve(ROOT_PATH, 'lib'), 11 | publicPath: './', 12 | filename: '[name].bundle.js', 13 | libraryTarget: 'umd', 14 | clean: true, 15 | }, 16 | resolve: { 17 | alias: { 18 | '@classroom': path.resolve(ROOT_PATH, './src'), 19 | 'agora-classroom-sdk': path.resolve(ROOT_PATH, './src/infra/api'), 20 | }, 21 | }, 22 | }; 23 | 24 | const mergedConfig = webpackMerge.merge(baseConfig, config); 25 | module.exports = mergedConfig; 26 | --------------------------------------------------------------------------------