bharbron / hexbook-semantic Goto Github PK
View Code? Open in Web Editor NEWAn Electron-React-Redux-Semantic-UI application for automatically generating a hexcrawl campaign notebook from random tables.
An Electron-React-Redux-Semantic-UI application for automatically generating a hexcrawl campaign notebook from random tables.
On the Tables screen, add a dropdown menu to each table card with a "Delete this table" option. Add an event handler to that menu option to trigger deletion of the table.
From the perspective of the store, deleting a table should do the following:
entities.tables.byId[<id of the deleted table>]
to undefined
entities.tables.allIds
allIds
arrays or anything. Remove them if it straightforward to do so, but it is not a requirement.Stumbled across something recently. Apparently chrome/chromium/headless-chrome has a build in --print-to-pdf
option which is designed to print/save an HTML page as a PDF. More importantly, it sounds like it might support some of the the CSS "printed media" options such as page breaks and columns.
This might not be as good as PrinceXML when it comes to "nice" layout (avoiding mid-block page breaks, etc.), but might be a much easier and "good enough" option to explore. And since ElectronJS runs inside an instance of Chromium, its an option that should be effectively "built-in" for us.
Do some investigation. Mockup some examples. etc.
We should move all plugin templates out of src/components/templateplugins.js
and into their own folder src/plugins/templates/
. We should also make each template plugin type a separate file, e.g. index.js
, randomtable.js
, etc.
In the table details screen, implement a way of clicking checkboxes to select multiple table entries, or clicking the checkbox at the top of the datatable to select all table entries. Add a dropdown menu with a "Delete selected entryies" option. Add an event handler to that option to trigger deletion of the selected table entries.
From the perspective of the Redux store, deleting a table entry should do the following:
entities.tables.byId[id of the related table].tableEntries
entities.tableEntries.byId[id of the table entry]
to undefined
entities.tableEntries.allIds
tagWeight
or entryDetails
directly. They are only queried via tableEntries. So if the tableEntry is gone, it shouldn't matter if those are still there. But it is a good idea to keep the store cleaned up and small, so its worth removing them if it can be done easily.Eliminate any uses of SingleLineAdder. Everything should be moved to SingleLineAdderV2, including proper input validation and display of validation errors.
(I think the tables screen is the only place still using SingleLineAdder)
Once everything has been moved to SingleLineAdderV2, delete SingleLineAdder from src/components/forms
and rename SingleLineAdderV2
to SingleLineAdder
Currently the Hexes screen hardcodes a bunch of things based on the assumption that it is always operating on the 'HEX' table, and we know how that table is arranged.
Update this so that it actually uses a selector to the the 'HEX' table
object and use that to process.
For example, it should be creating its labels based on values queried from inside an actual table
object, not based on hardcoding HEX
as the name of the table.
Likewise, add template labels to this screen that are based on template info found in the table
object
Getting really close to having basic functionality -- adding objects, editing objects -- complete. Getting ready to start implementing secondary and more complex functionality: deleting objects, importing/exporting objects.
We really need to think about how we are going to expose all of these actions in the UI. My initial thinking was dropdown menus for secondary actions, but that doesn't seem like it would work well with the card-based UI we've selected.
Perhaps something more in-line with "correct" card UI, where actions are listed at the bottom?
For the various places where terrain and territory can be input for a hex (adder at the bottom of the Hexes screen, modal for bulk inputting hexes, editing a single hex), make it so that terrain
is a required field and territory
is an optional field.
This will require updating related handleChange
validation, handleSubmit
validation, etc.
We will also need to update reducers for tableEntries so that they only store a value in addTags
if the value was given. (Note that addTags[0]
corresponds to terrain, addTags[1]
to territory).
In addition to just making more sense (ever hex should have a terrain, but isn't necessarily going to be part of anybody's territory) it also simplifies how we work with tableEntries.addTags
. We'll no longer need to keep placeholder undefined
values in that array if terrain was empty.
Would like to update the tables
selectors so that they return more "full" tables. I.e. follow all references-by-id and insert the relevant data.
Especially important is getting full information on attached templates and their template plugins, as we'll need that information for displaying attached template tags.
Since tables reference templates and templates reference tables, need to be careful not to introduce any infinite loops.
Also update any view code that uses the returned data to make sure it still works.
Our selector names are currently all over the place. Decide on a naming scheme and update selector names to match.
Suggest the following:
getByIdTables
=> returns a lookup object for all tables in the store. can lookup by ID. all tables are "full" -- all refefences-by-id are followed and details filled in.
Use this instead of getTablesById
, which leads to confusion over whether its returning a lookup object or returning a single table for a provided ID
If we need a selector for querying a single object, use this naming schema:
getTableById
=> returns state.entities.tables.byId[given ID]
. return object is "full" -- all references-by-id are followed and details filled in.
We should be able to remove any Full
from the names, as the assumption should be that all selectors return a "full" object, i.e. one whose references-by-id have been followed and the details filled in.
Documentation around the schema has fallen a little out of data, mostly related to actual implementation of templates, template plugins, and books. Get it up-to-date.
We have an interesting situation with the EditTemplateModal. In order to know whether to enable the SAVE button, allow submission of changes, etc. the modal component needs to know whether all of the input in the modal is valid, including the template properties and metadata.
However, the structure of the properties and the metadata varies depending on which template plugin is being used. So the EditTemplateModal component cannot do validation of properties and metadata itself. We have to trust that task to the specific template plugin.
This leads to a situation where the parent component can't control the value props of the child component, but needs to know their state. The parent modal needs to know whether properties and metadata are in a "valid" state, and needs to know their values for when the modal submits. But actual control of those needs to be done in the child, because only the individual template plugin knows anything about the structure of that data, what fields to show, what values are needed, whether values are "valid", etc.
Right now, I'm working around this by having the template plugins updating both the usual, asynchronous local state, and a synchronously updated copy of the state. That copy is then sent up to the parent component via an onChange
handler.
This feels like a hack though, and is probably not best practice for React. Let's do some research, find out what other people have done in this case in React, and possibly rework this. It is functional right now, but buts a lot of responsibility on the writer of template plugins to make sure they're updating everything in two different copies of the state.
Create a modal for editing templates. The trickiest part of this is going to be the fact that the section for editing properties and the section for editing metadata are going to vary quite a bit depending on which template plugin is being used.
Do something similar to what we did with the template plugin "preview" -- store the actual function in the store under state.ui.templatePlugins..
Create a new template plugin for an "Inline Index". This will be similar to the existing "Index" template, except for each table entry, it will display text
, entryDetails
, and references
all on a single line.
For now, can just implement the Preview, EditProperties, and EditMetadata parts of this. (We haven't implemented any finalization processing for template plugins yet.) The Preview will require some string manipulation trickery. We want to ensure that each text
, entryDetail
, and references
item is separated by a period and single space. But we need to ensure we don't end up with, for example, two periods followed by a space, a space followed by a period followed by a space, a question mark followed by a period, followed by a space, etc.
The string manipulation that we use on the preview will also be used for the eventual finalization processing.
Implement the change handler for the inputTemplateModal
Make sure there is no collision of the template name with existing template names. May require making a getByNameTemplates()
selector.
It would be helpful if the project state was periodically persisted to a file. So, for example, if a user was to close the application without manually saving their file, the application would open exactly where they left off.
There appear to be React modules out there already that support this.
We have the EditTemplateModal able to submit changes to the updateTemplate
action creator. Now we need to update reducers to support that action.
Implement a way of saving a project to a file. This will likely involve getting all "full" tables, tags, templates, books, etc. from the state via selectors, converting all of that to JSON, and writing to file.
Currently, most of our selectors take stateEntities
as an argument, which points to entities
in the redux store.
A major point of using selectors is to make sure the view needs to know as little bit about the underlying structure of the data store as possible. Asking the view containers to specify state.entities
, state.entities.tables
, or state.ui
goes against that principle. The containers should only need to know about the state
-- not what anything looks like under that. So they should only be able to pass state
, and our selectors should be able to work from that.
Nothing is currently broken, but let's correct this as it's much better coding practice.
We actually want our tables to be able to be attached to more than one template.
To accomplish this, we need to update the state.entities.tables
schema (and all code that works with it) to have an array of templates
instead of a single template
.
This will include making sure we loop through the array of templates when displaying template labels, etc.
Templates should still be attached to only a single table, so state.entities.templates
can remain unchanged.
In the BookContentSettings
component, we are currently using a custom component where you click a pencil icon, it turns the current setting value into a dropdown, you select the value, it update the state, and the dropdown turns back into text of the new setting value.
It's nifty, but it doesn't work too well. Some weird bugs with it (mostly related to onBlur()
not working as expected). We could try to fix those bugs, but instead, let's just use a straightforward Semantic <Form><Form.Group><Form.Select /></Form.Group></Form>
for this. It will look good, be easy to understand, and functional.
Fix ADD_TEMPLATE action so it updates template
on table object as well
We missed adding an ADD_TEMPLATE
action handler to the table reducer
New to Javascript, React, Redux, Electron development. Could really use help setting up a testing framework. Not looking for somebody to implement full test coverage of all existing components, reducers, etc. But help getting an automated testing framework and a couple examples of tests for components and reducers would be invaluable.
Create a new template plugin for formatting tables as "Random Tables". You can see a mockup example at the bottom of the Templates screen. Basically, print each table entry on a single line. Only include the text
for the table entry, not any template details
. The number on the left of the table should be aggregating based on the weight
of each item. Essentially, we're building a "you can roll dice against this" table.
For now, only need to implement the Preview, EditProperties, and EditMetadata for this plugin. (We have not implemented any finalization processors for any plugins yet.)
Still have a few for
loops hanging around. For consistency, replace them with forEach
or map
, whichever is appropriate.
On the hex screen, implement a method of clicking checkboxes to select multiple hexes, or clicking the checkbox at the top of the hex map to select all hexes. Then add an event handler on the "Delete Selected Hex[es]" option in the dropdown menu. This should delete all the selected hexes.
Deletion of the hexes should look like this from the perspective of the Redux store:
terrainHexes
and territoryHexes
values of any tags to reflect any hexes being removedentryDetailsGroup
and associated entryDetails
from any hexes that were overriding the global hex definition. In other words, if entryDetailsGroup
is not HEX
for the deleted hex, we will need to delete the entryDetails.Break down the Books screen mockup into smaller components. Will probably have BookCard
components, BookSummary
, BookConfiguration
, etc.
Currently the EditMetadata
component of the Index template plugin displays the entryDetails
part of the metadata as a <List>
component.
To make this easier to understand the effects of selecting different formatting options, update this so it displays each line using the actual, selected formatting option for that line, i.e. <h1>, <h2>, <p>, etc.
Currently the selectors.tables.getTables()
selector filters out any tables whose ID is 'HEX'
. This is because we are using this selector on the tables screen and don't want to manage the 'HEX' table there. (We manage it on the Hexes screen instead).
To make this selector better do what we'd expect from the name, we really should have it return the 'HEX' table as well. I expect we'll want to use this selector at some point to operate on all tables, including 'HEX'.
Update this selector so it doesn't filter out 'HEX'. Also update the tables screen so the filtering out of 'HEX' happens there instead.
Rename the initial Book 'Hex Book', change its ID to 'HEX' (for consistency with the rest of the store), and update the Book components so that this book cannot be renamed or have the 'HEX' template removed from the first position in the book.
Still allow other settings (page size, etc.) to be changed. Allow other templates to be added to the book.
Update books selectors so that this book appears first in the getBooks
array.
In src/constats/regex.js
there are currently a bunch of validation regexes that do checking on A-Za-z
. I'd actually like to be able to support the full Latin charset (i.e. accented characters) in these. Need to figure out the proper regex for doing so.
Note: There are 2 places we do not want to support accented characters. The CODE on tables and the name of tags. Those should continue to allow only A-Za-z
When inputting result text for table entries, the expectation is that users will type things like:
There is a [[LANDMARK]] on the horizon
A [[RANDOM_ENCOUNTER:+night,-day]] is hidden in the darkness
Where [[LANDMARK]]
means that the token will be replaced with the result of a random roll on the LANDMARK
table. And +night,-day
means that the night
tag will be added to the filter chain and the day
tag will be removed from the filter tag.
It would be really nice if we provided a list of auto-suggestions to users as they type.
e.g. They type A [[RAN
, and we display a popup with all tables whose CODEs start with RAN
. Or they've typed [[LANDMARK:+ni
and we display a popup with all tags whose names start with ni
. The user would be able to click on a suggestion to auto-complete the text.
As a reference, think how suggestions for emojis works in Slack.
Add a submit handler for TemplateInputModal. Gather the submitted template name, description, plugin, and table, dispatch them as an ADD_TEMPLATE
action, create the reducers for adding them to the store.
Acceptance criteria should be that the new template appears on the Templates screen.
There's basically no documentation for this project. This makes it really hard for other people to work on it. At a minimum, document the following:
Input fields that are coded to only accept integer input -- mostly the weight and limit fields on the table entry screens -- are currently unable to be "blanked out"
Example:
Edit a table entry. If it's current weight is "1", you cannot backspace to delete the 1 in order to type in a different integer. The field does not allow itself to become blank.
(As a workaround, you can Select All on the field and overwrite the existing integer with a new one.)
Downside of learning as I code: using some things incorrectly. There's a lot of places in code where I'm iterating over arrays using .map()
where I should be using .forEach()
(or maybe even .reduce()
) instead. Basically, it's anyplace where I'm not actually trying to build a new array, I'm just trying to perform an action on each item in an array.
Fix this! Bad javascript abounds!
Currently, the Table Entry Modal does "input modification" on some of its fields. Example: The code field automatically capitalizes any letters that are entered (since we want the table CODE to always be all caps). However, there is an issue where input modification like this causes unexpected behavior with the input cursor. For example, if you try to insert new text in the middle of an existing input field, the cursor jumps to the end of the input after the first character is entered.
For now, disabled all "input modications" for input fields on the Table Entry Modal. For fields like CODE where we require the value to be all uppercase, ensure we perform .toUpperCase() before dispatching the value to any actions.
Create a modal, actions, and reducers for adding a new book.
In general, most of the reducers for our data model are split into separate "byId" and "allIds" reducers to conform with Redux best practices for normalized data: https://redux.js.org/recipes/structuringreducers/updatingnormalizeddata
Example: To handle the state.entities.tables
part of the schema, there is a tables.byId
reducer that manages state.entities.tables.byId
and an tables.allIds
reducer that handles state.entities.tables.allIds
A while back, I had merged the byId
and allIds
reducers for tags. The thinking was that we would only know if we could remove a tag ID from state.entities.tags.allIds
if we knew certain things about data stored inside the tag in state.entities.tags.byId
. So I joined them to allow that information to be shared.
Now that I have changed the design so we delete tags only if a used manually clicks to delete one, there is no need for allIds
to know anything about byIds
when considering whether or not to remove a tag ID.
We should separate these into two separate reducers again to maintain consistency across the app.
We need a process for deleting templates. Probably this will take the form of a "Delete Template" button or drop-down menu option in the EditTemplateModal. It will also involve implementing the DELETE_TEMPLATE
action across all relevant reducers. This includes the tables reducer, as we will need to remove references to the deleted table.
We should not allow the id = HEX
template to be deleted.
Make actions, action creators, and reducer for "loading" an object. E.g. We get a "full" table, tag, template, etc. from a file, load the object and all of its referenced objects into the state.
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.