Git Product home page Git Product logo

hexbook-semantic's Introduction

HexPop!

Being a Javascript/React/Redux/Semantic UI/Electron application for the procedural generation of TTRPG hexcrawl notebooks

Say whah?

A "hexcrawl" is a style of TTRPG play where the players travel overland on a map divided into hexes, discovering landmarks, encountering enemies, recovering treasures. The expectation is that the referee (or "Dungeon Master" or "Game Master") has information on what is located in each map hex. However, this becomes more difficult the larger the map gets. In my own game, we use a 40x30 map. That's 1200 hexes that need information on landmarks, encounters, treasures, adventure hooks, etc. It is difficult and time consuming for an individual referee to come up with 1200 unique and interesting ideas to fill a map with.

Some referees instead use published hexcrawl products -- maps with included index listing the basic contents of each hex (or at least a great number of the hexes). Examples include: Wilderlands of High Fantasy, Carcosa, The Dark of Hot Springs Island, Fever Swamp. These can be excellent resources. Locations are already defined. Referencing information mid-game can be done quickly. The main downside is that -- unless you do a lot of modification -- you are not playing in your own custom world. There are also many wonderful published setting books that aren't written with hexcrawls in mind.

Some referees rely on random tables to procedurally generate content for their hexes. For example, if a referee needs to know what is found in hex 1232, they might roll on a table of random landmarks, a table of random encounters, a table of random adventure hooks, a table of random NPC motivations. Whatever detail and however much details they think they need. This is often done in the middle of a game, especially if players travel to a hex the referee had not prepared for.

Random tables have many benefits. They aren't too difficult to write. They are easily customized to the referee's world. The random results -- especially combinations of results from multiple tables -- can inspire ideas that the referee might not have otherwise had. There are a lot of published products that include a lot of useful random tables. Almost certainly more than there are published hexcrawl products.

But the big downside is that it takes time to roll on all of these mid-game. If the players are marching south and you need to know if there's any tall landmarks they see in adjacent hexes, now you have to roll on several tables for several hexes all at once. Repeat that each time the players move to a new hex. The referee also needs to keep notes about everything that was rolled and found. More time consumed at the table, and it is easy for these notes to become disorganized.

Enter HexPop!

HexPop! is an attempt to get the best of both worlds. It is an application where you input two main things: a list of hex coordinates and associated terrains from your campaign map, and your own curated collection of random tabels for generating content for those hexes. It outputs one main thing: a PDF with an index of contents for each of your hexes, generated from the collection of tables you inputted. The PDF will be nicely formatted, ready to be uploaded to a Print-on-Demand service (Lulu, etc.), and ready to be shipped to you as a notebook for your campaign.

An example of the result: https://blog.thesconesalone.com/2018/08/my-campaign-notebook-has-arrived.html

In short, HexPop! is a way of using easy-to-write random tables to create your own, easy-to-reference hexcrawl product.

Interface Overview

The HexPop! interface is designed around these main actions: writing random tables, inputting a listing of hex-to-terrain mappings, deciding what will be printed in the final notebook.

Table Entry Syntax

When entering table results, hex definitions, etc. the following syntax is used:

There is a [[LANDMARK]] on the horizon = Roll a random result on the table with code LANDMARK and insert the text

A [[MONSTER:+night,-friendly]] waits in ambush = Add the night tag to the filter, remove the friendly tag from the filter, roll a random result on the MONSTER table, insert the text

He is carrying [[TREASURE:-ALL,+magic]] = Remove all tags from the filter, add the magic tag to the filter, roll on the TREASURE, insert result

In this way, we can have tables referencing other tables, referencing other tables, etc. and get a variety of results from a few tables.

Hexes Screen

Hex Defintion

Defines what contents should be generated for each hex. Example:

  • [[LANDMARK]]
  • [[RANDOM_ENCOUNTER]]. NPC wants [[NPC_GOAL]]
  • Contains [[TREASURE]]

These will be used for every hex that doesn't have an override.

Hex Map

Input of all of the coordinate-to-terrain mappings for the campaign world. Coordinate and terrain are required. Territory is optional.

Note that there isn't anything special about terrain vs territory. They are simply tags that are applied to the filter chain when generating contents for each hex.

You can click on any hex to edit it. In the edit screen, you can choose to override the global Hex Definition with contents specific to this hex. You would use this in cases where you have already decided what will be located in a hex and don't need to randomly generate it.

Finally, the hex map itself is a random table, which can be referenced using the code HEX.

Tags Screen

Tags are our way of filtering available results on random tables. Whenever a roll is made on a table, the roll takes into account all of the tags on the "filter chain". Tags can change the weighting of an entry in the table (a "tag weight" is assigned to the entry) or make an entry inelligble as a result (a tag is "blacklisted" for the entry). The filter chain starts with whatever terrain and territory tags are assigned to the hex that is currently being processed. This is how we, for example, make sure that a "forest" hex is only getting results that are applicable to a "forest".

