Skip to content

Commit

Permalink
Timeline WYSIWYG port.
Browse files Browse the repository at this point in the history
This commit squashed:
* Prevent Timeline from updating when changing the description or title.
* Remove old shortcode references.
* Address feedback from PR review. Encapsulate Timeline node in its own component.
* Added debounce for the Sheet URL input.
* Add dangerouslySetInnerHTML to the description.
* Nicer warning for empty URL
* Remove Edit HTML option. Match defaults to PHP.
* Address review feedback.
* Moving hooks to components folder
* Minor improvements to the script and style loader hooks
* Add HTMLSidebarHelp component
  • Loading branch information
pablocubico committed Oct 24, 2020
1 parent 1cf1e19 commit 5cf0180
Show file tree
Hide file tree
Showing 15 changed files with 499 additions and 447 deletions.
298 changes: 52 additions & 246 deletions assets/src/blocks/Timeline/Timeline.js
Original file line number Diff line number Diff line change
@@ -1,247 +1,53 @@
import {RawHTML, Component, Fragment} from '@wordpress/element';
import {
CheckboxControl,
TextControl as BaseTextControl,
TextareaControl as BaseTextareaControl,
SelectControl,
ServerSideRender
} from '@wordpress/components';
import {Preview} from '../../components/Preview';
import withCharacterCounter from '../../components/withCharacterCounter/withCharacterCounter';
import {URLInput} from "../../components/URLInput/URLInput";

const TextControl = withCharacterCounter( BaseTextControl );
const TextareaControl = withCharacterCounter( BaseTextareaControl );

