cleanup some forms; break out major config styles into their own files

This commit is contained in:
gingervitis 2021-02-04 08:04:00 -08:00
parent e8c24fd2d4
commit 6e43870d41
18 changed files with 374 additions and 530 deletions

View File

@ -3,10 +3,18 @@ import '../styles/colors.scss';
import '../styles/globals.scss';
import '../styles/ant-overrides.scss';
import '../styles/form-textfields.scss';
import '../styles/form-toggleswitch.scss';
import '../styles/form-misc-elements.scss';
import '../styles/config-socialhandles.scss';
import '../styles/config-storage.scss';
import '../styles/config-tags.scss';
import '../styles/config-video-variants.scss';
import '../styles/home.scss';
import '../styles/chat.scss';
import '../styles/config.scss';
import '../styles/config-formfields.scss';
import { AppProps } from 'next/app';
import ServerStatusProvider from '../utils/server-status-context';

View File

@ -41,14 +41,14 @@ export default function CPUUsageSelector({ defaultValue, onChange }) {
};
return (
<div className="module-container config-video-segements-conatiner">
<div className="config-video-segements-conatiner">
<Title level={3}>CPU Usage</Title>
<p>There are trade-offs when considering CPU usage blah blah more wording here.</p>
<br />
<br />
<div className="segment-slider">
<div className="segment-slider-container">
<Slider
tipFormatter={value => TOOLTIPS[value] }
tipFormatter={value => TOOLTIPS[value]}
onChange={handleChange}
min={1}
max={Object.keys(SLIDER_MARKS).length}

View File

@ -51,8 +51,8 @@ export default function EditInstanceDetails() {
};
const showConfigurationRestartMessage = () => {
setMessage('Updating server settings requires a restart of your Owncast server.')
}
setMessage('Updating server settings requires a restart of your Owncast server.');
};
function generateStreamKey() {
let key = '';

View File

@ -157,7 +157,7 @@ export default function EditSocialLinks() {
postUpdateToAPI(postData);
};
const handleDeleteItem = index => {
const handleDeleteItem = (index: number) => {
const postData = [...currentSocialHandles];
postData.splice(index, 1);
postUpdateToAPI(postData);

View File

@ -1,4 +1,4 @@
import { Switch, Button, Collapse, Alert } from 'antd';
import { Switch, Button, Collapse } from 'antd';
import classNames from 'classnames';
import React, { useContext, useState, useEffect } from 'react';
import { UpdateArgs } from '../../../types/config-section';
@ -32,7 +32,7 @@ function checkSaveable(formValues: any, currentValues: any) {
if (enabled) {
if (!!endpoint && isValidUrl(endpoint) && !!accessKey && !!secret && !!bucket && !!region) {
if (
enabled !== currentValues.enabled ||
enabled !== currentValues.enabled ||
endpoint !== currentValues.endpoint ||
accessKey !== currentValues.accessKey ||
secret !== currentValues.secret ||
@ -52,13 +52,12 @@ function checkSaveable(formValues: any, currentValues: any) {
export default function EditStorage() {
const [formDataValues, setFormDataValues] = useState(null);
const [submitStatus, setSubmitStatus] = useState<StatusState>(null);
const [saved, setSaved] = useState<Boolean>(false);
const [shouldDisplayForm, setShouldDisplayForm] = useState(false);
const serverStatusData = useContext(ServerStatusContext);
const { serverConfig, setFieldInConfigState } = serverStatusData || {};
const {message, setMessage} = useContext(AlertMessageContext);
const { setMessage: setAlertMessage } = useContext(AlertMessageContext);
const { s3 } = serverConfig;
const {
@ -117,8 +116,9 @@ export default function EditStorage() {
setFieldInConfigState({ fieldName: 's3', value: postValue, path: '' });
setSubmitStatus(createInputStatus(STATUS_SUCCESS, 'Updated.'));
resetTimer = setTimeout(resetStates, RESET_TIMEOUT);
setSaved(true);
setMessage('Changing your storage configuration will take place the next time you start a new stream.');
setAlertMessage(
'Changing your storage configuration will take place the next time you start a new stream.',
);
},
onError: (message: string) => {
setSubmitStatus(createInputStatus(STATUS_ERROR, message));
@ -131,12 +131,6 @@ export default function EditStorage() {
const handleSwitchChange = (storageEnabled: boolean) => {
setShouldDisplayForm(storageEnabled);
handleFieldChange({ fieldName: 'enabled', value: storageEnabled });
// if current data in current store says s3 is enabled,
// we should save this state
// if (!storageEnabled && s3.enabled) {
// handleSave();
// }
};
const containerClass = classNames({

View File

@ -11,7 +11,7 @@ interface DropdownProps {
}
export default function SocialDropdown({ iconList, selectedOption, onSelected }: DropdownProps) {
const handleSelected = value => {
const handleSelected = (value: string) => {
if (onSelected) {
onSelected(value);
}

View File

@ -1,12 +1,15 @@
import React, { useContext, useState, useEffect } from 'react';
import { Typography, Slider } from 'antd';
import { ServerStatusContext } from '../../../utils/server-status-context';
import { API_VIDEO_SEGMENTS, RESET_TIMEOUT, postConfigUpdateToAPI } from './constants';
import {
API_VIDEO_SEGMENTS,
SUCCESS_STATES,
RESET_TIMEOUT,
postConfigUpdateToAPI,
} from './constants';
createInputStatus,
StatusState,
STATUS_ERROR,
STATUS_PROCESSING,
STATUS_SUCCESS,
} from '../../../utils/input-statuses';
import FormStatusIndicator from './form-status-indicator';
const { Title } = Typography;
@ -37,8 +40,10 @@ function SegmentToolTip({ value }: SegmentToolTipProps) {
}
export default function VideoLatency() {
const [submitStatus, setSubmitStatus] = useState(null);
const [submitStatusMessage, setSubmitStatusMessage] = useState('');
const [submitStatus, setSubmitStatus] = useState<StatusState>(null);
// const [submitStatus, setSubmitStatus] = useState(null);
// const [submitStatusMessage, setSubmitStatusMessage] = useState('');
const [selectedOption, setSelectedOption] = useState(null);
const serverStatusData = useContext(ServerStatusContext);
@ -57,13 +62,15 @@ export default function VideoLatency() {
const resetStates = () => {
setSubmitStatus(null);
setSubmitStatusMessage('');
// setSubmitStatusMessage('');
resetTimer = null;
clearTimeout(resetTimer);
};
// posts all the variants at once as an array obj
const postUpdateToAPI = async (postValue: any) => {
setSubmitStatus(createInputStatus(STATUS_PROCESSING));
await postConfigUpdateToAPI({
apiPath: API_VIDEO_SEGMENTS,
data: { value: postValue },
@ -73,34 +80,28 @@ export default function VideoLatency() {
value: postValue,
path: 'videoSettings',
});
setSubmitStatus(createInputStatus(STATUS_SUCCESS, 'Variants updated.'));
setSubmitStatus('success');
setSubmitStatusMessage('Variants updated.');
// setSubmitStatus('success');
// setSubmitStatusMessage('Variants updated.');
resetTimer = setTimeout(resetStates, RESET_TIMEOUT);
},
onError: (message: string) => {
setSubmitStatus('error');
setSubmitStatusMessage(message);
setSubmitStatus(createInputStatus(STATUS_ERROR, message));
// setSubmitStatus('error');
// setSubmitStatusMessage(message);
resetTimer = setTimeout(resetStates, RESET_TIMEOUT);
},
});
};
const { icon: newStatusIcon = null, message: newStatusMessage = '' } =
SUCCESS_STATES[submitStatus] || {};
const statusMessage = (
<div className={`status-message ${submitStatus || ''}`}>
{newStatusIcon} {newStatusMessage} {submitStatusMessage}
</div>
);
const handleChange = value => {
postUpdateToAPI(value);
};
return (
<div className="module-container config-video-segements-conatiner">
<div className="config-video-segements-conatiner">
<Title level={3}>Latency Buffer</Title>
<p>
There are trade-offs when cosidering video latency and reliability. Blah blah .. better
@ -108,7 +109,7 @@ export default function VideoLatency() {
</p>
<br />
<br />
<div className="segment-slider">
<div className="segment-slider-container">
<Slider
tipFormatter={value => <SegmentToolTip value={SLIDER_COMMENTS[value]} />}
onChange={handleChange}
@ -119,7 +120,7 @@ export default function VideoLatency() {
value={selectedOption}
/>
</div>
{statusMessage}
<FormStatusIndicator status={submitStatus} />
</div>
);
}

View File

@ -1,12 +1,11 @@
// This content populates the video variant modal, which is spawned from the variants table.
import React from 'react';
import { Slider, Select, Switch, Divider, Collapse } from 'antd';
import { FieldUpdaterFunc, CpuUsageLevel, VideoVariant } from '../../../types/config-section';
import { Slider, Switch, Collapse } from 'antd';
import { FieldUpdaterFunc, VideoVariant } from '../../../types/config-section';
import { DEFAULT_VARIANT_STATE } from './constants';
import InfoTip from '../info-tip';
import CPUUsageSelector from './cpu-usage';
const { Option } = Select;
const { Panel } = Collapse;
const VIDEO_VARIANT_DEFAULTS = {
@ -57,12 +56,6 @@ export default function VideoVariantForm({
const handleVideoBitrateChange = (value: number) => {
onUpdateField({ fieldName: 'videoBitrate', value });
};
const handleAudioBitrateChange = (value: number) => {
onUpdateField({ fieldName: 'audioBitrate', value });
};
const handleAudioPassChange = (value: boolean) => {
onUpdateField({ fieldName: 'audioPassthrough', value });
};
const handleVideoPassChange = (value: boolean) => {
onUpdateField({ fieldName: 'videoPassthrough', value });
};
@ -80,18 +73,12 @@ export default function VideoVariantForm({
const videoBRMax = videoBitrateDefaults.max;
const videoBRUnit = videoBitrateDefaults.unit;
const audioBitrateDefaults = VIDEO_VARIANT_DEFAULTS.audioBitrate;
const audioBRMin = audioBitrateDefaults.min;
const audioBRMax = audioBitrateDefaults.max;
const audioBRUnit = audioBitrateDefaults.unit;
const selectedVideoBRnote = `Selected: ${dataState.videoBitrate}${videoBRUnit} - it sucks`;
const selectedAudioBRnote = `Selected: ${dataState.audioBitrate}${audioBRUnit} - too slow`;
const selectedFramerateNote = `Selected: ${dataState.framerate}${framerateUnit} - whoa there`;
const selectedPresetNote = '';
return (
<div className="variant-form">
<div className="config-variant-form">
<div className="section-intro">
Say a thing here about how this all works. Read more{' '}
<a href="https://owncast.online/docs/configuration/">here</a>.
@ -130,6 +117,7 @@ export default function VideoVariantForm({
</div>
</div>
</div>
{/* VIDEO BITRATE FIELD */}
<div className={`field ${dataState.videoPassthrough ? 'disabled' : ''}`}>
<p className="label">
@ -192,55 +180,6 @@ export default function VideoVariantForm({
) : null}
</div>
</div>
<Divider />
{/* AUDIO PASSTHROUGH FIELD */}
{/* <div className="field">
<p className="label">
<InfoTip tip={VIDEO_VARIANT_DEFAULTS.audioPassthrough.tip} />
Use Audio Passthrough?
</p>
<div className="form-component">
<Switch
defaultChecked={dataState.audioPassthrough}
checked={dataState.audioPassthrough}
onChange={handleAudioPassChange}
checkedChildren="Yes"
unCheckedChildren="No"
/>
{dataState.audioPassthrough ? <span className="note">Same as source</span> : null}
</div>
</div> */}
{/* AUDIO BITRATE FIELD */}
{/* <div className={`field ${dataState.audioPassthrough ? 'disabled' : ''}`}>
<p className="label">
<InfoTip tip={VIDEO_VARIANT_DEFAULTS.audioBitrate.tip} />
Audio Bitrate:
</p>
<div className="form-component">
<Slider
// tooltipVisible={dataState.audioPassthrough !== true}
tipFormatter={value => `${value} ${audioBRUnit}`}
disabled={dataState.audioPassthrough === true}
defaultValue={dataState.audioBitrate}
value={dataState.audioBitrate}
onChange={handleAudioBitrateChange}
step={audioBitrateDefaults.incrementBy}
min={audioBRMin}
max={audioBRMax}
marks={{
[audioBRMin]: `${audioBRMin} ${audioBRUnit}`,
[audioBRMax]: `${audioBRMax} ${audioBRUnit}`,
}}
/>
{selectedAudioBRnote ? (
<span className="selected-value-note">{selectedAudioBRnote}</span>
) : null}
</div>
</div> */}
</Panel>
</Collapse>
</div>

View File

@ -1,6 +1,5 @@
// Updating a variant will post ALL the variants in an array as an update to the API.
// todo : add DELETE option
import React, { useContext, useState } from 'react';
import { Typography, Table, Modal, Button } from 'antd';
import { ColumnsType } from 'antd/lib/table';
@ -20,11 +19,19 @@ import {
const { Title } = Typography;
const CPU_USAGE_LEVEL_MAP = {
1: 'lowest',
2: 'low',
3: 'medium',
4: 'high',
5: 'highest',
};
export default function CurrentVariantsTable() {
const [displayModal, setDisplayModal] = useState(false);
const [modalProcessing, setModalProcessing] = useState(false);
const [editId, setEditId] = useState(0);
const {setMessage} = useContext(AlertMessageContext);
const { setMessage } = useContext(AlertMessageContext);
// current data inside modal
const [modalDataState, setModalDataState] = useState(DEFAULT_VARIANT_STATE);
@ -76,7 +83,9 @@ export default function CurrentVariantsTable() {
setSubmitStatusMessage('Variants updated.');
resetTimer = setTimeout(resetStates, RESET_TIMEOUT);
setMessage('Updating your video configuration will take effect the next time you begin a new stream.');
setMessage(
'Updating your video configuration will take effect the next time you begin a new stream.',
);
},
onError: (message: string) => {
setSubmitStatus('error');
@ -117,14 +126,6 @@ export default function CurrentVariantsTable() {
const { icon: newStatusIcon = null, message: newStatusMessage = '' } =
SUCCESS_STATES[submitStatus] || {};
const cpuUsageLevelLabelMap = {
1: 'lowest',
2: 'low',
3: 'medium',
4: 'high',
5: 'highest',
};
const videoQualityColumns: ColumnsType<VideoVariant> = [
{
title: 'Video bitrate',
@ -137,7 +138,7 @@ export default function CurrentVariantsTable() {
title: 'CPU Usage',
dataIndex: 'cpuUsageLevel',
key: 'cpuUsageLevel',
render: (level: string) => (!level ? 'n/a' : cpuUsageLevelLabelMap[level]),
render: (level: string) => (!level ? 'n/a' : CPU_USAGE_LEVEL_MAP[level]),
},
{
title: '',

View File

@ -0,0 +1,25 @@
// styles for social handles editing section
.social-option,
.social-dropdown {
.ant-select-item-option-content,
.ant-select-selection-item {
display: flex;
flex-direction: row;
justify-content: flex-start;
align-items: center;
padding: .25em;
line-height: normal;
.option-icon {
height: 1.5em;
width: 1.5em;
line-height: normal;
}
.option-label {
display: inline-block;
margin-left: 1em;
line-height: normal;
}
}
}

View File

@ -0,0 +1,49 @@
// styles for Storage config section
.edit-storage-container {
.form-fields {
display: none;
margin-bottom: 1em;
}
&.enabled {
.form-fields {
display: block;
}
}
.button-container {
margin: 1em 0;
}
.advanced-section {
margin: 1em 0;
}
}
// Do something special for the stream key field
.field-streamkey-container {
margin-bottom: 1.5em;
.field-tip {
color: var(--ant-warning);
}
.left-side {
display: flex;
flex-direction: row;
align-items: flex-start;
}
.textfield-with-submit-container {
margin-bottom: 0;
}
.streamkey-actions {
white-space: nowrap;
button {
margin: .25em;
}
@media (max-width: 800px) {
margin-top: 2em;
}
}
}

View File

@ -0,0 +1,37 @@
// config tags block
.tag-current-tags {
.ant-tag {
margin: .1rem;
font-size: .85rem;
border-radius: 10em;
padding: .25em 1em;
background-color: rgba(255,255,255,.5);
.ant-tag-close-icon {
transform: translateY(-1px);
margin-left: .3rem;
padding: 2px;
border-radius: 5rem;
border: 1px solid #eee;
&:hover {
border-color: #e03;
svg {
fill: black;
transition: fill .3s;
}
}
}
}
}
.add-new-tag-section {
display: flex;
flex-direction: row;
justify-content: flex-start;
align-items: center;
.new-tag-input {
width: 16em;
}
}

View File

@ -0,0 +1,85 @@
// styles for Video variant editor (table + modal)
.config-variant-form {
.blurb {
margin: 1em;
opacity: .75;
}
.note {
display: inline-block;
margin-left: 1em;
font-size: .75em;
opacity: .5;
font-style: italic;
}
.section-intro {
margin-bottom: 2em;
}
.field {
margin-bottom: 2em;
display: flex;
flex-direction: row;
justify-content: center;
align-items: flex-start;
transform: opacity .15s;
&.disabled {
opacity: .25;
}
.label {
width: 40%;
text-align: right;
padding-right: 2em;
font-weight: bold;
color: var(--owncast-purple);
}
.info-tip {
margin-right: 1em;
}
.form-component {
width: 60%;
.selected-value-note {
font-size: .85em;
display: inline-block;
text-align: center;
}
}
}
.ant-collapse {
border: none;
border-radius: 6px;
}
.ant-collapse > .ant-collapse-item:last-child,
.ant-collapse > .ant-collapse-item:last-child > .ant-collapse-header {
border: none;
background-color: rgba(0,0,0,.25);
border-radius: 6px;
}
.ant-collapse-content {
background-color: rgba(0,0,0,.1);
}
}
.config-video-segements-conatiner {
.status-message {
text-align: center;
}
}
.variants-table {
.actions {
display: flex;
align-items: center;
justify-content: center;
}
.delete-button {
margin-left: .5em;
opacity: .8;
}
}

View File

@ -1,164 +1,7 @@
// .config-public-details-container {
// display: flex;
// flex-direction: row;
// align-items: flex-start;
// flex-wrap: wrap;
// .text-fields {
// margin-right: 2rem;
// }
// .misc-fields {
// width: 25em;
// }
// .tag-editor-container,
// .config-directory-details-form {
// border-radius: 1em;
// background-color: rgba(128,99,255,.1);
// padding: 1.5em;
// margin-bottom: 1em;
// }
// }
.module-container {
border-radius: 1em;
background-color: rgba(128,99,255,.1);
padding: 1.5em;
margin-bottom: 1em;
}
// form-textfield
// form-textfield
// .textfield-container {
// display: flex;
// flex-direction: column;
// align-items: flex-start;
// justify-content: flex-end;
// position: relative;
// width: 314px;
// // &.type-numeric {
// // .ant-form-item-control {
// // flex-direction: row;
// // .ant-form-item-control-input {
// // margin-right: .75rem;
// // }
// // }
// // }
// }
// .textfield {
// display: flex;
// flex-direction: row;
// align-items: flex-start;
// .field {
// width: 18rem;
// &.ant-input-number {
// width: 8em;
// }
// }
// .info-tip {
// margin-right: .75rem;
// }
// .ant-form-item {
// margin-bottom: 16px;
// &.ant-form-item-with-help {
// margin-bottom: 16px;
// }
// }
// .ant-form-item-label label {
// font-weight: bold;
// color: var(--owncast-purple);
// }
// .ant-form-item-explain {
// width: 70%;
// }
// }
// .submit-button {
// position: absolute;
// right: 0;
// bottom: .5em;
// }
// .ant-form-horizontal {
// .textfield-container.type-numeric {
// width: auto;
// .submit-button {
// bottom: unset;
// top: 0;
// right: unset;
// }
// }
// }
// form-toggleswitch
// form-toggleswitch
// .toggleswitch-container {
// .status-message {
// margin-top: .25rem;
// }
// }
// .toggleswitch {
// display: flex;
// flex-direction: row;
// align-items: center;
// justify-content: flex-start;
// .label {
// font-weight: bold;
// color: var(--owncast-purple);
// }
// .info-tip {
// margin-left: .5rem;
// svg {
// fill: white;
// }
// }
// .ant-form-item {
// margin: 0 .75rem 0 0;
// }
// }
// TAGS STUFF
// TAGS STUFF
.tag-current-tags {
.ant-tag {
margin: .1rem;
font-size: .85rem;
border-radius: 10em;
padding: .25em 1em;
background-color: rgba(255,255,255,.5);
.ant-tag-close-icon {
transform: translateY(-1px);
margin-left: .3rem;
padding: 2px;
border-radius: 5rem;
border: 1px solid #eee;
&:hover {
border-color: #e03;
svg {
fill: black;
transition: fill .3s;
}
}
}
}
}
.add-new-tag-section {
display: flex;
flex-direction: row;
justify-content: flex-start;
align-items: center;
.new-tag-input {
width: 16em;
}
}
.config-page-content-form {
.page-content-actions {
@ -174,101 +17,9 @@
}
}
.config-video-variants {
.config-video-misc {
margin: 2rem 0;
// .ant-form {
// display: flex;
// flex-direction: row;
// align-items: flex-start;
// }
}
}
.variant-form {
.blurb {
margin: 1em;
opacity: .75;
}
.note {
display: inline-block;
margin-left: 1em;
font-size: .75em;
opacity: .5;
font-style: italic;
}
.section-intro {
margin-bottom: 2em;
}
.field {
margin-bottom: 2em;
display: flex;
flex-direction: row;
justify-content: center;
align-items: flex-start;
transform: opacity .15s;
&.disabled {
opacity: .25;
}
.label {
width: 40%;
text-align: right;
padding-right: 2em;
font-weight: bold;
color: var(--owncast-purple);
}
.info-tip {
margin-right: 1em;
}
.form-component {
width: 60%;
.selected-value-note {
font-size: .85em;
display: inline-block;
text-align: center;
}
}
}
.ant-collapse {
border: none;
border-radius: 6px;
}
.ant-collapse > .ant-collapse-item:last-child,
.ant-collapse > .ant-collapse-item:last-child > .ant-collapse-header {
border: none;
background-color: rgba(0,0,0,.25);
border-radius: 6px;
}
.ant-collapse-content {
background-color: rgba(0,0,0,.1);
}
}
.config-video-segements-conatiner {
.segment-slider {
width: 90%;
margin: auto;
padding: 1em 2em .75em;
background-color: black;
border-radius: 1em;
}
.status-message {
text-align: center;
}
}
.variants-table {
.actions {
display: flex;
align-items: center;
justify-content: center;
}
.delete-button {
margin-left: .5em;
opacity: .8;
}
}
.segment-tip {
width: 10em;
text-align: center;
@ -276,33 +27,6 @@
display: inline-block;
}
.social-option,
.social-dropdown {
// .ant-select-selector,
// .ant-select-selection-search-input {
// height: 40px !important;
// }
.ant-select-item-option-content,
.ant-select-selection-item {
display: flex;
flex-direction: row;
justify-content: flex-start;
align-items: center;
padding: .25em;
line-height: normal;
.option-icon {
height: 1.5em;
width: 1.5em;
line-height: normal;
}
.option-label {
display: inline-block;
margin-left: 1em;
line-height: normal;
}
}
}
// .social-option {
// .ant-select-item-option-content {
// display: flex;
@ -324,57 +48,3 @@
// EDIT STORAGE
.edit-storage-container {
.form-fields {
display: none;
margin-bottom: 1em;
}
&.enabled {
.form-fields {
display: block;
}
}
.button-container {
margin: 1em 0;
}
.advanced-section {
margin: 1em 0;
}
}
.field-container {
padding: .85em 0 .5em;
&:nth-child(even) {
background-color: rgba(0,0,0,.25);
}
}
.field-streamkey-container {
margin-bottom: 1.5em;
.field-tip {
color: var(--ant-warning);
}
.left-side {
display: flex;
flex-direction: row;
align-items: flex-start;
}
.textfield-with-submit-container {
margin-bottom: 0;
}
.streamkey-actions {
white-space: nowrap;
button {
margin: .25em;
}
@media (max-width: 800px) {
margin-top: 2em;
}
}
}

View File

@ -0,0 +1,64 @@
/* Base styles for misc helper components around forms */
/* STATUS-CONTAINER BASE */
.status-container {
&.status-success {
color: var(--ant-success);
}
&.status-error {
color: var(--ant-error);
}
&.status-warning {
color: var(--ant-warning);
}
&.empty {
display: none;
}
display: flex;
flex-direction: row;
justify-content: flex-start;
align-items: center;
font-size: .75rem;
.status-icon {
display: inline-block;
margin-right: .5em;
}
}
/* TIP CONTAINER BASE */
.field-tip {
font-size: .7em;
color: rgba(255,255,255,.5)
}
/*
Ideal for wrapping each Textfield on a page with many text fields in a row. This div will alternate colors and look like a table.
*/
.field-container {
padding: .85em 0 .5em;
&:nth-child(even) {
background-color: rgba(0,0,0,.25);
}
}
/* SEGMENT SLIDER */
.segment-slider-container {
width: 90%;
margin: auto;
padding: 1em 2em .75em;
background-color: black;
border-radius: 1em;
}
.segment-tip {
width: 10em;
text-align: center;
margin: auto;
display: inline-block;
}

View File

@ -1,37 +1,4 @@
// Base styles for form-textfield, form-textfield-with-submit, and helper components.
/* STATUS-CONTAINER BASE */
.status-container {
&.status-success {
color: var(--ant-success);
}
&.status-error {
color: var(--ant-error);
}
&.status-warning {
color: var(--ant-warning);
}
&.empty {
display: none;
}
display: flex;
flex-direction: row;
justify-content: flex-start;
align-items: center;
font-size: .75rem;
.status-icon {
display: inline-block;
margin-right: .5em;
}
}
/* TIP CONTAINER BASE */
.field-tip {
font-size: .7em;
color: rgba(255,255,255,.5)
}
// Base styles for form-textfield, form-textfield-with-submit
/* TEXTFIELD-CONTAINER BASE */
@ -103,6 +70,8 @@
}
}
/* TEXTFIELD-WITH-SUBMIT-CONTAINER BASE */
/* TEXTFIELD-WITH-SUBMIT-CONTAINER BASE */
/* TEXTFIELD-WITH-SUBMIT-CONTAINER BASE */
.textfield-with-submit-container {
display: flex;
@ -169,30 +138,3 @@
}
}
}
/* TOGGLE SWITCH-WITH-SUBMIT-CONTAINER BASE */
.toggleswitch-container {
.status-container {
margin-top: .25rem;
}
.toggleswitch {
display: flex;
flex-direction: row;
align-items: center;
justify-content: flex-start;
.label {
font-weight: bold;
color: var(--owncast-purple);
}
.info-tip {
margin-left: .5rem;
svg {
fill: white;
}
}
.ant-form-item {
margin: 0 .75rem 0 0;
}
}
}

View File

@ -0,0 +1,29 @@
/* TOGGLE SWITCH-WITH-SUBMIT-CONTAINER BASE */
.toggleswitch-container {
.status-container {
margin-top: .25rem;
}
.toggleswitch {
display: flex;
flex-direction: row;
align-items: center;
justify-content: flex-start;
.label {
font-weight: bold;
color: var(--owncast-purple);
}
.info-tip {
margin-left: .5rem;
svg {
fill: white;
}
}
.ant-form-item {
margin: 0 .75rem 0 0;
}
}
}

View File

@ -1,27 +1,27 @@
import React, { useState, useEffect } from 'react';
import React, { useState } from 'react';
import PropTypes from 'prop-types';
export const AlertMessageContext = React.createContext({
message: null,
setMessage: (text?: string) => {
return text;
}
message: null,
setMessage: (text?: string) => {
return text;
},
});
const AlertMessageProvider = ({ children }) => {
const [message, setMessage] = useState('');
const providerValue = {
message,
setMessage
}
return (
<AlertMessageContext.Provider value={providerValue}>{children}</AlertMessageContext.Provider>
)
}
const [message, setMessage] = useState('');
AlertMessageProvider.propTypes = {
children: PropTypes.element.isRequired
const providerValue = {
message,
setMessage,
};
return (
<AlertMessageContext.Provider value={providerValue}>{children}</AlertMessageContext.Provider>
);
};
export default AlertMessageProvider;
AlertMessageProvider.propTypes = {
children: PropTypes.element.isRequired,
};
export default AlertMessageProvider;