New tags are automatically created for the terrain and territory when a hex is added or edited. The user can also manually add "other" tags.

These classifications are fluid. A "terrain" tag is simply a tag that is assigned as the terrain to at least one hex. If it is removed from all hexes, it becomes an "other" tag. Likewise for a "territory" tag. An "other" tag is simply one that isn't assigned to a hex. On the backend, there isn't any difference between the three. They all affect result filtering in the same way.

Tables Screen

Here's where most the action happens. The user will add all of the various random tables they want to use to generate content for their hexes. Random lanmark tables, encounter tables, motifivation tables, adventure hook tables, treasure tables, name tables, personality tables. Whatever the user thinks they'll want.

Every table has the following: a name, a reference code, a description, and a listing of table entries. The code is especially important -- it is what is used for referencing the table as [[CODE]] and must be unique.

Every table entry has the following:

  • Weight
  • Tag Weights

Weight is the relative probability of this result being rolled compared to other results in the table. Example: Entry A has a total weight of 3, Entry B has a weight of 2, Entry C has a weight of 1. A roll of 1-3 will result in Entry A, 4-5 for Entry B, 6 for entry C. The total weight is always equal to: the generic weight defined in the Weight field plus the Tag Weights of any tags that are currently in the filter chain.

  • Result
  • Template Details

Result is the text returned to any [[CODE]] references to this table. Template Details are further details about the entry provided to any Templates that are attached to this table. Example:

We have a RANDOM_NPC table. The Result for the table is the NPC's name, which gets inserted in any [[RANDOM_NPC]] references. The Template Details defines further details about the NPC: their appearance, their personality, their goals, what weapons they have, their hometown, etc. Those do not get inserted in [[RANDOM_NPC]] references. Instead, let's say there is an NPC Index template attached to this table. That template defines that an index of the entries in this table will be printed in the notebook. That index does include the information from the Template Details.

  • Blacklist

This entry will be excluded from possibly results if any of its blacklisted tags are in the filter chain. This is how we, for example, prevent an ocean creature from showing up in a desert hex.

  • Limit

The maximum number of times this entry is allowed to be the result of a roll. For example, if you wanted a particular random treasure to be unique in your campaign, you could set its Limit as 1. Note that if all entries in a table have reached their limit, we allow the limits to be exceeded so that we can still have a result.

Templates Screen

Here we define any templates that will be attached to tables (including the hex map table). A template is basically our way of saying: "The full contents of this table may be displayed in the final book and should be displayed in this format." Templates are how we make the listings and indexes of hexes, NPCs, treasures, villages, etc. to be printed in the book.

The user will be able to choose from a selection of built-in templates. Templates may have their own settings unique to that template. For example, an "Index" template would have the user choose how many Template Details should be displayed for each result in the attached table and what formatting should be used for each. e.g. The first Template Detail will be displayed as an <h1>, the second as a <p>, the third as an italicized <span> continuing on the same line as the previous.

At the miniumum, all Templates have a setting defining which Table they are attached to.

If a Table has a Template attached to it, all of its entries become "static". This means their results and details are only rolled once. Example: The [[NPC]] table has an Index of Important NPCs template attached to it. If an entry in the [[NPC]] table has a result like His name is [[FIRSTNAME]] [[LASTNAME]], the first name and last name would be rolled for only once. Regardless of how many times this result comes up on rolls on the [[NPC]] table, the first name and last name remain the same.

Books Screen

Where we finalize our book[s]. We will create one or more books, define which Templates will be printed in which book in what order. Upon clicking the "Finalize" button, details for all tables (including HEX) that have attached templates will be rolled for. When all rolling and recursive rolling and recursive rolling is done, out pops the finalized PDF with all of the included Templates.

Development

Setting up development environment

You will need to have yarn installed. Then cd into the top-level directory and run the following command:

yarn

This will install all dependencies for the development environment

Running development instance

Once again, cd to the top-level directory and run the following:

yarn electron-dev

After a few seconds and Electron (chromium) window will open with HexPop! loaded. You should have access to developer tools (including React tools) via the application menu.

Development concerns

Redux vs. Local Store

All application "data" -- hex listing, tags, tables, table entries, etc. -- should be persisted to the Redux store. Each "screen" will be a Redux-connected container that writes data to Redux via actions and reads from Redux via selectors.

UI information -- current values in input boxes, state of toggles, whether modals are open or not, etc. -- should be stored only in local component state.

Containers vs. Components

In general, the only Redux-connected, smart "containers" we have should be the top level containers for each of the main screens. The hex, tag, table, template, book screens should be Redux-connected containers that write data to the Redux store only via action reducers and read from the Redux store only via selectors. The containers should render very little by themselves. Instead, most of the UI parts on each screen should be defined in non-Redux-connected, dumb "components" that the screen containers pass necessary application data to.

Class vs. Functional Components

