Skip to content

Commit

Permalink
merge a lot of things from TW
Browse files Browse the repository at this point in the history
  • Loading branch information
JeremyGamer13 committed Aug 31, 2023
1 parent ae15f36 commit e962b39
Show file tree
Hide file tree
Showing 12 changed files with 340 additions and 81 deletions.
13 changes: 1 addition & 12 deletions src/components/tw-fonts-modal/add-system-font.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -64,24 +64,13 @@ class AddSystemFont extends React.Component {
/>
</p>

{/* TODO: datalist is pretty bad at this. we should try our own dropdown? */}
<FontName
name={this.state.name}
onChange={this.handleChangeName}
fontManager={this.props.fontManager}
placeholder="Wingdings"
list="fontslist"
options={this.state.localFonts}
/>
{this.state.localFonts && (
<datalist id="fontslist">
{this.state.localFonts.map(family => (
<option
key={family}
value={family}
/>
))}
</datalist>
)}

{this.state.name && (
<React.Fragment>
Expand Down
39 changes: 39 additions & 0 deletions src/components/tw-fonts-modal/font-dropdown-item.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import React from 'react';
import PropTypes from 'prop-types';
import bindAll from 'lodash.bindall';
import styles from './fonts-modal.css';

class FontDropdownItem extends React.Component {
constructor (props) {
super(props);
bindAll(this, [
'handleSelect'
]);
}

handleSelect () {
this.props.onSelect(this.props.family);
}

render () {
return (
<div
className={styles.fontDropdownItem}
title={this.props.family}
style={{
fontFamily: this.props.family
}}
onMouseDown={this.handleSelect}
>
{this.props.family}
</div>
);
}
}

FontDropdownItem.propTypes = {
family: PropTypes.string.isRequired,
onSelect: PropTypes.func.isRequired
};

export default FontDropdownItem;
124 changes: 107 additions & 17 deletions src/components/tw-fonts-modal/font-name.jsx
Original file line number Diff line number Diff line change
@@ -1,53 +1,142 @@
import React from 'react';
import ReactDOM from 'react-dom';
import PropTypes from 'prop-types';
import styles from './fonts-modal.css';
import bindAll from 'lodash.bindall';
import styles from './fonts-modal.css';
import FontDropdownItem from './font-dropdown-item.jsx';

class FontName extends React.Component {
constructor (props) {
super(props);
bindAll(this, [
'setInputRef',
'handleChange',
'handleFlush',
'handleKeyPress'
'handleFocus',
'handleBlur',
'handleResize',
'handleSelectFont',
'handleKeyDown'
]);
this.state = {
focused: false,
rect: null
};
}

componentDidMount () {
window.addEventListener('resize', this.handleResize);
}

componentWillUnmount () {
window.removeEventListener('resize', this.handleResize);
}

setInputRef (input) {
this.input = input;

// can't use autoFocus because handleFocus relies on the ref existing already
if (input) {
input.focus();
}
}

handleChange (e) {
this.props.onChange(e.target.value);
}

handleFlush () {
handleFocus () {
this.setState({
focused: true,
rect: this.input.getBoundingClientRect()
});
}

handleBlur () {
this.props.onChange(this.props.fontManager.getSafeName(this.props.name));
this.setState({
focused: false
});
}

handleKeyPress (e) {
handleResize () {
if (this.state.focused) {
this.setState({
rect: this.input.getBoundingClientRect()
});
}
}

handleSelectFont (font) {
this.props.onChange(font);
}

handleKeyDown (e) {
if (e.key === 'Enter') {
this.handleFlush();
this.handleBlur();
e.target.blur();
}
}

getFilteredOptions () {
if (!this.state.focused || !this.props.options) {
return [];
}
const name = this.props.name.toLowerCase();
const candidates = this.props.options
.filter(family => family.toLowerCase().includes(name));
if (candidates.length === 0 && candidates[0] === this.props.name) {
return [];
}
return candidates;
}

render () {
const {
/* eslint-disable no-unused-vars */
name,
onChange,
fontManager,
options,
/* eslint-enable no-unused-vars */
...props
} = this.props;

const filteredOptions = this.getFilteredOptions();
return (
<input
{...props}
type="text"
autoFocus
className={styles.fontInput}
value={this.props.name}
onChange={this.handleChange}
onBlur={this.handleFlush}
onKeyPress={this.handleKeyPress}
/>
<div className={styles.fontInputOuter}>
<input
{...props}
type="text"
className={styles.fontInput}
value={this.props.name}
ref={this.setInputRef}
onChange={this.handleChange}
onFocus={this.handleFocus}
onBlur={this.handleBlur}
onKeyDown={this.handleKeyDown}
/>

{/* We need to use a portal to get out of the modal's overflow: hidden, unfortunately */}
{filteredOptions.length > 0 && ReactDOM.createPortal(
<div
className={styles.fontDropdownOuter}
style={{
left: `${this.state.rect.left - 4}px`,
top: `${this.state.rect.top + this.state.rect.height + 4}px`,
width: `${this.state.rect.width + 8}px`
}}
>
{this.getFilteredOptions().map(family => (
<FontDropdownItem
key={family}
family={family}
onSelect={this.handleSelectFont}
/>
))}
</div>,
document.body
)}
</div>
);
}
}
Expand All @@ -57,7 +146,8 @@ FontName.propTypes = {
onChange: PropTypes.func.isRequired,
fontManager: PropTypes.shape({
getSafeName: PropTypes.func.isRequired
}).isRequired
}).isRequired,
options: PropTypes.arrayOf(PropTypes.string.isRequired)
};

export default FontName;
32 changes: 32 additions & 0 deletions src/components/tw-fonts-modal/fonts-modal.css
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
@import "../../css/colors.css";
@import "../../css/z-index.css";

.modal-content {
max-width: 550px;
Expand Down Expand Up @@ -66,6 +67,9 @@

}

.font-input-outer {

}
.font-input {
width: 100%;
border: 1px solid $ui-black-transparent;
Expand All @@ -75,6 +79,34 @@
font: inherit;
}

.font-dropdown-outer {
position: absolute;
z-index: $z-index-modal;
background-color: white;
color: $text-primary;
border-radius: 0.25rem;
overflow: auto;
max-height: 300px;
border: 1px solid $ui-black-transparent;
box-sizing: border-box;
box-shadow: 0px 0px 8px 1px rgba(0, 0, 0, .3);
}
[theme="dark"] .font-dropdown-outer {
background-color: $ui-secondary;
}
.font-dropdown-item {
display: flex;
align-items: center;
padding: 0.5rem 0.75rem;
height: 1.5rem;
cursor: pointer;
transition: .1s ease;
}
.font-dropdown-item:hover {
background-color: $motion-primary;
color: #ffffff;
}

.font-playground {
background: none;
border: none;
Expand Down
20 changes: 20 additions & 0 deletions src/components/tw-security-manager-modal/data-url.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
@import "../../css/colors.css";

.code {
display: block;
width: 100%;
max-width: 100%;
min-width: 100%;
height: 5rem;
min-height: 3rem;
border: 1px solid $ui-black-transparent;
border-radius: 0.25rem;
padding: 0.25rem;
font-size: 0.875rem;
font-family: monospace;
margin: 0.5rem 0;
}
[theme="dark"] .code {
background: $ui-secondary;
color: white;
}
44 changes: 44 additions & 0 deletions src/components/tw-security-manager-modal/data-url.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import React from 'react';
import PropTypes from 'prop-types';
import styles from './data-url.css';

/**
* @param {string} dataURI data: URI
* @returns {string} A hopefully human-readable version
*/
const decodeDataURI = dataURI => {
const delimeter = dataURI.indexOf(',');
if (delimeter === -1) {
return dataURI;
}
const contentType = dataURI.substring(0, delimeter);
const data = dataURI.substring(delimeter + 1);
if (contentType.endsWith(';base64')) {
try {
return atob(data);
} catch (e) {
return dataURI;
}
}
try {
return decodeURIComponent(data);
} catch (e) {
return dataURI;
}
};

const DataURL = props => (
<textarea
className={styles.code}
value={decodeDataURI(props.url)}
readOnly
spellCheck={false}
autoComplete="off"
/>
);

DataURL.propTypes = {
url: PropTypes.string.isRequired
};

export default DataURL;
Loading

0 comments on commit e962b39

Please sign in to comment.