export class Timeline extends Component {
constructor(props) {
super(props);
}

componentDidMount() {
this.loadTimeline(this.props);
}

componentDidUpdate(prevProps) {
this.loadTimeline(prevProps);
}

loadTimeline(data) {
const timelinejs_version = '3.6.3';

let js = 'https://cdn.knightlab.com/libs/timeline3/' + timelinejs_version + '/js/timeline-min.js';
let scriptLoaded = this.loadScriptAsync(js);
scriptLoaded.then(function () {
new TL.Timeline('timeline-1', data.google_sheets_url, {
"timenav_position": data.timenav_position,
"start_at_end": data.start_at_end,
"language": data.language
});
}.bind(this));
}

loadScriptAsync(uri) {
return new Promise((resolve, reject) => {
let tag = document.createElement('script');
tag.src = uri;
tag.async = true;
tag.onload = () => {
resolve();
};
let body = document.getElementsByTagName('body')[0];
body.appendChild(tag);
});
};

renderEdit() {
const {__} = wp.i18n;

const position = [
{label: 'Bottom', value: 'bottom'},
{label: 'Top', value: 'top'},
]

const languages = [
{label: 'Afrikaans', value: 'af'},
{label: 'Arabic', value: 'ar'},
{label: 'Armenian', value: 'hy'},
{label: 'Basque', value: 'eu'},
{label: 'Belarusian', value: 'be'},
{label: 'Bulgarian', value: 'bg'},
{label: 'Catalan', value: 'ca'},
{label: 'Chinese', value: 'zh-cn'},
{label: 'Croatian / Hrvatski', value: 'hr'},
{label: 'Czech', value: 'cz'},
{label: 'Danish', value: 'da'},
{label: 'Dutch', value: 'nl'},
{label: 'English', value: 'en'},
{label: 'English (24-hour time)', value: 'en-24hr'},
{label: 'Esperanto', value: 'eo'},
{label: 'Estonian', value: 'et'},
{label: 'Faroese', value: 'fo'},
{label: 'Farsi', value: 'fa'},
{label: 'Finnish', value: 'fi'},
{label: 'French', value: 'fr'},
{label: 'Frisian', value: 'fy'},
{label: 'Galician', value: 'gl'},
{label: 'Georgian', value: 'ka'},
{label: 'German / Deutsch', value: 'de'},
{label: 'Greek', value: 'el'},
{label: 'Hebrew', value: 'he'},
{label: 'Hindi', value: 'hi'},
{label: 'Hungarian', value: 'hu'},
{label: 'Icelandic', value: 'is'},
{label: 'Indonesian', value: 'id'},
{label: 'Irish', value: 'ga'},
{label: 'Italian', value: 'it'},
{label: 'Japanese', value: 'ja'},
{label: 'Korean', value: 'ko'},
{label: 'Latvian', value: 'lv'},
{label: 'Lithuanian', value: 'lt'},
{label: 'Luxembourgish', value: 'lb'},
{label: 'Malay', value: 'ms'},
{label: 'Myanmar', value: 'my'},
{label: 'Nepali', value: 'ne'},
{label: 'Norwegian', value: 'no'},
{label: 'Polish', value: 'pl'},
{label: 'Portuguese', value: 'pt'},
{label: 'pt-br', value: 'Portuguese (Brazilian)'},
{label: 'Romanian', value: 'ro'},
{label: 'Romansh', value: 'rm'},
{label: 'Russian', value: 'ru'},
{label: 'Serbian - Cyrillic', value: 'sr-cy'},
{label: 'Serbian - Latin', value: 'sr'},
{label: 'Sinhalese', value: 'si'},
{label: 'Slovak', value: 'sk'},
{label: 'Slovenian', value: 'sl'},
{label: 'Spanish', value: 'es'},
{label: 'Swedish', value: 'sv'},
{label: 'Tagalog', value: 'tl'},
{label: 'Tamil', value: 'ta'},
{label: 'Taiwanese', value: 'zh-tw'},
{label: 'Telugu', value: 'te'},
{label: 'Thai', value: 'th'},
{label: 'Turkish', value: 'tr'},
{label: 'Ukrainian', value: 'uk'},
{label: 'Urdu', value: 'ur'},
]

let url_desc = __(
'Enter the URL of the Google Sheets spreadsheet containing your timeline data.',
'p4ge'
);
url_desc += '<br><a href="https://timeline.knightlab.com/#make" target="_blank" rel="noopener noreferrer">';
url_desc += __(
'See the TimelineJS website for a template GSheet.',
'p4ge'
);
url_desc += '</a><br>';
url_desc += __(
'Copy this, add your own timeline data, and publish to the web.',
'p4ge'
);
url_desc += '<br>';
url_desc += __(
"Once you have done so, use the URL from your address bar (not the one provided in Google's 'publish to web' dialog).",
'p4ge'
);

return (
<Fragment>
<div>
<h2>{__('Timeline options', 'p4ge')}</h2>
<p><i>{__(
'Display a timeline from a Google Sheet',
'p4ge'
)}</i></p>

<div>
<TextControl
label={__('Timeline Title', 'p4ge')}
placeholder={__('Enter title', 'p4ge')}
value={this.props.timeline_title}
onChange={this.props.onTimelineTitleChange}
characterLimit={60}
/>
</div>

<div>
<TextareaControl
label={__('Description', 'p4ge')}
placeholder={__('Enter description', 'p4ge')}
value={this.props.description}
onChange={this.props.onDescriptionChange}
characterLimit={400}
/>
</div>

<div>
<URLInput
label={__('Google Sheets URL', 'p4ge')}
placeholder={__('Enter URL', 'p4ge')}
help=<RawHTML>{url_desc}</RawHTML>
value={this.props.google_sheets_url}
onChange={this.props.onGoogleSheetsUrlChange}
/>
</div>

<div>
<SelectControl
label={__('Language', 'p4ge')}
value={this.props.language}
options={languages}
onChange={(e) => this.props.onLanguageChange(e)}
/>
</div>

<div>
<SelectControl
label={__('Timeline navigation position', 'p4ge')}
value={this.props.timenav_position}
options={position}
onChange={(e) => this.props.onTimenavPositionChange(e)}
/>
</div>

<div>
<CheckboxControl
label={__('Start at end', 'p4ge')}
help={__('Begin at the end of the timeline', 'p4ge')}
value={this.props.start_at_end}
checked={this.props.start_at_end}
onChange={(e) => this.props.onStartAtEndChange(e)}
/>
</div>

</div>
</Fragment>
);
}

render() {
return (
<div>
{
this.props.isSelected
? this.renderEdit()
: null
}
<Preview showBar={this.props.isSelected}>
<ServerSideRender
block={'planet4-blocks/timeline'}
attributes={{
timeline_title: this.props.timeline_title,
description: this.props.description,
google_sheets_url: this.props.google_sheets_url,
language: this.props.language,
timenav_position: this.props.timenav_position,
start_at_end: this.props.start_at_end,
}}
urlQueryArgs={{post_id: document.querySelector('#post_ID').value}}
>
</ServerSideRender>
</Preview>
</div>
);
};
import { useScript } from '../../components/useScript/useScript';
import { useStyleSheet } from '../../components/useStyleSheet/useStyleSheet';
import { useRef, useEffect } from 'react';
import { uniqueId } from 'lodash';

const TIMELINE_JS_VERSION = '3.6.6';

export const Timeline = (props) => {
const {
google_sheets_url,
timenav_position,
start_at_end,
language
} = props;

const timelineNode = useRef(null);

const [stylesLoaded, stylesError] = useStyleSheet(
`https://cdnjs.cloudflare.com/ajax/libs/timelinejs/${TIMELINE_JS_VERSION}/css/timeline.css`
);

const setupTimeline = function() {
timelineNode.current.id = uniqueId('timeline');

new TL.Timeline(timelineNode.current.id, google_sheets_url, {
'timenav_position': timenav_position,
'start_at_end': start_at_end,
'language': language
});
}

const [scriptLoaded, scriptError] = useScript(
`https://cdnjs.cloudflare.com/ajax/libs/timelinejs/${TIMELINE_JS_VERSION}/js/timeline-min.js`
);

useEffect(
() => {
if (stylesLoaded && scriptLoaded) {
setupTimeline();
}
},
[
stylesLoaded,
scriptLoaded,
start_at_end,
google_sheets_url,
timenav_position,
language,
],
);

return <div ref={ timelineNode }></div>
}
Loading

0 comments on commit 5cf0180

Please sign in to comment.