Strive to make components as small as possible. In general, a component should either: a) Render a single thing, or b) Render nothing directly, but call several other components to each render their one thing. If a component does not have its own local state, it should always be turned into a functional component. And we should, in general, raise state far enough up to create as many controlled, functional components as is convenient.

Controlled vs. Uncontrolled Components

"Values" on inputs should always be controlled. i.e. An input field should never be tracking its own value in its own local this.state.value. The value should be controlled in a parent component and provided to the input component via props.value. This is to make it easier to do input validation across an entire form. In this case, state should be raised only as high as is necessary to have a single component that is aware of all inputs on the screen and can validate them all. We are not required to elevate all the way to the screen "container". In fact, the screen container, in general, should only control onSubmit actions and the read/writes to Redux store. We can leave the rest to non-Redux-connected components.

Constants

In general, any setting/value that will be used in multiple places across the application: e.g. regex for a valid tag name, the color we're using for primary submission buttons, error messages for invalid inputs, etc. should be stored in an object of "constants". Anyplace where that value is used should reference it via the constant.

Schema

The data model/schema will change slightly between data in the Redux store, data returned by selectors, and data being used in final processing. Each of these has different concerns and different ways that it is better to work with the data. Selectors and reducers will be our primary way of translating between the differenet schemas, which is why it is mandatory that the containers only interact with the Redux store via those.

Redux store schema

Link

Selector schema

Link

Final processing schema

TBD

hexbook-semantic's People

Contributors

bharbron avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar

hexbook-semantic's Issues

Implement deletion of templates

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.

SPIKE: Investigate chromium --print-to-pdf option for generating our PDF

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.

Implement deletion of 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:

  • Set entities.tables.byId[<id of the deleted table>] to undefined
  • Remove the table id from entities.tables.allIds
  • Delete all tableEntries associated with the table from the store
  • Delete all templates associated with the table from the store
  • There shouldn't be a need to drill down any further than that. I.e. we don't need to delete tagWeights or entryDetails associated with associated tableEntries. Those are things that never get queried directly in the application, so we don't have to worry about them still showing up in allIds arrays or anything. Remove them if it straightforward to do so, but it is not a requirement.

Add Book Modal

Create a modal, actions, and reducers for adding a new book.

Document the crap out of everything

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:

  • How to get the app running in a dev environment
  • How to package for production
  • What the data schema looks like
  • Expectations about how responsibilities are shared between actions, reducers, and selectors
  • The expected workflow of the app, i.e. what each screen does
  • What the intended final output of the application is

Remove input modification from Table Entry Modal

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.

Eliminate any remaining uses of SingleLineAdder

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

Set up testing framework

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.

Update EditMetadata for Index plugin so it displays lines using the selected formatting

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.

Integer inputs can't be blanked out

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.)

Implement "Save to File"

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.

Replace .map() with .forEach() where preferable

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!

handleSubmit for TemplateInputModal

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.

Re-separate byId and allIds reducers for Tags

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.

Separate template plugins into their own directory

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.

Spike: Figure out how to create an auto-suggestion popup for CODEs and tags

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.

Name and fix-in-place the initial Book

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.

Update selectors.tables.getTables() so it doesn't filter out 'HEX'

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.

Template Edit Modal

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 "Inline Index" template plugin

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.

Rethink the way we're handling state for editing template properties and metadata

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.

Update regexes to support all Latin characters

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

Think about final UI design

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?

image

Implement periodic persistence of state to file

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.

Actions & reducers for "loading" objects

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.

Update documentation

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.

Make terrain required and territory optional for hexes

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.

Update all selectors so they take `state` instead of `stateEntities`, `stateTables`, etc.

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.

change handler for inputTemplateModal

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.

Implement deletion of hexes

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:

  • Set tableEntries.byId.id to "undefined" for each of the selected hexes
  • Remove the hex id from tableEntries.allIds
  • Do not delete any associated tags
  • Do update the terrainHexes and territoryHexes values of any tags to reflect any hexes being removed
  • Delete any entryDetailsGroup 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.

Improve consistency of selector names

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.

Update tables selectors to return "full" table objects

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.

Update Hexes screen so it uses a full table object

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

Create "Random Table" template plugin

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.)

Books screen

Break down the Books screen mockup into smaller components. Will probably have BookCard components, BookSummary, BookConfiguration, etc.

Change widgets for BookContentSettings

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.

Update tables so they have an array of templates instead of a single template

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.

Implement deletion of tableEntries

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:

  • Remove the id of the tableEntry from entities.tables.byId[id of the related table].tableEntries
  • Set entities.tableEntries.byId[id of the table entry] to undefined
  • Remove the table entry ID from entities.tableEntries.allIds
  • Not mandatory, but should be easy enough to do that its worth it: delete any tagWeights associated with the tableEntry
  • Also not mandatory, but easy enough to do and keeps the store clean: delete any associated entryDetailsGroup and the associated entryDetails
  • The above two are not mandatory because nothing in the application should be querying 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.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.