mirror of
https://github.com/Alamantus/Lexiconga.git
synced 2025-06-22 09:06:39 +02:00
Merge branch 'dev'
This commit is contained in:
commit
ee710988d5
17 changed files with 398 additions and 97 deletions
|
@ -45,6 +45,7 @@
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"babel-polyfill": "^6.26.0",
|
"babel-polyfill": "^6.26.0",
|
||||||
"bulma": "^0.6.2",
|
"bulma": "^0.6.2",
|
||||||
|
"bulma-checkradio": "^1.0.1",
|
||||||
"dexie": "^2.0.1",
|
"dexie": "^2.0.1",
|
||||||
"font-awesome": "^4.7.0",
|
"font-awesome": "^4.7.0",
|
||||||
"inferno": "^4.0.4",
|
"inferno": "^4.0.4",
|
||||||
|
|
|
@ -24,7 +24,10 @@ class User {
|
||||||
}
|
}
|
||||||
} else if (password_verify($password, $user['password'])) {
|
} else if (password_verify($password, $user['password'])) {
|
||||||
$this->db->execute('UPDATE users SET last_login=' . time() . ' WHERE id=' . $user['id']);
|
$this->db->execute('UPDATE users SET last_login=' . time() . ' WHERE id=' . $user['id']);
|
||||||
return $this->generateUserToken($user['id'], $user['current_dictionary']);
|
return array(
|
||||||
|
'token' => $this->generateUserToken($user['id'], $user['current_dictionary']),
|
||||||
|
'user' => $this->getUserData($user['id']),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
|
@ -60,13 +63,57 @@ VALUES (?, ?, ?, ?, ?, '. time() .')';
|
||||||
$new_dictionary = $this->dictionary->create($new_user_id);
|
$new_dictionary = $this->dictionary->create($new_user_id);
|
||||||
|
|
||||||
if ($new_dictionary !== false) {
|
if ($new_dictionary !== false) {
|
||||||
return $this->generateUserToken($new_user_id, $new_dictionary);
|
return array(
|
||||||
|
'token' => $this->generateUserToken($new_user_id, $new_dictionary),
|
||||||
|
'user' => $this->getUserData($new_user_id),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function setUserData ($token, $user_data) {
|
||||||
|
$token_data = $this->token->decode($token);
|
||||||
|
if ($token_data !== false) {
|
||||||
|
$query = 'UPDATE users SET email=?, public_name=?, username=?, allow_email=?, use_ipa=? WHERE id=?';
|
||||||
|
$properties = array(
|
||||||
|
$user_data['email'],
|
||||||
|
$user_data['publicName'],
|
||||||
|
$user_data['username'],
|
||||||
|
$user_data['allowEmail'],
|
||||||
|
$user_data['useIPAPronunciation'],
|
||||||
|
$user_id,
|
||||||
|
);
|
||||||
|
$update_success = $this->db->execute($query, $properties);
|
||||||
|
if ($update_success) {
|
||||||
|
return array(
|
||||||
|
'token' => $token,
|
||||||
|
'userData' => $user_data,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getUserData ($user_id) {
|
||||||
|
$query = 'SELECT * FROM users WHERE id=?';
|
||||||
|
$stmt = $this->db->query($query, array($user_id));
|
||||||
|
$user = $stmt->fetch();
|
||||||
|
if ($stmt && $user) {
|
||||||
|
return array(
|
||||||
|
'email' => $user['email'],
|
||||||
|
'username' => $user['username'],
|
||||||
|
'publicName' => $user['public_name'],
|
||||||
|
'allowEmails' => $user['allow_email'] == 1 ? true : false,
|
||||||
|
'useIPAPronunciation' => $user['use_ipa'] == 1 ? true : false,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
public function createNewDictionary ($token) {
|
public function createNewDictionary ($token) {
|
||||||
$user_data = $this->token->decode($token);
|
$user_data = $this->token->decode($token);
|
||||||
if ($user_data !== false) {
|
if ($user_data !== false) {
|
||||||
|
|
|
@ -12,10 +12,10 @@ switch ($action) {
|
||||||
case 'login': {
|
case 'login': {
|
||||||
if (isset($request['email']) && isset($request['password'])) {
|
if (isset($request['email']) && isset($request['password'])) {
|
||||||
$user = new User();
|
$user = new User();
|
||||||
$token = $user->logIn($request['email'], $request['password']);
|
$user_data = $user->logIn($request['email'], $request['password']);
|
||||||
if ($token !== false) {
|
if ($user_data !== false) {
|
||||||
return Response::json(array(
|
return Response::json(array(
|
||||||
'data' => $token,
|
'data' => $user_data,
|
||||||
'error' => false,
|
'error' => false,
|
||||||
), 200);
|
), 200);
|
||||||
}
|
}
|
||||||
|
@ -33,10 +33,10 @@ switch ($action) {
|
||||||
if (isset($request['email']) && isset($request['password'])) {
|
if (isset($request['email']) && isset($request['password'])) {
|
||||||
$user = new User();
|
$user = new User();
|
||||||
if (!$user->emailExists($request['email'])) {
|
if (!$user->emailExists($request['email'])) {
|
||||||
$token = $user->create($request['email'], $request['password'], $request['userData']);
|
$user_data = $user->create($request['email'], $request['password'], $request['userData']);
|
||||||
if ($token !== false) {
|
if ($user_data !== false) {
|
||||||
return Response::json(array(
|
return Response::json(array(
|
||||||
'data' => $token,
|
'data' => $user_data,
|
||||||
'error' => false,
|
'error' => false,
|
||||||
), 201);
|
), 201);
|
||||||
}
|
}
|
||||||
|
@ -103,6 +103,26 @@ switch ($action) {
|
||||||
'error' => true,
|
'error' => true,
|
||||||
), 403);
|
), 403);
|
||||||
}
|
}
|
||||||
|
case 'set-user-data': {
|
||||||
|
if ($token !== false && isset($request['userData'])) {
|
||||||
|
$user = new User();
|
||||||
|
$updated_user = $user->setUserData($token, $request['userData']);
|
||||||
|
if ($updated_user !== false) {
|
||||||
|
return Response::json(array(
|
||||||
|
'data' => $updated_user,
|
||||||
|
'error' => false,
|
||||||
|
), 200);
|
||||||
|
}
|
||||||
|
return Response::json(array(
|
||||||
|
'data' => 'Could not set user data: missing data',
|
||||||
|
'error' => true,
|
||||||
|
), 400);
|
||||||
|
}
|
||||||
|
return Response::json(array(
|
||||||
|
'data' => 'Could not get dictionaries: no token provided',
|
||||||
|
'error' => true,
|
||||||
|
), 403);
|
||||||
|
}
|
||||||
case 'create-new-dictionary': {
|
case 'create-new-dictionary': {
|
||||||
if ($token !== false) {
|
if ($token !== false) {
|
||||||
$user = new User();
|
$user = new User();
|
||||||
|
|
|
@ -16,8 +16,10 @@ export class MainDisplay extends Component {
|
||||||
|
|
||||||
PropTypes.checkPropTypes({
|
PropTypes.checkPropTypes({
|
||||||
dictionaryInfo: PropTypes.object.isRequired,
|
dictionaryInfo: PropTypes.object.isRequired,
|
||||||
|
isLoadingWords: PropTypes.bool,
|
||||||
wordsToDisplay: PropTypes.array.isRequired,
|
wordsToDisplay: PropTypes.array.isRequired,
|
||||||
wordsAreFiltered: PropTypes.bool,
|
wordsAreFiltered: PropTypes.bool,
|
||||||
|
wordsInCurrentList: PropTypes.number,
|
||||||
currentPage: PropTypes.number,
|
currentPage: PropTypes.number,
|
||||||
itemsPerPage: PropTypes.number,
|
itemsPerPage: PropTypes.number,
|
||||||
stats: PropTypes.object.isRequired,
|
stats: PropTypes.object.isRequired,
|
||||||
|
@ -55,8 +57,10 @@ export class MainDisplay extends Component {
|
||||||
render () {
|
render () {
|
||||||
const {
|
const {
|
||||||
dictionaryInfo,
|
dictionaryInfo,
|
||||||
|
isLoadingWords,
|
||||||
wordsToDisplay,
|
wordsToDisplay,
|
||||||
wordsAreFiltered,
|
wordsAreFiltered,
|
||||||
|
wordsInCurrentList,
|
||||||
currentPage,
|
currentPage,
|
||||||
itemsPerPage,
|
itemsPerPage,
|
||||||
stats,
|
stats,
|
||||||
|
@ -105,7 +109,16 @@ export class MainDisplay extends Component {
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
<Pagination
|
||||||
|
currentPage={ currentPage }
|
||||||
|
itemsPerPage={ itemsPerPage }
|
||||||
|
stats={ stats }
|
||||||
|
setPage={ setPage }
|
||||||
|
wordsInCurrentList={ wordsInCurrentList }
|
||||||
|
isTop />
|
||||||
|
|
||||||
<WordsList
|
<WordsList
|
||||||
|
isLoadingWords={ isLoadingWords }
|
||||||
words={ wordsToDisplay }
|
words={ wordsToDisplay }
|
||||||
adsEveryXWords={ 10 }
|
adsEveryXWords={ 10 }
|
||||||
updateDisplay={ updateDisplay } />
|
updateDisplay={ updateDisplay } />
|
||||||
|
@ -114,7 +127,8 @@ export class MainDisplay extends Component {
|
||||||
currentPage={currentPage}
|
currentPage={currentPage}
|
||||||
itemsPerPage={itemsPerPage}
|
itemsPerPage={itemsPerPage}
|
||||||
stats={stats}
|
stats={stats}
|
||||||
setPage={ setPage } />
|
setPage={setPage}
|
||||||
|
wordsInCurrentList={wordsInCurrentList} />
|
||||||
</RightColumn>
|
</RightColumn>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -13,6 +13,7 @@ export class WordsList extends Component {
|
||||||
super(props);
|
super(props);
|
||||||
|
|
||||||
PropTypes.checkPropTypes({
|
PropTypes.checkPropTypes({
|
||||||
|
isLoadingWords: PropTypes.bool,
|
||||||
adsEveryXWords: PropTypes.number,
|
adsEveryXWords: PropTypes.number,
|
||||||
words: PropTypes.array,
|
words: PropTypes.array,
|
||||||
updateDisplay: PropTypes.func.isRequired,
|
updateDisplay: PropTypes.func.isRequired,
|
||||||
|
@ -22,6 +23,12 @@ export class WordsList extends Component {
|
||||||
render () {
|
render () {
|
||||||
const adsEveryXWords = this.props.adsEveryXWords || 10;
|
const adsEveryXWords = this.props.adsEveryXWords || 10;
|
||||||
|
|
||||||
|
if (this.props.isLoadingWords) {
|
||||||
|
return <div className="content has-text-centered">
|
||||||
|
<div className="loader" style="display: inline-block; width: 60px; height: 60px;" />
|
||||||
|
</div>;
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className='box'>
|
<div className='box'>
|
||||||
|
|
||||||
|
|
94
src/components/management/AccountManager/MyAccount.jsx
Normal file
94
src/components/management/AccountManager/MyAccount.jsx
Normal file
|
@ -0,0 +1,94 @@
|
||||||
|
import Inferno from 'inferno';
|
||||||
|
import { Component } from 'inferno';
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
|
|
||||||
|
export class MyAccount extends Component {
|
||||||
|
constructor(props) {
|
||||||
|
super(props);
|
||||||
|
|
||||||
|
PropTypes.checkPropTypes({
|
||||||
|
email: PropTypes.string.isRequired,
|
||||||
|
username: PropTypes.string.isRequired,
|
||||||
|
publicName: PropTypes.string.isRequired,
|
||||||
|
allowEmails: PropTypes.bool.isRequired,
|
||||||
|
useIPAPronunciation: PropTypes.bool.isRequired,
|
||||||
|
userDictionaries: PropTypes.array.isRequired,
|
||||||
|
updateUserData: PropTypes.func,
|
||||||
|
changeDictionary: PropTypes.func,
|
||||||
|
}, props, 'prop', 'LoginForm');
|
||||||
|
|
||||||
|
this.state = {
|
||||||
|
email: this.props.email,
|
||||||
|
username: this.props.username,
|
||||||
|
publicName: this.props.publicName,
|
||||||
|
allowEmails: this.props.allowEmails,
|
||||||
|
useIPAPronunciation: this.props.useIPAPronunciation,
|
||||||
|
userDictionaries: this.props.userDictionaries,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<div className='columns'>
|
||||||
|
|
||||||
|
<div className='column'>
|
||||||
|
<h2 className='title'>Account Details</h2>
|
||||||
|
<div className='control'>
|
||||||
|
<strong>Email:</strong> <span>{this.state.email}</span>
|
||||||
|
</div>
|
||||||
|
<div className='control'>
|
||||||
|
<strong>Username:</strong> <span>{this.state.username}</span>
|
||||||
|
</div>
|
||||||
|
<div className='field'>
|
||||||
|
<label className='label'>
|
||||||
|
<span>Public Name:</span>
|
||||||
|
</label>
|
||||||
|
<div className='control'>
|
||||||
|
<input className='input' type='text' value={this.state.publicName}
|
||||||
|
onInput={(event) => {this.setState({publicName: event.target.value})}} />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className='field'>
|
||||||
|
<div className='control'>
|
||||||
|
<input className='is-checkradio is-rtl' type='checkbox' id='allowEmails'
|
||||||
|
checked={this.state.allowEmails ? 'checked' : false}
|
||||||
|
onChange={(event) => { this.setState({ allowEmails: !this.state.allowEmails }) }} />
|
||||||
|
<label className='label is-unselectable' htmlFor='allowEmails'>
|
||||||
|
Allow Emails
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className='field'>
|
||||||
|
<input className='is-checkradio is-rtl' type='checkbox' id='useIPAPronunciation'
|
||||||
|
checked={this.state.useIPAPronunciation ? 'checked' : false}
|
||||||
|
onChange={(event) => { this.setState({ useIPAPronunciation: !this.state.useIPAPronunciation }) }} />
|
||||||
|
<label className='label is-unselectable' htmlFor='useIPAPronunciation'>
|
||||||
|
Use IPA in Pronunciation Fields
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className='column'>
|
||||||
|
<h2 className='title'>Account Actions</h2>
|
||||||
|
<div className='field'>
|
||||||
|
<label className='label is-unselectable'>
|
||||||
|
<span>Change Dictionary</span>
|
||||||
|
</label>
|
||||||
|
<div className='control'>
|
||||||
|
<div className='select'>
|
||||||
|
<select>
|
||||||
|
{this.props.userDictionaries.map(item => {
|
||||||
|
return <option value={item.id}>{item.name}</option>;
|
||||||
|
})}
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
|
@ -6,9 +6,18 @@ import store from 'store';
|
||||||
|
|
||||||
import { Modal } from '../../structure/Modal';
|
import { Modal } from '../../structure/Modal';
|
||||||
import { LoginForm } from './LoginForm';
|
import { LoginForm } from './LoginForm';
|
||||||
|
import { MyAccount } from './MyAccount';
|
||||||
|
|
||||||
import { request } from '../../../Helpers';
|
import { request } from '../../../Helpers';
|
||||||
|
|
||||||
|
const defaultUserData = {
|
||||||
|
email: '',
|
||||||
|
username: '',
|
||||||
|
publicName: '',
|
||||||
|
allowEmails: true,
|
||||||
|
useIPAPronunciation: true,
|
||||||
|
};
|
||||||
|
|
||||||
export class AccountManager extends Component {
|
export class AccountManager extends Component {
|
||||||
constructor (props) {
|
constructor (props) {
|
||||||
super(props);
|
super(props);
|
||||||
|
@ -17,9 +26,21 @@ export class AccountManager extends Component {
|
||||||
updater: PropTypes.object.isRequired,
|
updater: PropTypes.object.isRequired,
|
||||||
}, props, 'prop', 'AccountManager');
|
}, props, 'prop', 'AccountManager');
|
||||||
|
|
||||||
|
const userData = store.get('LexicongaUserData');
|
||||||
|
|
||||||
this.state = {
|
this.state = {
|
||||||
isLoggedIn: false,
|
isLoggedIn: false,
|
||||||
|
userData: {
|
||||||
|
email: userData ? userData.email : defaultUserData.email,
|
||||||
|
username: userData ? userData.username : defaultUserData.username,
|
||||||
|
publicName: userData ? userData.publicName : defaultUserData.publicName,
|
||||||
|
allowEmails: userData ? userData.allowEmails : defaultUserData.allowEmails,
|
||||||
|
useIPAPronunciation: userData ? userData.useIPAPronunciation : defaultUserData.useIPAPronunciation,
|
||||||
|
},
|
||||||
|
userDictionaries: [],
|
||||||
};
|
};
|
||||||
|
|
||||||
|
this.getDictionaryNames();
|
||||||
}
|
}
|
||||||
|
|
||||||
logIn (email, password) {
|
logIn (email, password) {
|
||||||
|
@ -28,7 +49,12 @@ export class AccountManager extends Component {
|
||||||
|
|
||||||
logOut () {
|
logOut () {
|
||||||
store.remove('LexicongaToken');
|
store.remove('LexicongaToken');
|
||||||
this.setState({ isLoggedIn: false });
|
store.remove('LexicongaUserData');
|
||||||
|
this.setState({
|
||||||
|
isLoggedIn: false,
|
||||||
|
userData: Object.assign({}, defaultUserData),
|
||||||
|
userDictionaries: [],
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
signUp (email, password, userData) {
|
signUp (email, password, userData) {
|
||||||
|
@ -44,23 +70,69 @@ export class AccountManager extends Component {
|
||||||
if (error) {
|
if (error) {
|
||||||
console.error(data);
|
console.error(data);
|
||||||
} else {
|
} else {
|
||||||
store.set('LexicongaToken', data);
|
store.set('LexicongaToken', data.token);
|
||||||
this.setState({ isLoggedIn: true }, () => {
|
store.set('LexicongaUserData', data.user);
|
||||||
|
this.setState({
|
||||||
|
isLoggedIn: true,
|
||||||
|
userData: data.user,
|
||||||
|
}, () => {
|
||||||
|
this.getDictionaryNames();
|
||||||
this.props.updater.sync();
|
this.props.updater.sync();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
updateUserData (newUserData) {
|
||||||
|
const token = store.get('LexicongaToken');
|
||||||
|
|
||||||
|
if (token) {
|
||||||
|
store.set('LexicongaUserData', newUserData);
|
||||||
|
this.setState({ userData: newUserData }, () => {
|
||||||
|
request('set-user-data', { token, userData: newUserData }, (response) => {
|
||||||
|
const {data, error} = response;
|
||||||
|
if (error) {
|
||||||
|
console.error(data);
|
||||||
|
} else {
|
||||||
|
console.log(data);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
getDictionaryNames () {
|
||||||
|
const token = store.get('LexicongaToken');
|
||||||
|
|
||||||
|
if (token) {
|
||||||
|
return request('get-all-dictionary-names', { token }, (response) => {
|
||||||
|
const {data, error} = response;
|
||||||
|
if (error) {
|
||||||
|
console.error(data);
|
||||||
|
} else {
|
||||||
|
this.setState({ userDictionaries: data });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
render () {
|
render () {
|
||||||
const token = store.get('LexicongaToken');
|
const token = store.get('LexicongaToken');
|
||||||
|
|
||||||
if (token) {
|
if (token) {
|
||||||
|
const { userData } = this.state;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<Modal buttonText='Account' title='My Account'>
|
<Modal buttonText='Account' title='My Account' onShow={ this.getDictionaryNames.bind(this) }>
|
||||||
<div className='content has-text-left'>
|
<MyAccount
|
||||||
<p>Hello My Account!</p>
|
email={ userData.email }
|
||||||
</div>
|
username={ userData.username }
|
||||||
|
publicName={ userData.publicName }
|
||||||
|
allowEmails={ userData.allowEmails }
|
||||||
|
useIPAPronunciation={ userData.useIPAPronunciation }
|
||||||
|
userDictionaries={ this.state.userDictionaries }
|
||||||
|
updateUserData={ this.updateUserData.bind(this) }
|
||||||
|
changeDictionary={ () => {} } />
|
||||||
</Modal>
|
</Modal>
|
||||||
<a className='button' onClick={this.logOut.bind(this)}>
|
<a className='button' onClick={this.logOut.bind(this)}>
|
||||||
Log Out
|
Log Out
|
||||||
|
|
|
@ -30,17 +30,16 @@ export const EditSettingsForm = (props) => {
|
||||||
|
|
||||||
<div className='field'>
|
<div className='field'>
|
||||||
<div className='control'>
|
<div className='control'>
|
||||||
<label className='checkbox'>
|
<input className='is-checkradio is-rtl' type='checkbox' id='allowDuplicates'
|
||||||
|
defaultChecked={ allowDuplicates }
|
||||||
|
onChange={ (event) => {
|
||||||
|
editDictionaryModal.setState({
|
||||||
|
allowDuplicates: event.target.checked,
|
||||||
|
hasChanged: event.target.checked != editDictionaryModal.props.allowDuplicates,
|
||||||
|
});
|
||||||
|
}} />
|
||||||
|
<label class='label is-unselectable' htmlFor='allowDuplicates'>
|
||||||
Allow Duplicate Words
|
Allow Duplicate Words
|
||||||
{ ' ' }
|
|
||||||
<input type='checkbox'
|
|
||||||
defaultChecked={ allowDuplicates }
|
|
||||||
onChange={ (event) => {
|
|
||||||
editDictionaryModal.setState({
|
|
||||||
allowDuplicates: event.target.checked,
|
|
||||||
hasChanged: event.target.checked != editDictionaryModal.props.allowDuplicates,
|
|
||||||
});
|
|
||||||
}} />
|
|
||||||
</label>
|
</label>
|
||||||
<div className='help'>
|
<div className='help'>
|
||||||
Checking this box will allow any number of the exact same spelling of a word to be added
|
Checking this box will allow any number of the exact same spelling of a word to be added
|
||||||
|
@ -50,20 +49,19 @@ export const EditSettingsForm = (props) => {
|
||||||
|
|
||||||
<div className='field'>
|
<div className='field'>
|
||||||
<div className='control'>
|
<div className='control'>
|
||||||
<label className='checkbox'
|
<input className='is-checkradio is-rtl' type='checkbox' id='caseSensitive'
|
||||||
Disabled={ allowDuplicates ? 'disabled' : null }
|
defaultChecked={ caseSensitive }
|
||||||
title={ allowDuplicates ? 'Disabled because allowing duplicates makes this unnecessary' : null}>
|
disabled={ allowDuplicates }
|
||||||
|
onChange={ (event) => {
|
||||||
|
editDictionaryModal.setState({
|
||||||
|
caseSensitive: event.target.checked,
|
||||||
|
hasChanged: event.target.checked != editDictionaryModal.props.caseSensitive,
|
||||||
|
});
|
||||||
|
}} />
|
||||||
|
<label className='label is-unselectable' htmlFor='caseSensitive'
|
||||||
|
Disabled={allowDuplicates ? 'disabled' : null}
|
||||||
|
title={allowDuplicates ? 'Disabled because allowing duplicates makes this unnecessary' : null}>
|
||||||
Words are Case-Sensitive
|
Words are Case-Sensitive
|
||||||
{ ' ' }
|
|
||||||
<input type='checkbox'
|
|
||||||
defaultChecked={ caseSensitive }
|
|
||||||
disabled={ allowDuplicates }
|
|
||||||
onChange={ (event) => {
|
|
||||||
editDictionaryModal.setState({
|
|
||||||
caseSensitive: event.target.checked,
|
|
||||||
hasChanged: event.target.checked != editDictionaryModal.props.caseSensitive,
|
|
||||||
});
|
|
||||||
}} />
|
|
||||||
</label>
|
</label>
|
||||||
<div className='help'>
|
<div className='help'>
|
||||||
Checking this box will allow any words spelled the same but with different capitalization to be added.
|
Checking this box will allow any words spelled the same but with different capitalization to be added.
|
||||||
|
@ -73,17 +71,16 @@ export const EditSettingsForm = (props) => {
|
||||||
|
|
||||||
<div className='field'>
|
<div className='field'>
|
||||||
<div className='control'>
|
<div className='control'>
|
||||||
<label className='checkbox'>
|
<input className='is-checkradio is-rtl' type='checkbox' id='sortByDefinition'
|
||||||
|
defaultChecked={ sortByDefinition }
|
||||||
|
onChange={ (event) => {
|
||||||
|
editDictionaryModal.setState({
|
||||||
|
sortByDefinition: event.target.checked,
|
||||||
|
hasChanged: event.target.checked != editDictionaryModal.props.sortByDefinition,
|
||||||
|
});
|
||||||
|
}} />
|
||||||
|
<label className='label is-unselectable' htmlFor='sortByDefinition'>
|
||||||
Sort by Definition
|
Sort by Definition
|
||||||
{ ' ' }
|
|
||||||
<input type='checkbox'
|
|
||||||
defaultChecked={ sortByDefinition }
|
|
||||||
onChange={ (event) => {
|
|
||||||
editDictionaryModal.setState({
|
|
||||||
sortByDefinition: event.target.checked,
|
|
||||||
hasChanged: event.target.checked != editDictionaryModal.props.sortByDefinition,
|
|
||||||
});
|
|
||||||
}} />
|
|
||||||
</label>
|
</label>
|
||||||
<div className='help'>
|
<div className='help'>
|
||||||
Checking this box will sort the words in alphabetical order based on the Definition instead of the Word.
|
Checking this box will sort the words in alphabetical order based on the Definition instead of the Word.
|
||||||
|
@ -93,20 +90,19 @@ export const EditSettingsForm = (props) => {
|
||||||
|
|
||||||
<div className='field'>
|
<div className='field'>
|
||||||
<div className='control'>
|
<div className='control'>
|
||||||
<label className='checkbox'>
|
<input className='is-checkradio is-rtl' type='checkbox' id='isComplete'
|
||||||
|
defaultChecked={ isComplete }
|
||||||
|
onChange={ (event) => {
|
||||||
|
editDictionaryModal.setState({
|
||||||
|
isComplete: event.target.checked,
|
||||||
|
hasChanged: event.target.checked != editDictionaryModal.props.isComplete,
|
||||||
|
});
|
||||||
|
}} />
|
||||||
|
<label className='label is-unselectable' htmlFor='isComplete'>
|
||||||
Mark Complete
|
Mark Complete
|
||||||
{ ' ' }
|
|
||||||
<input type='checkbox'
|
|
||||||
defaultChecked={ isComplete }
|
|
||||||
onChange={ (event) => {
|
|
||||||
editDictionaryModal.setState({
|
|
||||||
isComplete: event.target.checked,
|
|
||||||
hasChanged: event.target.checked != editDictionaryModal.props.isComplete,
|
|
||||||
});
|
|
||||||
}} />
|
|
||||||
</label>
|
</label>
|
||||||
<div className='help'>
|
<div className='help'>
|
||||||
Checking this box will mark your { specification } as "complete" and prevent any changes from being made.
|
Checking this box will mark your { specification } as 'complete' and prevent any changes from being made.
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -115,17 +111,16 @@ export const EditSettingsForm = (props) => {
|
||||||
&& (
|
&& (
|
||||||
<div className='field'>
|
<div className='field'>
|
||||||
<div className='control'>
|
<div className='control'>
|
||||||
<label className='checkbox'>
|
<input className='is-checkradio is-rtl' type='checkbox' id='isPublic'
|
||||||
|
defaultChecked={ isPublic }
|
||||||
|
onChange={ (event) => {
|
||||||
|
editDictionaryModal.setState({
|
||||||
|
isPublic: event.target.checked,
|
||||||
|
hasChanged: event.target.checked != editDictionaryModal.props.isPublic,
|
||||||
|
});
|
||||||
|
}} />
|
||||||
|
<label className='label is-unselectable' htmlFor='isPublic'>
|
||||||
Make Public
|
Make Public
|
||||||
{ ' ' }
|
|
||||||
<input type='checkbox'
|
|
||||||
defaultChecked={ isPublic }
|
|
||||||
onChange={ (event) => {
|
|
||||||
editDictionaryModal.setState({
|
|
||||||
isPublic: event.target.checked,
|
|
||||||
hasChanged: event.target.checked != editDictionaryModal.props.isPublic,
|
|
||||||
});
|
|
||||||
}} />
|
|
||||||
</label>
|
</label>
|
||||||
<div className='help'>
|
<div className='help'>
|
||||||
Checking this box will make your { specification } as public.
|
Checking this box will make your { specification } as public.
|
||||||
|
|
|
@ -13,6 +13,7 @@ const DISPLAY = {
|
||||||
DETAILS: 1,
|
DETAILS: 1,
|
||||||
LINGUISTICS: 2,
|
LINGUISTICS: 2,
|
||||||
SETTINGS: 3,
|
SETTINGS: 3,
|
||||||
|
ACTIONS: 4,
|
||||||
}
|
}
|
||||||
|
|
||||||
export class EditDictionaryModal extends Component {
|
export class EditDictionaryModal extends Component {
|
||||||
|
@ -58,6 +59,8 @@ export class EditDictionaryModal extends Component {
|
||||||
|
|
||||||
hasChanged: false,
|
hasChanged: false,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.modal = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
hasChanged () {
|
hasChanged () {
|
||||||
|
@ -126,6 +129,14 @@ export class EditDictionaryModal extends Component {
|
||||||
);
|
);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
case DISPLAY.ACTIONS : {
|
||||||
|
displayJSX = (
|
||||||
|
<div class="content">
|
||||||
|
<p>Actions like import, export, delete, etc.</p>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
@ -135,7 +146,7 @@ export class EditDictionaryModal extends Component {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
save () {
|
save (callback = () => {}) {
|
||||||
const updatedDetails = {};
|
const updatedDetails = {};
|
||||||
|
|
||||||
if (this.state.name !== this.props.name) {
|
if (this.state.name !== this.props.name) {
|
||||||
|
@ -243,7 +254,7 @@ export class EditDictionaryModal extends Component {
|
||||||
this.setState({ hasChanged: false }, () => {
|
this.setState({ hasChanged: false }, () => {
|
||||||
// If setting that alters word display is changed, update the display.
|
// If setting that alters word display is changed, update the display.
|
||||||
if (updatedDetails.hasOwnProperty('sortByDefinition')) {
|
if (updatedDetails.hasOwnProperty('sortByDefinition')) {
|
||||||
this.props.updateDisplay();
|
this.props.updateDisplay(callback);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
})
|
})
|
||||||
|
@ -269,9 +280,16 @@ export class EditDictionaryModal extends Component {
|
||||||
>
|
>
|
||||||
Save
|
Save
|
||||||
</button>
|
</button>
|
||||||
|
<button className='button'
|
||||||
|
disabled={ !hasChanged }
|
||||||
|
onClick={ () => this.save(this.modal.hide())}
|
||||||
|
>
|
||||||
|
Save & Close
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
ref={(modal) => this.modal = modal}
|
||||||
>
|
>
|
||||||
|
|
||||||
{!settings.isComplete
|
{!settings.isComplete
|
||||||
|
@ -293,6 +311,11 @@ export class EditDictionaryModal extends Component {
|
||||||
Settings
|
Settings
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
|
<li className={ (currentDisplay === DISPLAY.ACTIONS) ? 'is-active' : null }>
|
||||||
|
<a onClick={ this.toggleDisplay.bind(this, DISPLAY.ACTIONS) }>
|
||||||
|
Actions
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
),
|
),
|
||||||
|
|
|
@ -23,7 +23,7 @@ export class SearchBox extends Component {
|
||||||
searchTerm: '',
|
searchTerm: '',
|
||||||
caseSensitive: false,
|
caseSensitive: false,
|
||||||
ignoreDiacritics: false,
|
ignoreDiacritics: false,
|
||||||
filteredPartsOfSpeech: [...props.partsOfSpeech, 'Uncategorized'],
|
filteredPartsOfSpeech: this.partsOfSpeechForFilter,
|
||||||
showHeader: false,
|
showHeader: false,
|
||||||
showAdvanced: false,
|
showAdvanced: false,
|
||||||
};
|
};
|
||||||
|
@ -62,7 +62,7 @@ export class SearchBox extends Component {
|
||||||
}
|
}
|
||||||
|
|
||||||
togglePartOfSpeech (event) {
|
togglePartOfSpeech (event) {
|
||||||
const uniquePartsOfSpeech = new Set(this.partsOfSpeechForFilter);
|
const uniquePartsOfSpeech = new Set(this.state.filteredPartsOfSpeech);
|
||||||
if (event.target.checked) {
|
if (event.target.checked) {
|
||||||
uniquePartsOfSpeech.add(event.target.value);
|
uniquePartsOfSpeech.add(event.target.value);
|
||||||
} else {
|
} else {
|
||||||
|
@ -111,7 +111,7 @@ export class SearchBox extends Component {
|
||||||
this.searchBox = input;
|
this.searchBox = input;
|
||||||
}}
|
}}
|
||||||
value={ this.state.searchTerm }
|
value={ this.state.searchTerm }
|
||||||
onChange={(event) => {
|
onInput={(event) => {
|
||||||
this.setState({ searchTerm: event.target.value.trim() }, () => this.search());
|
this.setState({ searchTerm: event.target.value.trim() }, () => this.search());
|
||||||
}} />
|
}} />
|
||||||
<span className='icon is-small is-left'>
|
<span className='icon is-small is-left'>
|
||||||
|
@ -314,6 +314,8 @@ export class SearchBox extends Component {
|
||||||
showHeader () {
|
showHeader () {
|
||||||
this.setState({
|
this.setState({
|
||||||
showHeader: true,
|
showHeader: true,
|
||||||
|
}, () => {
|
||||||
|
this.searchBox.focus();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -109,7 +109,7 @@ export class WordForm extends Component {
|
||||||
<input className={ `input${(!this.state.nameIsValid) ? ' is-danger' : ''}` }
|
<input className={ `input${(!this.state.nameIsValid) ? ' is-danger' : ''}` }
|
||||||
type='text' placeholder='Required'
|
type='text' placeholder='Required'
|
||||||
value={ this.state.wordName }
|
value={ this.state.wordName }
|
||||||
onChange={(event) => {
|
onInput={(event) => {
|
||||||
this.setState({ wordName: event.target.value });
|
this.setState({ wordName: event.target.value });
|
||||||
}} />
|
}} />
|
||||||
{(!this.state.nameIsValid)
|
{(!this.state.nameIsValid)
|
||||||
|
@ -149,7 +149,7 @@ export class WordForm extends Component {
|
||||||
<input className={ `input${(!this.state.definitionIsValid) ? ' is-danger' : ''}` }
|
<input className={ `input${(!this.state.definitionIsValid) ? ' is-danger' : ''}` }
|
||||||
type='text' placeholder='Equivalent word(s)'
|
type='text' placeholder='Equivalent word(s)'
|
||||||
value={ this.state.wordDefinition }
|
value={ this.state.wordDefinition }
|
||||||
onChange={(event) => {
|
onInput={(event) => {
|
||||||
this.setState({ wordDefinition: event.target.value })
|
this.setState({ wordDefinition: event.target.value })
|
||||||
}} />
|
}} />
|
||||||
{(!this.state.definitionIsValid)
|
{(!this.state.definitionIsValid)
|
||||||
|
|
|
@ -13,6 +13,8 @@ export class Modal extends Component {
|
||||||
children: PropTypes.oneOfType([PropTypes.object, PropTypes.array]),
|
children: PropTypes.oneOfType([PropTypes.object, PropTypes.array]),
|
||||||
footerAlign: PropTypes.string,
|
footerAlign: PropTypes.string,
|
||||||
footerContent: PropTypes.oneOfType([PropTypes.object, PropTypes.array]),
|
footerContent: PropTypes.oneOfType([PropTypes.object, PropTypes.array]),
|
||||||
|
onShow: PropTypes.func,
|
||||||
|
onHide: PropTypes.func,
|
||||||
}, props, 'prop', 'Modal');
|
}, props, 'prop', 'Modal');
|
||||||
|
|
||||||
this.state = {
|
this.state = {
|
||||||
|
@ -23,12 +25,20 @@ export class Modal extends Component {
|
||||||
show () {
|
show () {
|
||||||
this.setState({
|
this.setState({
|
||||||
isVisible: true,
|
isVisible: true,
|
||||||
|
}, () => {
|
||||||
|
if (this.props.onShow) {
|
||||||
|
this.props.onShow();
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
hide () {
|
hide () {
|
||||||
this.setState({
|
this.setState({
|
||||||
isVisible: false,
|
isVisible: false,
|
||||||
|
}, () => {
|
||||||
|
if (this.props.onHide) {
|
||||||
|
this.props.onHide();
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,24 +1,25 @@
|
||||||
import Inferno from 'inferno';
|
import Inferno from 'inferno';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
|
|
||||||
|
import './styles.scss';
|
||||||
|
|
||||||
export const Pagination = (props) => {
|
export const Pagination = (props) => {
|
||||||
PropTypes.checkPropTypes({
|
PropTypes.checkPropTypes({
|
||||||
currentPage: PropTypes.number.isRequired,
|
currentPage: PropTypes.number.isRequired,
|
||||||
itemsPerPage: PropTypes.number.isRequired,
|
itemsPerPage: PropTypes.number.isRequired,
|
||||||
stats: PropTypes.object.isRequired,
|
stats: PropTypes.object.isRequired,
|
||||||
setPage: PropTypes.func.isRequired,
|
setPage: PropTypes.func.isRequired,
|
||||||
|
wordsInCurrentList: PropTypes.number,
|
||||||
|
isTop: PropTypes.bool,
|
||||||
}, props, 'prop', 'Pagination');
|
}, props, 'prop', 'Pagination');
|
||||||
|
|
||||||
const { currentPage, itemsPerPage, stats, setPage } = props;
|
const { currentPage, itemsPerPage, stats, setPage, wordsInCurrentList, isTop } = props;
|
||||||
|
|
||||||
const totalWords = stats.hasOwnProperty('numberOfWords')
|
|
||||||
? stats.numberOfWords.find(group => group.name === 'Total').value : null;
|
|
||||||
|
|
||||||
if (totalWords === null) {
|
if (wordsInCurrentList === null) {
|
||||||
return <div className="loader"></div>;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
const lastPage = Math.floor(totalWords / itemsPerPage);
|
const lastPage = Math.floor(wordsInCurrentList / itemsPerPage);
|
||||||
const nextPage = currentPage + 1 > lastPage ? lastPage : currentPage + 1;
|
const nextPage = currentPage + 1 > lastPage ? lastPage : currentPage + 1;
|
||||||
const prevPage = currentPage - 1 < 0 ? 0 : currentPage - 1;
|
const prevPage = currentPage - 1 < 0 ? 0 : currentPage - 1;
|
||||||
|
|
||||||
|
@ -29,7 +30,7 @@ export const Pagination = (props) => {
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<nav className="pagination is-centered" role="navigation" aria-label="pagination">
|
<nav className={`pagination is-centered ${isTop ? 'is-top' : ''}`} role="navigation" aria-label="pagination">
|
||||||
<a className="pagination-previous" aria-label="Goto page 1"
|
<a className="pagination-previous" aria-label="Goto page 1"
|
||||||
Disabled={currentPage === 0 ? 'disabled' : null}
|
Disabled={currentPage === 0 ? 'disabled' : null}
|
||||||
onClick={() => changePage(prevPage)}>
|
onClick={() => changePage(prevPage)}>
|
3
src/components/structure/Pagination/styles.scss
Normal file
3
src/components/structure/Pagination/styles.scss
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
.pagination.is-top {
|
||||||
|
margin-bottom: 15px;
|
||||||
|
}
|
|
@ -46,6 +46,8 @@ class App extends Component {
|
||||||
ignoreDiacritics: false,
|
ignoreDiacritics: false,
|
||||||
filteredPartsOfSpeech: [...dictionary.partsOfSpeech, 'Uncategorized'],
|
filteredPartsOfSpeech: [...dictionary.partsOfSpeech, 'Uncategorized'],
|
||||||
},
|
},
|
||||||
|
isLoadingWords: true,
|
||||||
|
wordsInCurrentList: null,
|
||||||
}
|
}
|
||||||
|
|
||||||
this.updater = new Updater(this, dictionary);
|
this.updater = new Updater(this, dictionary);
|
||||||
|
@ -98,7 +100,7 @@ class App extends Component {
|
||||||
const partsOfSpeechForFilter = [...partsOfSpeech, 'Uncategorized'];
|
const partsOfSpeechForFilter = [...partsOfSpeech, 'Uncategorized'];
|
||||||
const pageStart = currentPage * itemsPerPage;
|
const pageStart = currentPage * itemsPerPage;
|
||||||
const pageEnd = pageStart + itemsPerPage;
|
const pageEnd = pageStart + itemsPerPage;
|
||||||
let displayedWords;
|
let displayedWords = words;
|
||||||
if (this.isUsingFilter) {
|
if (this.isUsingFilter) {
|
||||||
const {
|
const {
|
||||||
searchingIn,
|
searchingIn,
|
||||||
|
@ -109,10 +111,7 @@ class App extends Component {
|
||||||
filteredPartsOfSpeech
|
filteredPartsOfSpeech
|
||||||
} = searchConfig;
|
} = searchConfig;
|
||||||
|
|
||||||
displayedWords = words.filter((word, index) => {
|
displayedWords = displayedWords.filter((word, index) => {
|
||||||
if (index < pageStart || index >= pageEnd) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
const wordPartOfSpeech = word.partOfSpeech === '' ? 'Uncategorized' : word.partOfSpeech;
|
const wordPartOfSpeech = word.partOfSpeech === '' ? 'Uncategorized' : word.partOfSpeech;
|
||||||
if (!filteredPartsOfSpeech.includes(wordPartOfSpeech)) {
|
if (!filteredPartsOfSpeech.includes(wordPartOfSpeech)) {
|
||||||
return false;
|
return false;
|
||||||
|
@ -168,30 +167,36 @@ class App extends Component {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
} else {
|
|
||||||
displayedWords = words.filter((word, index) => {
|
|
||||||
if (index < pageStart || index >= pageEnd) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const wordsInCurrentList = displayedWords.length;
|
||||||
|
|
||||||
|
displayedWords = displayedWords.filter((word, index) => {
|
||||||
|
if (index < pageStart || index >= pageEnd) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
|
||||||
this.setState({
|
this.setState({
|
||||||
displayedWords,
|
displayedWords,
|
||||||
stats: getWordsStats(words, partsOfSpeech, this.state.settings.caseSensitive),
|
stats: getWordsStats(words, partsOfSpeech, this.state.settings.caseSensitive),
|
||||||
|
wordsInCurrentList,
|
||||||
|
isLoadingWords: false,
|
||||||
}, () => callback());
|
}, () => callback());
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
search (searchConfig) {
|
search (searchConfig) {
|
||||||
this.setState({
|
this.setState({
|
||||||
|
isLoadingWords: true,
|
||||||
searchConfig: searchConfig,
|
searchConfig: searchConfig,
|
||||||
}, () => this.updateDisplayedWords());
|
}, () => this.updateDisplayedWords());
|
||||||
}
|
}
|
||||||
|
|
||||||
setPage (newPage) {
|
setPage (newPage) {
|
||||||
this.setState({
|
this.setState({
|
||||||
|
isLoadingWords: true,
|
||||||
currentPage: newPage,
|
currentPage: newPage,
|
||||||
}, () => this.updateDisplayedWords());
|
}, () => this.updateDisplayedWords());
|
||||||
}
|
}
|
||||||
|
@ -207,8 +212,10 @@ class App extends Component {
|
||||||
|
|
||||||
<MainDisplay
|
<MainDisplay
|
||||||
dictionaryInfo={ this.dictionaryInfo }
|
dictionaryInfo={ this.dictionaryInfo }
|
||||||
|
isLoadingWords={ this.state.isLoadingWords }
|
||||||
wordsToDisplay={ this.state.displayedWords }
|
wordsToDisplay={ this.state.displayedWords }
|
||||||
wordsAreFiltered={ this.isUsingFilter }
|
wordsAreFiltered={ this.isUsingFilter }
|
||||||
|
wordsInCurrentList={ this.state.wordsInCurrentList }
|
||||||
currentPage={ this.state.currentPage }
|
currentPage={ this.state.currentPage }
|
||||||
itemsPerPage={ this.state.itemsPerPage }
|
itemsPerPage={ this.state.itemsPerPage }
|
||||||
stats={ this.state.stats }
|
stats={ this.state.stats }
|
||||||
|
|
|
@ -5,6 +5,7 @@ $fa-font-path: "~font-awesome/fonts/";
|
||||||
|
|
||||||
@import 'bulma-overrides';
|
@import 'bulma-overrides';
|
||||||
@import '../../node_modules/bulma/bulma';
|
@import '../../node_modules/bulma/bulma';
|
||||||
|
@import '../../node_modules/bulma-checkradio/dist/bulma-checkradio';
|
||||||
|
|
||||||
@import '../../node_modules/sweetalert2/dist/sweetalert2';
|
@import '../../node_modules/sweetalert2/dist/sweetalert2';
|
||||||
|
|
||||||
|
|
|
@ -1002,6 +1002,10 @@ builtin-status-codes@^3.0.0:
|
||||||
version "3.0.0"
|
version "3.0.0"
|
||||||
resolved "https://registry.yarnpkg.com/builtin-status-codes/-/builtin-status-codes-3.0.0.tgz#85982878e21b98e1c66425e03d0174788f569ee8"
|
resolved "https://registry.yarnpkg.com/builtin-status-codes/-/builtin-status-codes-3.0.0.tgz#85982878e21b98e1c66425e03d0174788f569ee8"
|
||||||
|
|
||||||
|
bulma-checkradio@^1.0.1:
|
||||||
|
version "1.0.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/bulma-checkradio/-/bulma-checkradio-1.0.1.tgz#21674a9aee65c48e4028594bd0f22e5b52ea5346"
|
||||||
|
|
||||||
bulma@^0.6.2:
|
bulma@^0.6.2:
|
||||||
version "0.6.2"
|
version "0.6.2"
|
||||||
resolved "https://registry.yarnpkg.com/bulma/-/bulma-0.6.2.tgz#f4b1d11d5acc51a79644eb0a2b0b10649d3d71f5"
|
resolved "https://registry.yarnpkg.com/bulma/-/bulma-0.6.2.tgz#f4b1d11d5acc51a79644eb0a2b0b10649d3d71f5"
|
||||||
|
|
Loading…
Add table
Reference in a new issue