{"_id":"@asemirsk/core","_rev":"27-4c1564099d0cc81bced31c99a55b7a57","time":{"created":"2022-04-04T11:44:56.389Z","1.0.0-alpha-3.9.0":"2022-03-15T12:56:37.207Z","modified":"2022-05-25T09:17:51.436Z","1.0.0-alpha-3.1.0":"2022-03-15T13:54:14.947Z","1.0.0-beta.2":"2022-03-15T14:25:09.901Z","1.0.0-beta.3":"2022-03-15T14:37:40.419Z","1.0.0":"2022-03-15T14:40:36.856Z","1.0.1-beta.0":"2022-03-15T14:44:44.558Z","1.0.0-alpha-5.3.0":"2022-03-15T16:03:37.697Z","1.0.0-alpha.0":"2022-04-04T11:44:56.616Z","1.0.0-beta.5":"2022-04-04T13:57:37.967Z","1.0.0-beta.6":"2022-04-08T20:00:50.817Z","1.0.1-beta.6":"2022-04-08T20:05:02.741Z","1.0.1":"2022-04-08T20:09:43.537Z","1.0.2-beta.0":"2022-04-08T20:19:12.758Z","1.0.2":"2022-04-08T20:22:21.671Z","1.0.3":"2022-04-08T20:26:25.376Z","1.1.0":"2022-04-19T15:07:12.300Z","2.0.1-beta.0":"2022-04-29T14:35:17.249Z","2.0.1-beta.1":"2022-04-29T14:41:38.104Z","2.0.3-beta.0":"2022-04-29T14:49:47.126Z","2.0.3-beta.1":"2022-04-29T14:51:40.733Z","2.0.5-beta.0":"2022-04-29T15:06:48.554Z","2.0.6-beta.0":"2022-05-02T10:08:14.356Z","3.0.0":"2022-05-02T10:21:43.927Z","1.0.0-beta.16":"2022-05-06T09:17:13.429Z","1.0.0-canary-24-22.0":"2022-05-25T08:46:21.917Z","1.0.0-canary-24-23.0":"2022-05-25T09:07:55.019Z","1.0.0-canary-24-24.0":"2022-05-25T09:17:51.378Z"},"name":"@asemirsk/core","dist-tags":{"alpha":"1.0.0-alpha.0","latest":"3.0.0","next":"1.0.0-beta.16","canary-24":"1.0.0-canary-24-24.0"},"versions":{"1.0.0-alpha.0":{"name":"@asemirsk/core","version":"1.0.0-alpha.0","description":"Edit context for BodilessJS site editor","author":{"name":"Chris Oden","email":"coden@its.jnj.com"},"license":"Apache-2.0","main":"lib/index.js","sideEffects":false,"typings":"lib/index.d.ts","repository":{"type":"git","url":"git+https://github.com/johnsonandjohnson/bodiless-js.git"},"publishConfig":{"access":"public"},"scripts":{"build":"tsc -p ./tsconfig.json && npm run build:api-doc","build:watch":"npm run build -- --watch","build:api-doc":"typedoc --out doc/api src","lint":"tslint ./src/**/*.ts?x","clean":"rimraf \"lib/*\" && rimraf tsconfig.tsbuildinfo && rimraf \"doc/api\""},"dependencies":{"@asemirsk/fclasses":"1.0.0-alpha.0+28ce15a","@types/react-tag-autocomplete":"^6.0.0","axios":"^0.21.0","enzyme":"^3.9.0","informed":"^3.34.0","lodash":"^4.17.19","rc-tooltip":"^5.1.1","react-tag-autocomplete":"^6.0.0","resize-observer-polyfill":"^1.5.1","universal-cookie":"^4.0.3","uuid":"^3.3.2"},"peerDependencies":{"mobx":"^6.0.0","mobx-react":"^7.2.1","react":"^17.0.2","react-dom":"^17.0.2"},"devDependencies":{"@types/rc-tooltip":"^3.7.1","@types/react-tag-autocomplete":"^6.0.0","@types/uuid":"^3.4.4"},"gitHead":"28ce15a6b1c49e6f984d59ff1db29bac050fe78d","bugs":{"url":"https://github.com/johnsonandjohnson/bodiless-js/issues"},"homepage":"https://github.com/johnsonandjohnson/bodiless-js#readme","_id":"@asemirsk/core@1.0.0-alpha.0","_nodeVersion":"16.13.2","_npmVersion":"lerna/4.0.0/node@v16.13.2+x64 (linux)","dist":{"integrity":"sha512-3pndj3FnFcDsfA8vQgEbw/F1Sf/yzgpxvYNmZKCca8oAfVCFPP4zQ03g+Hf197r6LdLXzSvQErZGVQGhrTgVvQ==","shasum":"e4bef7a50469e1bb8b5eb8a9355ca42533ef1325","tarball":"https://registry.npmjs.org/@asemirsk/core/-/core-1.0.0-alpha.0.tgz","fileCount":4,"unpackedSize":27644,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQCGW+BDbtZlrLAD5kN20V9rDy4g3q1WpMJNQhxifTZOKwIgJpKGwfnoQ+TO4saPFeuo7zCBSyl3+DkKASUjagvSOX0="}],"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJiSto4ACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmrOgw//R2EjNJjJhmYmooT6DKQABB+v4XMZVflMiPejii4yVooXzQLj\r\nMkjtnUpFe/jpJrXM3ZyCqpDgWxeZO1sKkKSacRjMyDr/5bLpYtoMgoTS8/Et\r\n0MA4X/Qk8f3RHh3AwN0hSBcsW4oi3znoOBzWBeyTIN+fVaWzAKk5OuDJ/mcj\r\nztK+KRoJJY3jAdAS1oKgVFQFzAsKP35eB8q/42F8FhtAzN7hKSK9a1SOtX1R\r\n8/Dn8Aj97yTiNs5d3bkZVpC5U7l08e6D/Hg5v2mDyuW/uLwrbU4aOVRQYjMN\r\nbdOvgvPWENxAuawX9pVvhxq6xMOrJ8HWI2RpHYiT/AZHbz/qlTQyTV86L5CX\r\njn6GoZwCNTQ4ahYkoV/O5eKPqXI5CMDrB1wATLbEQknOnTrt2i2K7EgsAnvA\r\n5AxVA2d+Len8dLv8OpnfQAkUSxn5G+nC+MO1qANFaKwqovSOPeDzT2Ud2RLj\r\nPrMEKXURpYo3IoeOSb1gbUCOLiuJB7rQfc+PejBEU4c5OMrm1EKd/DIeMjni\r\naxXzVybPXT1saDV+d4gV5QDqyOEV4uZiM96PuQGlTVI89vGn2rtxpQVmIOJ9\r\nGC9zd8WY2RaLwqkTgrLxEoh4qUpel9NRI+0AX/rsJPvMsgKOpQTQsmawKHnk\r\n/qNZQG1eBZVL31VrU3uLnQnBbX5cKdkxoTA=\r\n=soFu\r\n-----END PGP SIGNATURE-----\r\n"},"_npmUser":{"name":"asemirsk","email":"al.semirski@gmail.com"},"directories":{},"maintainers":[{"name":"asemirsk","email":"al.semirski@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/core_1.0.0-alpha.0_1649072696438_0.9128244067598621"},"_hasShrinkwrap":false},"1.0.0-beta.5":{"name":"@asemirsk/core","version":"1.0.0-beta.5","description":"Edit context for BodilessJS site editor","author":{"name":"Chris Oden","email":"coden@its.jnj.com"},"license":"Apache-2.0","main":"lib/index.js","sideEffects":false,"typings":"lib/index.d.ts","repository":{"type":"git","url":"git+https://github.com/johnsonandjohnson/bodiless-js.git"},"publishConfig":{"access":"public"},"scripts":{"build":"tsc -p ./tsconfig.json && npm run build:api-doc","build:watch":"npm run build -- --watch","build:api-doc":"typedoc --out doc/api src","lint":"tslint ./src/**/*.ts?x","clean":"rimraf \"lib/*\" && rimraf tsconfig.tsbuildinfo && rimraf \"doc/api\""},"dependencies":{"@asemirsk/fclasses":"^1.0.0-beta.5","@types/react-tag-autocomplete":"^6.0.0","axios":"^0.21.0","enzyme":"^3.9.0","informed":"^3.34.0","lodash":"^4.17.19","rc-tooltip":"^5.1.1","react-tag-autocomplete":"^6.0.0","resize-observer-polyfill":"^1.5.1","universal-cookie":"^4.0.3","uuid":"^3.3.2"},"peerDependencies":{"mobx":"^6.0.0","mobx-react":"^7.2.1","react":"^17.0.2","react-dom":"^17.0.2"},"devDependencies":{"@types/rc-tooltip":"^3.7.1","@types/react-tag-autocomplete":"^6.0.0","@types/uuid":"^3.4.4"},"gitHead":"e6beb3c3e2c8fcff324e0e741ec5746fc55ec312","readme":"# Edit Context and UI State\n\n## Overview\n\nBodiless uses the [React Context API](https://reactjs.org/docs/context.html) to\nprovide a \"page edit context\"\n(../packages/bodiless-core/src/PageEditContext/index.tsx) instance to all\ncomponents on the page. This is an interface which allows a component to\ncontribute contextual elements to the page edit UI (currently limited to toolbar\nmenu items). For example, an image component might contribute a menu item which\nallows a user to upload an image.\n\nThe edit context also provides an interface to a global store which defines the\nUI state, including:\n\n- which edit context is \"active\" (ie, which component has the current focus),\n  and what related menu options should be available.\n- whether edit mode is active (ie, whether components should expose their edit\n  interfaces)\n- in future, anything else which might affect all components on the page (eg\n  whether the current changes have been pushed, whether there are upstream\n  changes to be pulled, etc).\n\n## Flow\n\nThe following diagram illustrates the flow of data for page edit contexts:\n\n![context](assets/context.jpeg)\n\n1. The `PageEditor`(../packages/bodiless-core/src/components/PageEditor.tsx) (the\n   top-level component of an editable page) defines a base edit context,\n   contributing menu options which apply to the page as a whole (e.g. whether\n   edit mode should be enabled). This is set as the default value of a React\n   context type named \"PageEditContext\". Provider and Consumer components for\n   this context are available as static properties of the PageEditContext class\n   (`PageEditContext.Provider` and `PageEditContext.Consumer`).\n2. Any item on the page which wishes to define a more specific context can do so\n   using the `spawn()` method of the `PageEditContext` instance. This will\n   create a new instance with the current instance as its parent. This can then\n   be set as the new context value using `PageEditContext.Provider`. For\n   convenience, a `ContextProvider`(../packages/bodiless-core/src/PageContextProvider.tsx)\n   component is provided which creates a new context and injects it into its\n   children (provided as a render prop).\n3. A component which does not want to create a new context can either consume\n   the current context (either via `PageEditContext.Consumer` or via the\n   `useEditContext()` hook), or simply ignore it, if it doesn't care about the\n   current context or UI state.\n4. The `PageEditContext` instance exposes an `activate()` method which can be\n   used by a component to declare that its context is active (usually when the\n   user focuses there).  This sets the active context in the store. If the active\n   context has parents, then these are also considered active, and the set of\n   all active contexts is referred to as the <a id=\"context-trail\"></a>\n   _*context trail*_. For convenience a `useContextActivator()` hook is provided.\n5. This, in turn triggers any observer component to re-render (for example to\n   highlight the fact that it is active).\n6. The \"global context menu\"(../packages/bodiless-core/src/components/PageEditor.tsx)\n   is one such observer component, and re-renders itself with all menu options\n   contributed by the newly activated context and all its parents. Thus, for\n   example, when you activate the context of an image within a grid, the menu\n   options of the image (eg set source) are added to those of the grid (eg\n   insert/delete items), which in turn are added to the page level items (eg\n   toggle edit on/off).\n7. Similarly, the\n   \"local context menu\"(../packages/bodiless-core/src/components/LocalContextMenu.tsx)\n   is another observer which renders a subset of context menu options as a tooltip\n   near the component providing current innermost active context.\n\n## Working with edit context\n\nThere are several ways in which a component can provide or consume the edit context.\n\n### Providing a new context\n\nTo provide a new context value (usually to add menu options which should appear\nwhen a component has focus), you may use the supplied `PageContextProvider`\n\n```javascript\nconst getExampleMenuOptions = useGetter([{\n  // An array of context menu option objects\n}]);\n\nconst Example: React.FC = ({ children }) => (\n  <PageContextProvider getMenuOptions={getExampleMenuOptions} name=\"Example\">\n    {children}\n  </PageContextProvider}\n```\nHere we provide a menu options callback as a prop to the `ContextProvider`\ncomponent. This will be invoked when this context or any descendant is\nactivated. It should return any menu options this component wishes to provide\n(see [Context Menu Options](#context-menu-options) below for more information).\n\n> Note: It is important to memoize our `getMenuOptions()` callback so as to\n> prevent unnecessary renders of subscribers to the edit context. Here, we do\n> this with the bodilesscore `useGetter()` hook, which creates an invariant\n> callback even if the menu options it returns change.\n\nIt is actually unusual to invoke the provider directly in this manner. Instead,\nuse the `withMenuOptions` hoc to attach options to your component. First you\ndefine a custom hook which will create your `getMenuOptions()` callback. This\nhook will be invoked when the component is rendered, and will receive its props\nas an argument:\n\n```\nconst useMenuOptions = (props) => {\n  return [{\n    // An array of context menu option objects\n  }];\n};\n```\n\nThen, pass that along with a unique name to `withMenuOptions` to create an HOC\nwhich will add the options to your component.\n\n```\nconst ComponentWithMyOptions = withMenuOptions({\n  useMenuOptions,\n  name: 'my-component'\n})(AnyComponent);\n...\n<ComponentWithMyOptions propUsedInOption=\"foo\" />\n```\nNote here that we don't need to memoize the list of menu options: memoization of\nthe callback is handled within `withMenuOptions`. However, certain callbacks\nprovided as properties of the menu option object may need to be memoized (see\n[Context Menu Options](#context-menu-options) below for more information).\n\nNote that the menu options you are defining here will only be available when\nyour component (or one of its children) declares itself as \"active\". To do so,\nit will need to use a method on the current context.\n\n### Consuming the context with hooks\n\nTo access the current page edit context, simply use the `useEditContext()`\nand/or `useContextActivator()` hooks.\n\n```javascript\nimport { observer } from 'mobx-react';\nconst Example = observer(props => {\n  const { isActive, isEdit } = useEditContext();\n  const { onClick } = useContextActivator();\n  return (\n    <React.Fragment>\n      <div>{isActive ? 'Active' : 'Not Active'}</div>\n      {isEdit ?\n        <button onClick={onClick} />\n        : <span>Not editing!</span>\n      }\n    </React.Fragment>\n  );\n});\n```\n\nNote that `useContextActivator()` can be used to provide a handler for other\nevents besides `onClick`, and can invoke another handler passed in as a prop:\n\n```\nconst TextareaActivator = ({ onFocus: onFocus$1, ...rest }) => {\n  const { onFocus } = useContextActivator('onFocus', onFocus$1);\n  return <textarea onFocus={onFocus} {...rest} />\n}\n```\nIn the above example, the `textarea` will activate the context on focus, and\nwill then invoke any 'onFocus' handler passed to it.\n\nNote that we are using the `isActive` property of the context to render\ndifferently if the current context is active, and using the `isEdit` property to\ndetermine if we are in edit mode. Note also that we wrap our component in a\n[mobx observer](https://mobx-react.js.org/observer-hoc) to ensure that it\nupdates properly if the active context or edit mode changes.\n\n> Important! For functional components using hooks, we must use the version of\n> `observer` from mobx-react.\n\nIt is also possible to use the `withContextActivator` HOC to provide an activation\nevent to a pre-existing component:\n```\nconst ComponentWithMenuOption = flowRight(\n  withMenuOptions({ ... }), // See abo ve\n  withContextActivator('onClick'),\n)(AnyComponent)\n```\n\nNow, when you click on `AnyComponent` it will activate the provided context, and\nthe associated menu options will be displayed.\n\nNote - the above will only work if `AnyComponent` can accept an `onClick` prop.\nIf not, you will want to wrap it in an element which can, for example a `div`:\n```\nconst ComponentWithMenuOption = flowRight(\n  withMenuOptions({ ... }), // See above\n  withContextActivator('onClick'),\n  withActivatorWrapper('div'),\n)(AnyComponent)\n```\n\nFinally, you will want to be sure that none of the above HOC's are applied when not in edit mode.\nFor this, `ifEditable` comes in handy:\n```\nconst ComponentWithMenuOption = ifEditable(\n  withMenuOptions({ ... }), // See above\n  withContextActivator('onClick'),\n  withActivatorWrapper('div'),\n)(AnyComponent)\n```\nNote that the order of these HOC's is important.\n\n## Context Menu Options\n\nThe Global Context Menu aggregates menu options provided by all contexts within\nthe [active context trail](#context-trail). It is a wrapper around the generic\n`ContextMenu` (../packages/bodiless-core/src/components/ContextMenu.tsx)\ncomponent, which provides a mechanism for displaying a set of menu options\nprovided in an \"options\" prop. Each item is an object with the following\nmembers:\n- name: a unique machine name for this item.\n- group: the machine name of the group to which this button belongs\n- Component: An optional component to use to render this button.\n- icon: the name of a [material design icon](https://material.io/tools/icons/?style=baseline)\n  to display for this item.\n- label: a human readable label to display beneath the icon.\n- isActive; a boolean specifying the option or its flyout panel is currently\n  \"active\". This is usually used for toggles.\n- isDisabled; a boolean specifying whether the option is currently \"disabled\".\n  If true, the menu button will be greyed out and bnot clickable.\n- isHidden: a boolean specifying whether the option is currently hidden. If\n  true, the menu button will not be displayed.\n- handler: a callback to invoke when the menu button is clicked.\n\n> Note all of the above properties except `handler`, `name`, `group` and\n> `Component` can be provided either as a primitive value or as a function\n> returning that value - eg `{ isActive: true }` or `{ isActive: () => true; }`\n> The latter is useful if the value depends on some external state which is not\n> used by the component providing the button, to allow the button to update\n> without re-rendering the component. If you provide callbacks, it is important\n> to memoize any such callbacks to avoid unnecessary renders of the button\n> itself. The `handler` callback, however, need not be memoized.\n\n A simple context menu implementation with a single option might look like this:\n ```javascript\nconst [isUp, setIsUp] = React.useState(false);\n\nconst options = [\n{\n  name: 'say_yes',\n  icon: isUp ? 'thumb_up' : 'thumb_down',\n  label: 'Yes!',\n  isActive: isUp,\n  handler: () => {\n    if (!isUp) {\n      alert('Yes!');\n    }\n    setIsUp(isUp => !isUp);\n  },\n];\n\nconst MyContextMenu = props => (\n  <ContextMenu options={options} />\n);\n```\n\n#### Global and Local Context Menu\n\nBy default, Bodiless provides two `ContextMenu` instances to display menu options\nprovided by edit contexts. \n\nThe `GlobalContextMenu` is a part of the `PageEditor` and is intended to provide\na single, top-level menu for the page.\n\nThe `LocalContextMenu` component may be used to add a tooltip version of the\ncontext menu to any component, as\n\n```\n<ContextProvider getMenuOptions={myComponentMenuOptionGetter} name=\"My Component\">\n  <LocalContextMenu>\n    <MyComponent>\n    ...\n    </MyComponent>\n  </LocalContextMenu>\n</ContextProvider>\n```\n\nThis will provide a tooltip for `MyComponent` which displays a local version of the context menu.\n\nWhen defining a menu option, there are two flags you can use to control whether\nit should appear on the global context menu, the local context menu or both:\n\n```javascript\nconst myMenuOption = {\n  ...,\n  global: true // Show on global context menu\n  local: false // Hide on local context menu\n}\n```\n\n#### Context menu forms\n\nThe handler function for a menu option can, optionally, return a render\nfunction. If it does, when the menu item is selected, this will be invoked to\nrender the contents of a fly-out panel (usually a form). This allows menu\noptions to trigger display of a configuration form to collect additional user\ninput. For example, an image component might provide a menu option to configure\nits content. This might then display a form for uploading the image, entering\nalt-text, etc.\n\n## Styling\n\nThis library includes components which render visible, interactive pieces of the edit UI. These include:\n\n- ContextMenu\n- PageEditor\n- LocalContextMenu\n- ContextWrapper\n\nThese components allow injection of UI elements via a `ui` prop. This prop is a\njavascript object enumerating the UI elements which the component will use. For example,\nthe UI specification for a ContextMenu is:\n\n```javascript\nexport type UI = {\n  Icon?: ComponentType<HTMLProps<HTMLSpanElement>>;\n  Toolbar?: ComponentType<HTMLProps<HTMLDivElement>>;\n  ToolbarButton?: ComponentType<ButtonVariantProps>;\n  FormWrapper?: ComponentType<HTMLProps<HTMLDivElement>>;\n  ToolbarDivider?: ComponentType<HTMLProps<HTMLHRElement>>;\n};\n```\n\nThe `ui` prop fo `ContextMenu` accepts low-level UI elements which are composed\nto render the menu. In contrast, that for `LocalContextMenu` and `PageEditor`\nsimply lists the menu component which should be used to render the currently\nselected options:\n\n```\ntype UI = {\n  LocalContextMenu?: ComponentType<ContextMenuProps>;\n};\n```\n\nThis allows us to create customized versions of the context menu, as is done in\nthe canvas-x\nstylable `ContextMenu` (../packages/bodiless-core-ui/src/GlobalContextMenu.tsx)\nand\n `LocalContextMenu` (../packages/bodiless-core-ui/src/LocalContextMenu.tsx).\n\n## Activate Context System\n\nThe activate Context system allow for one component to store an id of another\ncomponent that should activate its context on creation.\n\nThis is a three step process.\n\n### Step 1 Ensure there is a Activate Context in place\n\nOne can wrap there components in a `<ActivateContextProvider>` component or one\ncan use the withActivateContext() HOC that will wrap a component in the Provider\n\n### Step 2 set the Id\n\nThen one needs to set the id of the component to be activated by pulling `setId` from `useActivateContext`.\n\n``` tsx\nconst { setId } from useActivateContext();\nsetId(id);\n```\n\n### Step 3  Add useActivateOnEffect hook\n\nFinally, the component needs to have the `useActivateOnEffect` hook so that it will activate if the id match the one stored\n\n``` tsx\nuseActivateOnEffect(id);\n```\n\n","readmeFilename":"README.md","bugs":{"url":"https://github.com/johnsonandjohnson/bodiless-js/issues"},"homepage":"https://github.com/johnsonandjohnson/bodiless-js#readme","_id":"@asemirsk/core@1.0.0-beta.5","_nodeVersion":"16.13.2","_npmVersion":"lerna/4.0.0/node@v16.13.2+x64 (linux)","dist":{"integrity":"sha512-h3DfC6EvE3UTQ9nbSvRcBh+tO/7yWZgrjRvPdTsDWJR7MfUTIpAjxtRUeLn5lsxEkBNoe2IVhlKPAeISup9H8Q==","shasum":"8b6e3d054a18d5f1985249f44537aa464c5d93ac","tarball":"https://registry.npmjs.org/@asemirsk/core/-/core-1.0.0-beta.5.tgz","fileCount":4,"unpackedSize":27627,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQCJlxrgSy4f/6tCAZK9Wmznd554yIhpsrv6tvn4jVNOUwIhAP0asxC6qFKcs3+cR1t1uWIzoXK6YPIQSW/+VOVLU0e7"}],"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJiSvlSACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmqpgA//YOU1IrHC8wVJtliSSGKIFDyGWC+z+nYOGD8c3V1BF4ibaT5z\r\nVIeCbvDf/qgQRBI7Uwlt9kmXOaUWrJI6p1UcojQVKkCfONIjlH7YmagUgfvE\r\nQPLBpn/sDm6BHC3joRReAecmt40fcbe5HKly+k/PArDowqnghPgtsq6B3M5Q\r\ngKVFT9bGD18rJTm1uZX+ffmghtoHVBYlZ9D/KQDK7sPFuV0knot9mW7Vf18F\r\n4XUozQXVW9oVqPuGUo7te3+HxGmuZxE6+dWZBZyGYyOfrl42tyKbbnQGb0hb\r\nq2M9ZChtckQ6V+1mhaG19FkPKgquiB5UQPvPeHNM+67nFeSCqXFIKSKEJjwp\r\nKEbfNNOFi4uGCWCt+1PI//X433QSmB6/9tlq7r7Kbfbh1gRJR5L2rqTsSIHQ\r\n5Oi4yGcR3KcdBWqo40gXgUZc0FQCtEueCcW+vh71hR7IRhsdl10MeTR3zrXa\r\nBVPIylHO6+M9XivNRmBGc3zcAVg/uaf2CggUGkNvkhVO5M3YFpTLn4arIQY/\r\n9ZIWCCfp/mTyPxM4eEMcabX1ILgPMau/i23Xb+ECIEj6/fwDGE/dv3CoDYk7\r\nktSR1X+AZAXOVM0QBA/skAr8J+RLk2wDk0IQdmJAYplapQRSIesFDyjITYBx\r\nS6lq1FydKR+RJeQE+CcRfRgs2bOzWBE2UVQ=\r\n=Kcgx\r\n-----END PGP SIGNATURE-----\r\n"},"_npmUser":{"name":"asemirsk","email":"al.semirski@gmail.com"},"directories":{},"maintainers":[{"name":"asemirsk","email":"al.semirski@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/core_1.0.0-beta.5_1649080657804_0.36217086400191545"},"_hasShrinkwrap":false},"1.0.0-beta.6":{"name":"@asemirsk/core","version":"1.0.0-beta.6","description":"Edit context for BodilessJS site editor","author":{"name":"Chris Oden","email":"coden@its.jnj.com"},"license":"Apache-2.0","main":"lib/index.js","sideEffects":false,"typings":"lib/index.d.ts","repository":{"type":"git","url":"git+https://github.com/johnsonandjohnson/bodiless-js.git"},"publishConfig":{"access":"public"},"scripts":{"build":"tsc -p ./tsconfig.json && npm run build:api-doc","build:watch":"npm run build -- --watch","build:api-doc":"typedoc --out doc/api src","lint":"tslint ./src/**/*.ts?x","clean":"rimraf \"lib/*\" && rimraf tsconfig.tsbuildinfo && rimraf \"doc/api\""},"dependencies":{"@asemirsk/cli":"^1.0.0-beta.6","@asemirsk/fclasses":"^1.0.0-beta.6","@types/react-tag-autocomplete":"^6.0.0","axios":"^0.21.0","enzyme":"^3.9.0","informed":"^3.34.0","lodash":"^4.17.19","rc-tooltip":"^5.1.1","react-tag-autocomplete":"^6.0.0","resize-observer-polyfill":"^1.5.1","universal-cookie":"^4.0.3","uuid":"^3.3.2"},"peerDependencies":{"mobx":"^6.0.0","mobx-react":"^7.2.1","react":"^17.0.2","react-dom":"^17.0.2"},"devDependencies":{"@types/rc-tooltip":"^3.7.1","@types/react-tag-autocomplete":"^6.0.0","@types/uuid":"^3.4.4"},"gitHead":"cbd97725152f2362bb86e6d4bba02043e3c4570e","readme":"# Edit Context and UI State\n\n## Overview\n\nBodiless uses the [React Context API](https://reactjs.org/docs/context.html) to\nprovide a \"page edit context\"\n(../packages/bodiless-core/src/PageEditContext/index.tsx) instance to all\ncomponents on the page. This is an interface which allows a component to\ncontribute contextual elements to the page edit UI (currently limited to toolbar\nmenu items). For example, an image component might contribute a menu item which\nallows a user to upload an image.\n\nThe edit context also provides an interface to a global store which defines the\nUI state, including:\n\n- which edit context is \"active\" (ie, which component has the current focus),\n  and what related menu options should be available.\n- whether edit mode is active (ie, whether components should expose their edit\n  interfaces)\n- in future, anything else which might affect all components on the page (eg\n  whether the current changes have been pushed, whether there are upstream\n  changes to be pulled, etc).\n\n## Flow\n\nThe following diagram illustrates the flow of data for page edit contexts:\n\n![context](assets/context.jpeg)\n\n1. The `PageEditor`(../packages/bodiless-core/src/components/PageEditor.tsx) (the\n   top-level component of an editable page) defines a base edit context,\n   contributing menu options which apply to the page as a whole (e.g. whether\n   edit mode should be enabled). This is set as the default value of a React\n   context type named \"PageEditContext\". Provider and Consumer components for\n   this context are available as static properties of the PageEditContext class\n   (`PageEditContext.Provider` and `PageEditContext.Consumer`).\n2. Any item on the page which wishes to define a more specific context can do so\n   using the `spawn()` method of the `PageEditContext` instance. This will\n   create a new instance with the current instance as its parent. This can then\n   be set as the new context value using `PageEditContext.Provider`. For\n   convenience, a `ContextProvider`(../packages/bodiless-core/src/PageContextProvider.tsx)\n   component is provided which creates a new context and injects it into its\n   children (provided as a render prop).\n3. A component which does not want to create a new context can either consume\n   the current context (either via `PageEditContext.Consumer` or via the\n   `useEditContext()` hook), or simply ignore it, if it doesn't care about the\n   current context or UI state.\n4. The `PageEditContext` instance exposes an `activate()` method which can be\n   used by a component to declare that its context is active (usually when the\n   user focuses there).  This sets the active context in the store. If the active\n   context has parents, then these are also considered active, and the set of\n   all active contexts is referred to as the <a id=\"context-trail\"></a>\n   _*context trail*_. For convenience a `useContextActivator()` hook is provided.\n5. This, in turn triggers any observer component to re-render (for example to\n   highlight the fact that it is active).\n6. The \"global context menu\"(../packages/bodiless-core/src/components/PageEditor.tsx)\n   is one such observer component, and re-renders itself with all menu options\n   contributed by the newly activated context and all its parents. Thus, for\n   example, when you activate the context of an image within a grid, the menu\n   options of the image (eg set source) are added to those of the grid (eg\n   insert/delete items), which in turn are added to the page level items (eg\n   toggle edit on/off).\n7. Similarly, the\n   \"local context menu\"(../packages/bodiless-core/src/components/LocalContextMenu.tsx)\n   is another observer which renders a subset of context menu options as a tooltip\n   near the component providing current innermost active context.\n\n## Working with edit context\n\nThere are several ways in which a component can provide or consume the edit context.\n\n### Providing a new context\n\nTo provide a new context value (usually to add menu options which should appear\nwhen a component has focus), you may use the supplied `PageContextProvider`\n\n```javascript\nconst getExampleMenuOptions = useGetter([{\n  // An array of context menu option objects\n}]);\n\nconst Example: React.FC = ({ children }) => (\n  <PageContextProvider getMenuOptions={getExampleMenuOptions} name=\"Example\">\n    {children}\n  </PageContextProvider}\n```\nHere we provide a menu options callback as a prop to the `ContextProvider`\ncomponent. This will be invoked when this context or any descendant is\nactivated. It should return any menu options this component wishes to provide\n(see [Context Menu Options](#context-menu-options) below for more information).\n\n> Note: It is important to memoize our `getMenuOptions()` callback so as to\n> prevent unnecessary renders of subscribers to the edit context. Here, we do\n> this with the bodilesscore `useGetter()` hook, which creates an invariant\n> callback even if the menu options it returns change.\n\nIt is actually unusual to invoke the provider directly in this manner. Instead,\nuse the `withMenuOptions` hoc to attach options to your component. First you\ndefine a custom hook which will create your `getMenuOptions()` callback. This\nhook will be invoked when the component is rendered, and will receive its props\nas an argument:\n\n```\nconst useMenuOptions = (props) => {\n  return [{\n    // An array of context menu option objects\n  }];\n};\n```\n\nThen, pass that along with a unique name to `withMenuOptions` to create an HOC\nwhich will add the options to your component.\n\n```\nconst ComponentWithMyOptions = withMenuOptions({\n  useMenuOptions,\n  name: 'my-component'\n})(AnyComponent);\n...\n<ComponentWithMyOptions propUsedInOption=\"foo\" />\n```\nNote here that we don't need to memoize the list of menu options: memoization of\nthe callback is handled within `withMenuOptions`. However, certain callbacks\nprovided as properties of the menu option object may need to be memoized (see\n[Context Menu Options](#context-menu-options) below for more information).\n\nNote that the menu options you are defining here will only be available when\nyour component (or one of its children) declares itself as \"active\". To do so,\nit will need to use a method on the current context.\n\n### Consuming the context with hooks\n\nTo access the current page edit context, simply use the `useEditContext()`\nand/or `useContextActivator()` hooks.\n\n```javascript\nimport { observer } from 'mobx-react';\nconst Example = observer(props => {\n  const { isActive, isEdit } = useEditContext();\n  const { onClick } = useContextActivator();\n  return (\n    <React.Fragment>\n      <div>{isActive ? 'Active' : 'Not Active'}</div>\n      {isEdit ?\n        <button onClick={onClick} />\n        : <span>Not editing!</span>\n      }\n    </React.Fragment>\n  );\n});\n```\n\nNote that `useContextActivator()` can be used to provide a handler for other\nevents besides `onClick`, and can invoke another handler passed in as a prop:\n\n```\nconst TextareaActivator = ({ onFocus: onFocus$1, ...rest }) => {\n  const { onFocus } = useContextActivator('onFocus', onFocus$1);\n  return <textarea onFocus={onFocus} {...rest} />\n}\n```\nIn the above example, the `textarea` will activate the context on focus, and\nwill then invoke any 'onFocus' handler passed to it.\n\nNote that we are using the `isActive` property of the context to render\ndifferently if the current context is active, and using the `isEdit` property to\ndetermine if we are in edit mode. Note also that we wrap our component in a\n[mobx observer](https://mobx-react.js.org/observer-hoc) to ensure that it\nupdates properly if the active context or edit mode changes.\n\n> Important! For functional components using hooks, we must use the version of\n> `observer` from mobx-react.\n\nIt is also possible to use the `withContextActivator` HOC to provide an activation\nevent to a pre-existing component:\n```\nconst ComponentWithMenuOption = flowRight(\n  withMenuOptions({ ... }), // See abo ve\n  withContextActivator('onClick'),\n)(AnyComponent)\n```\n\nNow, when you click on `AnyComponent` it will activate the provided context, and\nthe associated menu options will be displayed.\n\nNote - the above will only work if `AnyComponent` can accept an `onClick` prop.\nIf not, you will want to wrap it in an element which can, for example a `div`:\n```\nconst ComponentWithMenuOption = flowRight(\n  withMenuOptions({ ... }), // See above\n  withContextActivator('onClick'),\n  withActivatorWrapper('div'),\n)(AnyComponent)\n```\n\nFinally, you will want to be sure that none of the above HOC's are applied when not in edit mode.\nFor this, `ifEditable` comes in handy:\n```\nconst ComponentWithMenuOption = ifEditable(\n  withMenuOptions({ ... }), // See above\n  withContextActivator('onClick'),\n  withActivatorWrapper('div'),\n)(AnyComponent)\n```\nNote that the order of these HOC's is important.\n\n## Context Menu Options\n\nThe Global Context Menu aggregates menu options provided by all contexts within\nthe [active context trail](#context-trail). It is a wrapper around the generic\n`ContextMenu` (../packages/bodiless-core/src/components/ContextMenu.tsx)\ncomponent, which provides a mechanism for displaying a set of menu options\nprovided in an \"options\" prop. Each item is an object with the following\nmembers:\n- name: a unique machine name for this item.\n- group: the machine name of the group to which this button belongs\n- Component: An optional component to use to render this button.\n- icon: the name of a [material design icon](https://material.io/tools/icons/?style=baseline)\n  to display for this item.\n- label: a human readable label to display beneath the icon.\n- isActive; a boolean specifying the option or its flyout panel is currently\n  \"active\". This is usually used for toggles.\n- isDisabled; a boolean specifying whether the option is currently \"disabled\".\n  If true, the menu button will be greyed out and bnot clickable.\n- isHidden: a boolean specifying whether the option is currently hidden. If\n  true, the menu button will not be displayed.\n- handler: a callback to invoke when the menu button is clicked.\n\n> Note all of the above properties except `handler`, `name`, `group` and\n> `Component` can be provided either as a primitive value or as a function\n> returning that value - eg `{ isActive: true }` or `{ isActive: () => true; }`\n> The latter is useful if the value depends on some external state which is not\n> used by the component providing the button, to allow the button to update\n> without re-rendering the component. If you provide callbacks, it is important\n> to memoize any such callbacks to avoid unnecessary renders of the button\n> itself. The `handler` callback, however, need not be memoized.\n\n A simple context menu implementation with a single option might look like this:\n ```javascript\nconst [isUp, setIsUp] = React.useState(false);\n\nconst options = [\n{\n  name: 'say_yes',\n  icon: isUp ? 'thumb_up' : 'thumb_down',\n  label: 'Yes!',\n  isActive: isUp,\n  handler: () => {\n    if (!isUp) {\n      alert('Yes!');\n    }\n    setIsUp(isUp => !isUp);\n  },\n];\n\nconst MyContextMenu = props => (\n  <ContextMenu options={options} />\n);\n```\n\n#### Global and Local Context Menu\n\nBy default, Bodiless provides two `ContextMenu` instances to display menu options\nprovided by edit contexts. \n\nThe `GlobalContextMenu` is a part of the `PageEditor` and is intended to provide\na single, top-level menu for the page.\n\nThe `LocalContextMenu` component may be used to add a tooltip version of the\ncontext menu to any component, as\n\n```\n<ContextProvider getMenuOptions={myComponentMenuOptionGetter} name=\"My Component\">\n  <LocalContextMenu>\n    <MyComponent>\n    ...\n    </MyComponent>\n  </LocalContextMenu>\n</ContextProvider>\n```\n\nThis will provide a tooltip for `MyComponent` which displays a local version of the context menu.\n\nWhen defining a menu option, there are two flags you can use to control whether\nit should appear on the global context menu, the local context menu or both:\n\n```javascript\nconst myMenuOption = {\n  ...,\n  global: true // Show on global context menu\n  local: false // Hide on local context menu\n}\n```\n\n#### Context menu forms\n\nThe handler function for a menu option can, optionally, return a render\nfunction. If it does, when the menu item is selected, this will be invoked to\nrender the contents of a fly-out panel (usually a form). This allows menu\noptions to trigger display of a configuration form to collect additional user\ninput. For example, an image component might provide a menu option to configure\nits content. This might then display a form for uploading the image, entering\nalt-text, etc.\n\n## Styling\n\nThis library includes components which render visible, interactive pieces of the edit UI. These include:\n\n- ContextMenu\n- PageEditor\n- LocalContextMenu\n- ContextWrapper\n\nThese components allow injection of UI elements via a `ui` prop. This prop is a\njavascript object enumerating the UI elements which the component will use. For example,\nthe UI specification for a ContextMenu is:\n\n```javascript\nexport type UI = {\n  Icon?: ComponentType<HTMLProps<HTMLSpanElement>>;\n  Toolbar?: ComponentType<HTMLProps<HTMLDivElement>>;\n  ToolbarButton?: ComponentType<ButtonVariantProps>;\n  FormWrapper?: ComponentType<HTMLProps<HTMLDivElement>>;\n  ToolbarDivider?: ComponentType<HTMLProps<HTMLHRElement>>;\n};\n```\n\nThe `ui` prop fo `ContextMenu` accepts low-level UI elements which are composed\nto render the menu. In contrast, that for `LocalContextMenu` and `PageEditor`\nsimply lists the menu component which should be used to render the currently\nselected options:\n\n```\ntype UI = {\n  LocalContextMenu?: ComponentType<ContextMenuProps>;\n};\n```\n\nThis allows us to create customized versions of the context menu, as is done in\nthe canvas-x\nstylable `ContextMenu` (../packages/bodiless-core-ui/src/GlobalContextMenu.tsx)\nand\n `LocalContextMenu` (../packages/bodiless-core-ui/src/LocalContextMenu.tsx).\n\n## Activate Context System\n\nThe activate Context system allow for one component to store an id of another\ncomponent that should activate its context on creation.\n\nThis is a three step process.\n\n### Step 1 Ensure there is a Activate Context in place\n\nOne can wrap there components in a `<ActivateContextProvider>` component or one\ncan use the withActivateContext() HOC that will wrap a component in the Provider\n\n### Step 2 set the Id\n\nThen one needs to set the id of the component to be activated by pulling `setId` from `useActivateContext`.\n\n``` tsx\nconst { setId } from useActivateContext();\nsetId(id);\n```\n\n### Step 3  Add useActivateOnEffect hook\n\nFinally, the component needs to have the `useActivateOnEffect` hook so that it will activate if the id match the one stored\n\n``` tsx\nuseActivateOnEffect(id);\n```\n\n","readmeFilename":"README.md","bugs":{"url":"https://github.com/johnsonandjohnson/bodiless-js/issues"},"homepage":"https://github.com/johnsonandjohnson/bodiless-js#readme","_id":"@asemirsk/core@1.0.0-beta.6","_nodeVersion":"16.13.2","_npmVersion":"lerna/4.0.0/node@v16.13.2+x64 (linux)","dist":{"integrity":"sha512-3WhJpW7K7HtZRJv6bODSv8k1nbp8Zh3ovn3N2bCDxQeTiqeaoMC1xowrQaDollwTTznOp9kAaQRJSy0I+6v6Wg==","shasum":"6435024e0287dea47f258c88f8f5cfbb072184ac","tarball":"https://registry.npmjs.org/@asemirsk/core/-/core-1.0.0-beta.6.tgz","fileCount":4,"unpackedSize":27665,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQCSgyqACymboy6fRgobRoS5kPIySCz/nl0vXXddLG0h8QIhAOof6mdlaRddsnAARUXG1mOHW4PuRlsVfLWTLSi9Mh4k"}],"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJiUJRyACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmrIMxAAlMD4nFgyaM9wTPsVPgg0APQxRb8+a97/X6ugZPmSLMnib/Gv\r\n8MdKB7HRBnxkG1zmwaLejJS0c7KClntRvOaaZNQBLmju2lOegaj2pcXqHbtZ\r\nD5TLQqrJAOywjSDr2dIuP0vCvNo8KUfFu6mUZJRBtO/4Wr8m69rZ5TsZljj/\r\nU10xhLIA/LJASz6pY1P8700oIFXkuP/UVqzuTVxNZ2I9HapXTcagexZSiYcn\r\nLC5Vk5yESM3Un14xA4QH/B+77BeJzJIlrPakG7/3AYpJ0NirxMgRDDEnb91H\r\nSh35Es/WgW72pRXSERdU6g6qMCiPiPdQ4ZbjtLMdd51J+Xxa6UwUVwwU5P58\r\n72YkPaZgfn573r3GLZB3dFxxe9STGZyaAvCCfJTtPt8Nft2neISMPX6Gf9p6\r\nBbYqBon6tduc9qTyi2nQ7JVNmK3WwQI7EJcccD0YwgFE2cNNZ5BaPwgzpoXH\r\nyuxUUGcCEunbly7wep+n+287bgvS/wWGQKQhAIMS01riEcJvFt2LnTbwFFjJ\r\n0bOwxYu5nF21h0Wf5BhSuZiSuc3Dk3iAzykgq8DYcNMnPdiOH8CPV4U1RpxY\r\nlAKRn8naq50T+DfrciMINdW4LKT6l0EMYePFuWO4qwSqNyOqG6zqZMbDLSZg\r\np8X65ychN5j/XhW/Y9Tt+ceIgz911+AWxpM=\r\n=KWbD\r\n-----END PGP SIGNATURE-----\r\n"},"_npmUser":{"name":"asemirsk","email":"al.semirski@gmail.com"},"directories":{},"maintainers":[{"name":"asemirsk","email":"al.semirski@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/core_1.0.0-beta.6_1649448050682_0.47977812755714844"},"_hasShrinkwrap":false},"1.0.1-beta.6":{"name":"@asemirsk/core","version":"1.0.1-beta.6","description":"Edit context for BodilessJS site editor","author":{"name":"Chris Oden","email":"coden@its.jnj.com"},"license":"Apache-2.0","main":"lib/index.js","sideEffects":false,"typings":"lib/index.d.ts","repository":{"type":"git","url":"git+https://github.com/johnsonandjohnson/bodiless-js.git"},"publishConfig":{"access":"public"},"scripts":{"build":"tsc -p ./tsconfig.json && npm run build:api-doc","build:watch":"npm run build -- --watch","build:api-doc":"typedoc --out doc/api src","lint":"tslint ./src/**/*.ts?x","clean":"rimraf \"lib/*\" && rimraf tsconfig.tsbuildinfo && rimraf \"doc/api\""},"dependencies":{"@asemirsk/cli":"^1.0.1-beta.6","@asemirsk/fclasses":"^1.0.1-beta.6","@types/react-tag-autocomplete":"^6.0.0","axios":"^0.21.0","enzyme":"^3.9.0","informed":"^3.34.0","lodash":"^4.17.19","rc-tooltip":"^5.1.1","react-tag-autocomplete":"^6.0.0","resize-observer-polyfill":"^1.5.1","universal-cookie":"^4.0.3","uuid":"^3.3.2"},"peerDependencies":{"mobx":"^6.0.0","mobx-react":"^7.2.1","react":"^17.0.2","react-dom":"^17.0.2"},"devDependencies":{"@types/rc-tooltip":"^3.7.1","@types/react-tag-autocomplete":"^6.0.0","@types/uuid":"^3.4.4"},"gitHead":"dfbcb3dab592c76bd48c138c275c1e27fbd9ca1d","readme":"# Edit Context and UI State\n\n## Overview\n\nBodiless uses the [React Context API](https://reactjs.org/docs/context.html) to\nprovide a \"page edit context\"\n(../packages/bodiless-core/src/PageEditContext/index.tsx) instance to all\ncomponents on the page. This is an interface which allows a component to\ncontribute contextual elements to the page edit UI (currently limited to toolbar\nmenu items). For example, an image component might contribute a menu item which\nallows a user to upload an image.\n\nThe edit context also provides an interface to a global store which defines the\nUI state, including:\n\n- which edit context is \"active\" (ie, which component has the current focus),\n  and what related menu options should be available.\n- whether edit mode is active (ie, whether components should expose their edit\n  interfaces)\n- in future, anything else which might affect all components on the page (eg\n  whether the current changes have been pushed, whether there are upstream\n  changes to be pulled, etc).\n\n## Flow\n\nThe following diagram illustrates the flow of data for page edit contexts:\n\n![context](assets/context.jpeg)\n\n1. The `PageEditor`(../packages/bodiless-core/src/components/PageEditor.tsx) (the\n   top-level component of an editable page) defines a base edit context,\n   contributing menu options which apply to the page as a whole (e.g. whether\n   edit mode should be enabled). This is set as the default value of a React\n   context type named \"PageEditContext\". Provider and Consumer components for\n   this context are available as static properties of the PageEditContext class\n   (`PageEditContext.Provider` and `PageEditContext.Consumer`).\n2. Any item on the page which wishes to define a more specific context can do so\n   using the `spawn()` method of the `PageEditContext` instance. This will\n   create a new instance with the current instance as its parent. This can then\n   be set as the new context value using `PageEditContext.Provider`. For\n   convenience, a `ContextProvider`(../packages/bodiless-core/src/PageContextProvider.tsx)\n   component is provided which creates a new context and injects it into its\n   children (provided as a render prop).\n3. A component which does not want to create a new context can either consume\n   the current context (either via `PageEditContext.Consumer` or via the\n   `useEditContext()` hook), or simply ignore it, if it doesn't care about the\n   current context or UI state.\n4. The `PageEditContext` instance exposes an `activate()` method which can be\n   used by a component to declare that its context is active (usually when the\n   user focuses there).  This sets the active context in the store. If the active\n   context has parents, then these are also considered active, and the set of\n   all active contexts is referred to as the <a id=\"context-trail\"></a>\n   _*context trail*_. For convenience a `useContextActivator()` hook is provided.\n5. This, in turn triggers any observer component to re-render (for example to\n   highlight the fact that it is active).\n6. The \"global context menu\"(../packages/bodiless-core/src/components/PageEditor.tsx)\n   is one such observer component, and re-renders itself with all menu options\n   contributed by the newly activated context and all its parents. Thus, for\n   example, when you activate the context of an image within a grid, the menu\n   options of the image (eg set source) are added to those of the grid (eg\n   insert/delete items), which in turn are added to the page level items (eg\n   toggle edit on/off).\n7. Similarly, the\n   \"local context menu\"(../packages/bodiless-core/src/components/LocalContextMenu.tsx)\n   is another observer which renders a subset of context menu options as a tooltip\n   near the component providing current innermost active context.\n\n## Working with edit context\n\nThere are several ways in which a component can provide or consume the edit context.\n\n### Providing a new context\n\nTo provide a new context value (usually to add menu options which should appear\nwhen a component has focus), you may use the supplied `PageContextProvider`\n\n```javascript\nconst getExampleMenuOptions = useGetter([{\n  // An array of context menu option objects\n}]);\n\nconst Example: React.FC = ({ children }) => (\n  <PageContextProvider getMenuOptions={getExampleMenuOptions} name=\"Example\">\n    {children}\n  </PageContextProvider}\n```\nHere we provide a menu options callback as a prop to the `ContextProvider`\ncomponent. This will be invoked when this context or any descendant is\nactivated. It should return any menu options this component wishes to provide\n(see [Context Menu Options](#context-menu-options) below for more information).\n\n> Note: It is important to memoize our `getMenuOptions()` callback so as to\n> prevent unnecessary renders of subscribers to the edit context. Here, we do\n> this with the bodilesscore `useGetter()` hook, which creates an invariant\n> callback even if the menu options it returns change.\n\nIt is actually unusual to invoke the provider directly in this manner. Instead,\nuse the `withMenuOptions` hoc to attach options to your component. First you\ndefine a custom hook which will create your `getMenuOptions()` callback. This\nhook will be invoked when the component is rendered, and will receive its props\nas an argument:\n\n```\nconst useMenuOptions = (props) => {\n  return [{\n    // An array of context menu option objects\n  }];\n};\n```\n\nThen, pass that along with a unique name to `withMenuOptions` to create an HOC\nwhich will add the options to your component.\n\n```\nconst ComponentWithMyOptions = withMenuOptions({\n  useMenuOptions,\n  name: 'my-component'\n})(AnyComponent);\n...\n<ComponentWithMyOptions propUsedInOption=\"foo\" />\n```\nNote here that we don't need to memoize the list of menu options: memoization of\nthe callback is handled within `withMenuOptions`. However, certain callbacks\nprovided as properties of the menu option object may need to be memoized (see\n[Context Menu Options](#context-menu-options) below for more information).\n\nNote that the menu options you are defining here will only be available when\nyour component (or one of its children) declares itself as \"active\". To do so,\nit will need to use a method on the current context.\n\n### Consuming the context with hooks\n\nTo access the current page edit context, simply use the `useEditContext()`\nand/or `useContextActivator()` hooks.\n\n```javascript\nimport { observer } from 'mobx-react';\nconst Example = observer(props => {\n  const { isActive, isEdit } = useEditContext();\n  const { onClick } = useContextActivator();\n  return (\n    <React.Fragment>\n      <div>{isActive ? 'Active' : 'Not Active'}</div>\n      {isEdit ?\n        <button onClick={onClick} />\n        : <span>Not editing!</span>\n      }\n    </React.Fragment>\n  );\n});\n```\n\nNote that `useContextActivator()` can be used to provide a handler for other\nevents besides `onClick`, and can invoke another handler passed in as a prop:\n\n```\nconst TextareaActivator = ({ onFocus: onFocus$1, ...rest }) => {\n  const { onFocus } = useContextActivator('onFocus', onFocus$1);\n  return <textarea onFocus={onFocus} {...rest} />\n}\n```\nIn the above example, the `textarea` will activate the context on focus, and\nwill then invoke any 'onFocus' handler passed to it.\n\nNote that we are using the `isActive` property of the context to render\ndifferently if the current context is active, and using the `isEdit` property to\ndetermine if we are in edit mode. Note also that we wrap our component in a\n[mobx observer](https://mobx-react.js.org/observer-hoc) to ensure that it\nupdates properly if the active context or edit mode changes.\n\n> Important! For functional components using hooks, we must use the version of\n> `observer` from mobx-react.\n\nIt is also possible to use the `withContextActivator` HOC to provide an activation\nevent to a pre-existing component:\n```\nconst ComponentWithMenuOption = flowRight(\n  withMenuOptions({ ... }), // See abo ve\n  withContextActivator('onClick'),\n)(AnyComponent)\n```\n\nNow, when you click on `AnyComponent` it will activate the provided context, and\nthe associated menu options will be displayed.\n\nNote - the above will only work if `AnyComponent` can accept an `onClick` prop.\nIf not, you will want to wrap it in an element which can, for example a `div`:\n```\nconst ComponentWithMenuOption = flowRight(\n  withMenuOptions({ ... }), // See above\n  withContextActivator('onClick'),\n  withActivatorWrapper('div'),\n)(AnyComponent)\n```\n\nFinally, you will want to be sure that none of the above HOC's are applied when not in edit mode.\nFor this, `ifEditable` comes in handy:\n```\nconst ComponentWithMenuOption = ifEditable(\n  withMenuOptions({ ... }), // See above\n  withContextActivator('onClick'),\n  withActivatorWrapper('div'),\n)(AnyComponent)\n```\nNote that the order of these HOC's is important.\n\n## Context Menu Options\n\nThe Global Context Menu aggregates menu options provided by all contexts within\nthe [active context trail](#context-trail). It is a wrapper around the generic\n`ContextMenu` (../packages/bodiless-core/src/components/ContextMenu.tsx)\ncomponent, which provides a mechanism for displaying a set of menu options\nprovided in an \"options\" prop. Each item is an object with the following\nmembers:\n- name: a unique machine name for this item.\n- group: the machine name of the group to which this button belongs\n- Component: An optional component to use to render this button.\n- icon: the name of a [material design icon](https://material.io/tools/icons/?style=baseline)\n  to display for this item.\n- label: a human readable label to display beneath the icon.\n- isActive; a boolean specifying the option or its flyout panel is currently\n  \"active\". This is usually used for toggles.\n- isDisabled; a boolean specifying whether the option is currently \"disabled\".\n  If true, the menu button will be greyed out and bnot clickable.\n- isHidden: a boolean specifying whether the option is currently hidden. If\n  true, the menu button will not be displayed.\n- handler: a callback to invoke when the menu button is clicked.\n\n> Note all of the above properties except `handler`, `name`, `group` and\n> `Component` can be provided either as a primitive value or as a function\n> returning that value - eg `{ isActive: true }` or `{ isActive: () => true; }`\n> The latter is useful if the value depends on some external state which is not\n> used by the component providing the button, to allow the button to update\n> without re-rendering the component. If you provide callbacks, it is important\n> to memoize any such callbacks to avoid unnecessary renders of the button\n> itself. The `handler` callback, however, need not be memoized.\n\n A simple context menu implementation with a single option might look like this:\n ```javascript\nconst [isUp, setIsUp] = React.useState(false);\n\nconst options = [\n{\n  name: 'say_yes',\n  icon: isUp ? 'thumb_up' : 'thumb_down',\n  label: 'Yes!',\n  isActive: isUp,\n  handler: () => {\n    if (!isUp) {\n      alert('Yes!');\n    }\n    setIsUp(isUp => !isUp);\n  },\n];\n\nconst MyContextMenu = props => (\n  <ContextMenu options={options} />\n);\n```\n\n#### Global and Local Context Menu\n\nBy default, Bodiless provides two `ContextMenu` instances to display menu options\nprovided by edit contexts. \n\nThe `GlobalContextMenu` is a part of the `PageEditor` and is intended to provide\na single, top-level menu for the page.\n\nThe `LocalContextMenu` component may be used to add a tooltip version of the\ncontext menu to any component, as\n\n```\n<ContextProvider getMenuOptions={myComponentMenuOptionGetter} name=\"My Component\">\n  <LocalContextMenu>\n    <MyComponent>\n    ...\n    </MyComponent>\n  </LocalContextMenu>\n</ContextProvider>\n```\n\nThis will provide a tooltip for `MyComponent` which displays a local version of the context menu.\n\nWhen defining a menu option, there are two flags you can use to control whether\nit should appear on the global context menu, the local context menu or both:\n\n```javascript\nconst myMenuOption = {\n  ...,\n  global: true // Show on global context menu\n  local: false // Hide on local context menu\n}\n```\n\n#### Context menu forms\n\nThe handler function for a menu option can, optionally, return a render\nfunction. If it does, when the menu item is selected, this will be invoked to\nrender the contents of a fly-out panel (usually a form). This allows menu\noptions to trigger display of a configuration form to collect additional user\ninput. For example, an image component might provide a menu option to configure\nits content. This might then display a form for uploading the image, entering\nalt-text, etc.\n\n## Styling\n\nThis library includes components which render visible, interactive pieces of the edit UI. These include:\n\n- ContextMenu\n- PageEditor\n- LocalContextMenu\n- ContextWrapper\n\nThese components allow injection of UI elements via a `ui` prop. This prop is a\njavascript object enumerating the UI elements which the component will use. For example,\nthe UI specification for a ContextMenu is:\n\n```javascript\nexport type UI = {\n  Icon?: ComponentType<HTMLProps<HTMLSpanElement>>;\n  Toolbar?: ComponentType<HTMLProps<HTMLDivElement>>;\n  ToolbarButton?: ComponentType<ButtonVariantProps>;\n  FormWrapper?: ComponentType<HTMLProps<HTMLDivElement>>;\n  ToolbarDivider?: ComponentType<HTMLProps<HTMLHRElement>>;\n};\n```\n\nThe `ui` prop fo `ContextMenu` accepts low-level UI elements which are composed\nto render the menu. In contrast, that for `LocalContextMenu` and `PageEditor`\nsimply lists the menu component which should be used to render the currently\nselected options:\n\n```\ntype UI = {\n  LocalContextMenu?: ComponentType<ContextMenuProps>;\n};\n```\n\nThis allows us to create customized versions of the context menu, as is done in\nthe canvas-x\nstylable `ContextMenu` (../packages/bodiless-core-ui/src/GlobalContextMenu.tsx)\nand\n `LocalContextMenu` (../packages/bodiless-core-ui/src/LocalContextMenu.tsx).\n\n## Activate Context System\n\nThe activate Context system allow for one component to store an id of another\ncomponent that should activate its context on creation.\n\nThis is a three step process.\n\n### Step 1 Ensure there is a Activate Context in place\n\nOne can wrap there components in a `<ActivateContextProvider>` component or one\ncan use the withActivateContext() HOC that will wrap a component in the Provider\n\n### Step 2 set the Id\n\nThen one needs to set the id of the component to be activated by pulling `setId` from `useActivateContext`.\n\n``` tsx\nconst { setId } from useActivateContext();\nsetId(id);\n```\n\n### Step 3  Add useActivateOnEffect hook\n\nFinally, the component needs to have the `useActivateOnEffect` hook so that it will activate if the id match the one stored\n\n``` tsx\nuseActivateOnEffect(id);\n```\n\n","readmeFilename":"README.md","bugs":{"url":"https://github.com/johnsonandjohnson/bodiless-js/issues"},"homepage":"https://github.com/johnsonandjohnson/bodiless-js#readme","_id":"@asemirsk/core@1.0.1-beta.6","_nodeVersion":"16.13.2","_npmVersion":"lerna/4.0.0/node@v16.13.2+x64 (linux)","dist":{"integrity":"sha512-RpK0bYsJS+2SaPn/MjuvcS4YnOOSsx/mRQIwRYobN+wT2Z3jvnU4JkN/I3Uxa94R2YvAfndid0ai47IB47neTA==","shasum":"5878b6dcf22f3f1e9d9d003a78a13841c3e1cb5f","tarball":"https://registry.npmjs.org/@asemirsk/core/-/core-1.0.1-beta.6.tgz","fileCount":4,"unpackedSize":27665,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIH+asBqZKzXxZxc/D57uD/A0NZUSsyMJ3kJUscD5X7R/AiAS+207XVoOxVHvNRXk6wK92r0yCkQCoARwxwB0frfAZA=="}],"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJiUJVuACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmrzZxAAiwTxmhwuHI0+s9E9/WCH38ZzV6eqkH8XDhCNk/ArcvvhtIEW\r\nNhV/IbQl+oFxdtwpLU4/DujQMBsRhOsuHcaXmYlDqPyJJXnaA0xMgxVrTy70\r\nMBgyH8ZpCH3VcFWwq0M+bQt3mqTCCtQG9uBGAVR4wiB2HZ+WtKcMHsRzd58I\r\nYdBkTEQnuw5TOmcSAy7n3ZPGA84YQ1IasWCLn+AfN5c4jdENns8v+at0L9ON\r\n7cyC8xVngg6AlL7svRmNZYTx5yTOLbTOVVn6kHTJHIySKGggF9zCdUVNnjoy\r\nSwMKqpegzFVGPVAtnXxaYuEth6e36wIQPROelrZF8OwpbuVjb6Pgqc1/F1qH\r\nphign8A1DwZ/oxglr+gpbA0ZKd3idWjSlYbv6BIhMIgcM/sV3Zbhz73TwmA/\r\nzt8dJ4X9UTdx+awK/ppch6ay0U0UA/5JWCK7poKwVUtkkPSB/GnXBI5UgNqq\r\nmedtN9xJPRZobDXpeIGYcCXFESLicm3dISoyHIPQ2QgiXJZx8jcnSXGlvceb\r\nEZMYqWOUrGoyqxvQEwoPm7pAxM3Fz1Offc1YDtQc8eYwsDMtIum7LQ7/MyI1\r\nBqq0W7WpbvfY4zRT6Mgo8C1H70BmuCME7LzDQDSQ5WtWe5j86X2pECKfeies\r\nBtZgBtveZeOJMjSucedejnB3Z6+QAwk4p/s=\r\n=A5qq\r\n-----END PGP SIGNATURE-----\r\n"},"_npmUser":{"name":"asemirsk","email":"al.semirski@gmail.com"},"directories":{},"maintainers":[{"name":"asemirsk","email":"al.semirski@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/core_1.0.1-beta.6_1649448302547_0.19516348354652413"},"_hasShrinkwrap":false},"1.0.1":{"name":"@asemirsk/core","version":"1.0.1","description":"Edit context for BodilessJS site editor","author":{"name":"Chris Oden","email":"coden@its.jnj.com"},"license":"Apache-2.0","main":"lib/index.js","sideEffects":false,"typings":"lib/index.d.ts","repository":{"type":"git","url":"git+https://github.com/johnsonandjohnson/bodiless-js.git"},"publishConfig":{"access":"public"},"scripts":{"build":"tsc -p ./tsconfig.json && npm run build:api-doc","build:watch":"npm run build -- --watch","build:api-doc":"typedoc --out doc/api src","lint":"tslint ./src/**/*.ts?x","clean":"rimraf \"lib/*\" && rimraf tsconfig.tsbuildinfo && rimraf \"doc/api\""},"dependencies":{"@asemirsk/cli":"^1.0.1","@asemirsk/fclasses":"^1.0.1","@types/react-tag-autocomplete":"^6.0.0","axios":"^0.21.0","enzyme":"^3.9.0","informed":"^3.34.0","lodash":"^4.17.19","rc-tooltip":"^5.1.1","react-tag-autocomplete":"^6.0.0","resize-observer-polyfill":"^1.5.1","universal-cookie":"^4.0.3","uuid":"^3.3.2"},"peerDependencies":{"mobx":"^6.0.0","mobx-react":"^7.2.1","react":"^17.0.2","react-dom":"^17.0.2"},"devDependencies":{"@types/rc-tooltip":"^3.7.1","@types/react-tag-autocomplete":"^6.0.0","@types/uuid":"^3.4.4"},"gitHead":"b943f745d8dc223a872b770f79f3e0db78f43486","bugs":{"url":"https://github.com/johnsonandjohnson/bodiless-js/issues"},"homepage":"https://github.com/johnsonandjohnson/bodiless-js#readme","_id":"@asemirsk/core@1.0.1","_nodeVersion":"16.13.2","_npmVersion":"lerna/4.0.0/node@v16.13.2+x64 (linux)","dist":{"integrity":"sha512-iDsqOcqwlpDLJ1HEYg824R4G2+FasEco9DG1U9rtQM/8Rgpb+bhsr78ihwVjJHOctqcXP4M94chwM8hqlkZNOQ==","shasum":"1b376e2bcb7e72f8349e34b3cea960633bb3a161","tarball":"https://registry.npmjs.org/@asemirsk/core/-/core-1.0.1.tgz","fileCount":4,"unpackedSize":27644,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIAEy2WlMhQJ4IKctEvHZ5OeNkHIMZ9B8DOqdnFIw9+RlAiEA4+Q50Sxlz6iiku2vHS+PBkgJapXJX4xvelLnlcz/QAU="}],"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJiUJaHACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2Vmpq6g/+Jq8H1r+ZG7tjxc9PNa3xzDJwlj2hkTRkq/vLtEQ32ekjHlXW\r\ng4qHQsXpJcJmkfDoL1Dya/+32ikkeOrULcUrF+2aZDWWY41oKfX4GyJDxhXA\r\n1won9c5Mqy670zhef8KVF499OksRi1QIMaIPgC0zE4GZPOAaj6CN7+QpSwuf\r\naCQ4afalbbzvjJK+0WYjW78A0b+SXYW+CoKATIX2krj8VgQodWbCI9tjKW0C\r\npvkLQ2n+uCKVScLPqlBVKsqE4+hRa5fq4sdikLBfdlJrAy4GWJl8RPasAoZt\r\n/6Be3niJM/OedHpqgT/fcuM2BJI9yRwGeRIo+Ek4agw7vNzYy82tHp7Xi1yF\r\n9RoePc2U9Bvbn3pYrdfToJ8a0UMhNf9xmFdVYPrbAb9pZu/REog7uJh2EO/F\r\nvqBNmIGmxJNLA/DLdMEkj8qE1+UdeKuN4po4DCF68/rz6OMdtmgfdXJREUKW\r\nhVCs9c1s9qsG9aEWw9MzgJ3dplTXEfMGcLINgMVM9SqANdr1fNCK4Ed1lwOA\r\n/VbPSIHM0JRI9cDiLD1boB7wzh4q3oxZuOYQQ48l3DdBt5o/vnXUQzJ9Ler5\r\no0QvuTXPOeqm8kYVmIyl7xRyC0bUdpw7Zsno6iXeEymSNsVNqKxLf9vhF9PX\r\ni540rz1LjKFLTS7PasYAx1HPhgaixC3sQQU=\r\n=F7Qa\r\n-----END PGP SIGNATURE-----\r\n"},"_npmUser":{"name":"asemirsk","email":"al.semirski@gmail.com"},"directories":{},"maintainers":[{"name":"asemirsk","email":"al.semirski@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/core_1.0.1_1649448583396_0.05213950278716073"},"_hasShrinkwrap":false},"1.0.2-beta.0":{"name":"@asemirsk/core","version":"1.0.2-beta.0","description":"Edit context for BodilessJS site editor","author":{"name":"Chris Oden","email":"coden@its.jnj.com"},"license":"Apache-2.0","main":"lib/index.js","sideEffects":false,"typings":"lib/index.d.ts","repository":{"type":"git","url":"git+https://github.com/johnsonandjohnson/bodiless-js.git"},"publishConfig":{"access":"public"},"scripts":{"build":"tsc -p ./tsconfig.json && npm run build:api-doc","build:watch":"npm run build -- --watch","build:api-doc":"typedoc --out doc/api src","lint":"tslint ./src/**/*.ts?x","clean":"rimraf \"lib/*\" && rimraf tsconfig.tsbuildinfo && rimraf \"doc/api\""},"dependencies":{"@asemirsk/cli":"^1.0.2-beta.0","@asemirsk/fclasses":"^1.0.2-beta.0","@types/react-tag-autocomplete":"^6.0.0","axios":"^0.21.0","enzyme":"^3.9.0","informed":"^3.34.0","lodash":"^4.17.19","rc-tooltip":"^5.1.1","react-tag-autocomplete":"^6.0.0","resize-observer-polyfill":"^1.5.1","universal-cookie":"^4.0.3","uuid":"^3.3.2"},"peerDependencies":{"mobx":"^6.0.0","mobx-react":"^7.2.1","react":"^17.0.2","react-dom":"^17.0.2"},"devDependencies":{"@types/rc-tooltip":"^3.7.1","@types/react-tag-autocomplete":"^6.0.0","@types/uuid":"^3.4.4"},"gitHead":"da62659a8fb42aa89cdc8cae2f148c8dd8db5dcb","readme":"# Edit Context and UI State\n\n## Overview\n\nBodiless uses the [React Context API](https://reactjs.org/docs/context.html) to\nprovide a \"page edit context\"\n(../packages/bodiless-core/src/PageEditContext/index.tsx) instance to all\ncomponents on the page. This is an interface which allows a component to\ncontribute contextual elements to the page edit UI (currently limited to toolbar\nmenu items). For example, an image component might contribute a menu item which\nallows a user to upload an image.\n\nThe edit context also provides an interface to a global store which defines the\nUI state, including:\n\n- which edit context is \"active\" (ie, which component has the current focus),\n  and what related menu options should be available.\n- whether edit mode is active (ie, whether components should expose their edit\n  interfaces)\n- in future, anything else which might affect all components on the page (eg\n  whether the current changes have been pushed, whether there are upstream\n  changes to be pulled, etc).\n\n## Flow\n\nThe following diagram illustrates the flow of data for page edit contexts:\n\n![context](assets/context.jpeg)\n\n1. The `PageEditor`(../packages/bodiless-core/src/components/PageEditor.tsx) (the\n   top-level component of an editable page) defines a base edit context,\n   contributing menu options which apply to the page as a whole (e.g. whether\n   edit mode should be enabled). This is set as the default value of a React\n   context type named \"PageEditContext\". Provider and Consumer components for\n   this context are available as static properties of the PageEditContext class\n   (`PageEditContext.Provider` and `PageEditContext.Consumer`).\n2. Any item on the page which wishes to define a more specific context can do so\n   using the `spawn()` method of the `PageEditContext` instance. This will\n   create a new instance with the current instance as its parent. This can then\n   be set as the new context value using `PageEditContext.Provider`. For\n   convenience, a `ContextProvider`(../packages/bodiless-core/src/PageContextProvider.tsx)\n   component is provided which creates a new context and injects it into its\n   children (provided as a render prop).\n3. A component which does not want to create a new context can either consume\n   the current context (either via `PageEditContext.Consumer` or via the\n   `useEditContext()` hook), or simply ignore it, if it doesn't care about the\n   current context or UI state.\n4. The `PageEditContext` instance exposes an `activate()` method which can be\n   used by a component to declare that its context is active (usually when the\n   user focuses there).  This sets the active context in the store. If the active\n   context has parents, then these are also considered active, and the set of\n   all active contexts is referred to as the <a id=\"context-trail\"></a>\n   _*context trail*_. For convenience a `useContextActivator()` hook is provided.\n5. This, in turn triggers any observer component to re-render (for example to\n   highlight the fact that it is active).\n6. The \"global context menu\"(../packages/bodiless-core/src/components/PageEditor.tsx)\n   is one such observer component, and re-renders itself with all menu options\n   contributed by the newly activated context and all its parents. Thus, for\n   example, when you activate the context of an image within a grid, the menu\n   options of the image (eg set source) are added to those of the grid (eg\n   insert/delete items), which in turn are added to the page level items (eg\n   toggle edit on/off).\n7. Similarly, the\n   \"local context menu\"(../packages/bodiless-core/src/components/LocalContextMenu.tsx)\n   is another observer which renders a subset of context menu options as a tooltip\n   near the component providing current innermost active context.\n\n## Working with edit context\n\nThere are several ways in which a component can provide or consume the edit context.\n\n### Providing a new context\n\nTo provide a new context value (usually to add menu options which should appear\nwhen a component has focus), you may use the supplied `PageContextProvider`\n\n```javascript\nconst getExampleMenuOptions = useGetter([{\n  // An array of context menu option objects\n}]);\n\nconst Example: React.FC = ({ children }) => (\n  <PageContextProvider getMenuOptions={getExampleMenuOptions} name=\"Example\">\n    {children}\n  </PageContextProvider}\n```\nHere we provide a menu options callback as a prop to the `ContextProvider`\ncomponent. This will be invoked when this context or any descendant is\nactivated. It should return any menu options this component wishes to provide\n(see [Context Menu Options](#context-menu-options) below for more information).\n\n> Note: It is important to memoize our `getMenuOptions()` callback so as to\n> prevent unnecessary renders of subscribers to the edit context. Here, we do\n> this with the bodilesscore `useGetter()` hook, which creates an invariant\n> callback even if the menu options it returns change.\n\nIt is actually unusual to invoke the provider directly in this manner. Instead,\nuse the `withMenuOptions` hoc to attach options to your component. First you\ndefine a custom hook which will create your `getMenuOptions()` callback. This\nhook will be invoked when the component is rendered, and will receive its props\nas an argument:\n\n```\nconst useMenuOptions = (props) => {\n  return [{\n    // An array of context menu option objects\n  }];\n};\n```\n\nThen, pass that along with a unique name to `withMenuOptions` to create an HOC\nwhich will add the options to your component.\n\n```\nconst ComponentWithMyOptions = withMenuOptions({\n  useMenuOptions,\n  name: 'my-component'\n})(AnyComponent);\n...\n<ComponentWithMyOptions propUsedInOption=\"foo\" />\n```\nNote here that we don't need to memoize the list of menu options: memoization of\nthe callback is handled within `withMenuOptions`. However, certain callbacks\nprovided as properties of the menu option object may need to be memoized (see\n[Context Menu Options](#context-menu-options) below for more information).\n\nNote that the menu options you are defining here will only be available when\nyour component (or one of its children) declares itself as \"active\". To do so,\nit will need to use a method on the current context.\n\n### Consuming the context with hooks\n\nTo access the current page edit context, simply use the `useEditContext()`\nand/or `useContextActivator()` hooks.\n\n```javascript\nimport { observer } from 'mobx-react';\nconst Example = observer(props => {\n  const { isActive, isEdit } = useEditContext();\n  const { onClick } = useContextActivator();\n  return (\n    <React.Fragment>\n      <div>{isActive ? 'Active' : 'Not Active'}</div>\n      {isEdit ?\n        <button onClick={onClick} />\n        : <span>Not editing!</span>\n      }\n    </React.Fragment>\n  );\n});\n```\n\nNote that `useContextActivator()` can be used to provide a handler for other\nevents besides `onClick`, and can invoke another handler passed in as a prop:\n\n```\nconst TextareaActivator = ({ onFocus: onFocus$1, ...rest }) => {\n  const { onFocus } = useContextActivator('onFocus', onFocus$1);\n  return <textarea onFocus={onFocus} {...rest} />\n}\n```\nIn the above example, the `textarea` will activate the context on focus, and\nwill then invoke any 'onFocus' handler passed to it.\n\nNote that we are using the `isActive` property of the context to render\ndifferently if the current context is active, and using the `isEdit` property to\ndetermine if we are in edit mode. Note also that we wrap our component in a\n[mobx observer](https://mobx-react.js.org/observer-hoc) to ensure that it\nupdates properly if the active context or edit mode changes.\n\n> Important! For functional components using hooks, we must use the version of\n> `observer` from mobx-react.\n\nIt is also possible to use the `withContextActivator` HOC to provide an activation\nevent to a pre-existing component:\n```\nconst ComponentWithMenuOption = flowRight(\n  withMenuOptions({ ... }), // See abo ve\n  withContextActivator('onClick'),\n)(AnyComponent)\n```\n\nNow, when you click on `AnyComponent` it will activate the provided context, and\nthe associated menu options will be displayed.\n\nNote - the above will only work if `AnyComponent` can accept an `onClick` prop.\nIf not, you will want to wrap it in an element which can, for example a `div`:\n```\nconst ComponentWithMenuOption = flowRight(\n  withMenuOptions({ ... }), // See above\n  withContextActivator('onClick'),\n  withActivatorWrapper('div'),\n)(AnyComponent)\n```\n\nFinally, you will want to be sure that none of the above HOC's are applied when not in edit mode.\nFor this, `ifEditable` comes in handy:\n```\nconst ComponentWithMenuOption = ifEditable(\n  withMenuOptions({ ... }), // See above\n  withContextActivator('onClick'),\n  withActivatorWrapper('div'),\n)(AnyComponent)\n```\nNote that the order of these HOC's is important.\n\n## Context Menu Options\n\nThe Global Context Menu aggregates menu options provided by all contexts within\nthe [active context trail](#context-trail). It is a wrapper around the generic\n`ContextMenu` (../packages/bodiless-core/src/components/ContextMenu.tsx)\ncomponent, which provides a mechanism for displaying a set of menu options\nprovided in an \"options\" prop. Each item is an object with the following\nmembers:\n- name: a unique machine name for this item.\n- group: the machine name of the group to which this button belongs\n- Component: An optional component to use to render this button.\n- icon: the name of a [material design icon](https://material.io/tools/icons/?style=baseline)\n  to display for this item.\n- label: a human readable label to display beneath the icon.\n- isActive; a boolean specifying the option or its flyout panel is currently\n  \"active\". This is usually used for toggles.\n- isDisabled; a boolean specifying whether the option is currently \"disabled\".\n  If true, the menu button will be greyed out and bnot clickable.\n- isHidden: a boolean specifying whether the option is currently hidden. If\n  true, the menu button will not be displayed.\n- handler: a callback to invoke when the menu button is clicked.\n\n> Note all of the above properties except `handler`, `name`, `group` and\n> `Component` can be provided either as a primitive value or as a function\n> returning that value - eg `{ isActive: true }` or `{ isActive: () => true; }`\n> The latter is useful if the value depends on some external state which is not\n> used by the component providing the button, to allow the button to update\n> without re-rendering the component. If you provide callbacks, it is important\n> to memoize any such callbacks to avoid unnecessary renders of the button\n> itself. The `handler` callback, however, need not be memoized.\n\n A simple context menu implementation with a single option might look like this:\n ```javascript\nconst [isUp, setIsUp] = React.useState(false);\n\nconst options = [\n{\n  name: 'say_yes',\n  icon: isUp ? 'thumb_up' : 'thumb_down',\n  label: 'Yes!',\n  isActive: isUp,\n  handler: () => {\n    if (!isUp) {\n      alert('Yes!');\n    }\n    setIsUp(isUp => !isUp);\n  },\n];\n\nconst MyContextMenu = props => (\n  <ContextMenu options={options} />\n);\n```\n\n#### Global and Local Context Menu\n\nBy default, Bodiless provides two `ContextMenu` instances to display menu options\nprovided by edit contexts. \n\nThe `GlobalContextMenu` is a part of the `PageEditor` and is intended to provide\na single, top-level menu for the page.\n\nThe `LocalContextMenu` component may be used to add a tooltip version of the\ncontext menu to any component, as\n\n```\n<ContextProvider getMenuOptions={myComponentMenuOptionGetter} name=\"My Component\">\n  <LocalContextMenu>\n    <MyComponent>\n    ...\n    </MyComponent>\n  </LocalContextMenu>\n</ContextProvider>\n```\n\nThis will provide a tooltip for `MyComponent` which displays a local version of the context menu.\n\nWhen defining a menu option, there are two flags you can use to control whether\nit should appear on the global context menu, the local context menu or both:\n\n```javascript\nconst myMenuOption = {\n  ...,\n  global: true // Show on global context menu\n  local: false // Hide on local context menu\n}\n```\n\n#### Context menu forms\n\nThe handler function for a menu option can, optionally, return a render\nfunction. If it does, when the menu item is selected, this will be invoked to\nrender the contents of a fly-out panel (usually a form). This allows menu\noptions to trigger display of a configuration form to collect additional user\ninput. For example, an image component might provide a menu option to configure\nits content. This might then display a form for uploading the image, entering\nalt-text, etc.\n\n## Styling\n\nThis library includes components which render visible, interactive pieces of the edit UI. These include:\n\n- ContextMenu\n- PageEditor\n- LocalContextMenu\n- ContextWrapper\n\nThese components allow injection of UI elements via a `ui` prop. This prop is a\njavascript object enumerating the UI elements which the component will use. For example,\nthe UI specification for a ContextMenu is:\n\n```javascript\nexport type UI = {\n  Icon?: ComponentType<HTMLProps<HTMLSpanElement>>;\n  Toolbar?: ComponentType<HTMLProps<HTMLDivElement>>;\n  ToolbarButton?: ComponentType<ButtonVariantProps>;\n  FormWrapper?: ComponentType<HTMLProps<HTMLDivElement>>;\n  ToolbarDivider?: ComponentType<HTMLProps<HTMLHRElement>>;\n};\n```\n\nThe `ui` prop fo `ContextMenu` accepts low-level UI elements which are composed\nto render the menu. In contrast, that for `LocalContextMenu` and `PageEditor`\nsimply lists the menu component which should be used to render the currently\nselected options:\n\n```\ntype UI = {\n  LocalContextMenu?: ComponentType<ContextMenuProps>;\n};\n```\n\nThis allows us to create customized versions of the context menu, as is done in\nthe canvas-x\nstylable `ContextMenu` (../packages/bodiless-core-ui/src/GlobalContextMenu.tsx)\nand\n `LocalContextMenu` (../packages/bodiless-core-ui/src/LocalContextMenu.tsx).\n\n## Activate Context System\n\nThe activate Context system allow for one component to store an id of another\ncomponent that should activate its context on creation.\n\nThis is a three step process.\n\n### Step 1 Ensure there is a Activate Context in place\n\nOne can wrap there components in a `<ActivateContextProvider>` component or one\ncan use the withActivateContext() HOC that will wrap a component in the Provider\n\n### Step 2 set the Id\n\nThen one needs to set the id of the component to be activated by pulling `setId` from `useActivateContext`.\n\n``` tsx\nconst { setId } from useActivateContext();\nsetId(id);\n```\n\n### Step 3  Add useActivateOnEffect hook\n\nFinally, the component needs to have the `useActivateOnEffect` hook so that it will activate if the id match the one stored\n\n``` tsx\nuseActivateOnEffect(id);\n```\n\n","readmeFilename":"README.md","bugs":{"url":"https://github.com/johnsonandjohnson/bodiless-js/issues"},"homepage":"https://github.com/johnsonandjohnson/bodiless-js#readme","_id":"@asemirsk/core@1.0.2-beta.0","_nodeVersion":"16.13.2","_npmVersion":"lerna/4.0.0/node@v16.13.2+x64 (linux)","dist":{"integrity":"sha512-wVwuDhMGWGrPLgWZIIoQ01NCEt7db/HbjCAbfIn5LO3maxjVFFxvYoHijWY0s/sQ0oMjjYFDVLkK3gZfcYCQpg==","shasum":"09bc865fbb22aadc415c3eae73c0f653b3642459","tarball":"https://registry.npmjs.org/@asemirsk/core/-/core-1.0.2-beta.0.tgz","fileCount":4,"unpackedSize":27665,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQC7CDOUJ1NmVWrbO5XghXAW1xASMxu5/qoDWTvdhFeuQAIhAP66i3Ufca2YkXsE2oweAwgtbdkqt5dM+JIio4Moiqdd"}],"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJiUJjAACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmorkRAAkQiRIaRwif3kGefBI5u/ac6ADvDIR8TXx54bpRkPKuWZxi17\r\nnaKSJ5EW/BK6CXtgVxFCTyrMf3CJ+cgLBsgVYehutX5HPyxj/e0/dQXJ8urX\r\nHbbkk/a/mDjar9iI3vOI4L0KReAfGxqDsKh6CFRQEqvyBbj/zB5MRsYzLI7t\r\nv0MxGuCALOcLmoaGZfOb83thQUXOmZIXq8mM1XZUcKU69leNkQKj1fpkiolu\r\n2Fmn732N/aFEn+BJR8oojHFT7k3pd1JknRxerpcnfBs8qLHVqv4haTfONJiF\r\n5QpCY1MNcSnRPTLKWwYW2CodNmp5QEPJW7alAfDiAkzwVMmcBMiPWBFWkMf2\r\njrgn1Q7h/ATxhFuBL2wJ+PsIwEAquxdxFXmlTbG6JQAYn9Z8tdMD6BReLbQz\r\nsFYaxI3nWjVGdnWq9weOq5De9Hk7V2/NVHqyPUCjhZkJg4Hz3ZxylJbcF2SM\r\nsJGeh8ToK7q2WpL2xujXCr/spjxASwr0uyOiqKi0/exmtaSvJ4QfyXeNf5SH\r\nxYIlgHAXlnYvspvrEO5ua2jGwNAbwSLlf+/5ESqCtMp5IqH5AaHbspS5Nnq/\r\nOd+cyCZDTOqcNmplLMHUD+0vvUJtrrtUe3hAlAtmnXk/y+fsBwty1e47UTAY\r\nd23J7zFckL+ED4mmLPzZpQ2QwEBBIFF/Onw=\r\n=SufE\r\n-----END PGP SIGNATURE-----\r\n"},"_npmUser":{"name":"asemirsk","email":"al.semirski@gmail.com"},"directories":{},"maintainers":[{"name":"asemirsk","email":"al.semirski@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/core_1.0.2-beta.0_1649449152441_0.6566479968165686"},"_hasShrinkwrap":false},"1.0.2":{"name":"@asemirsk/core","version":"1.0.2","description":"Edit context for BodilessJS site editor","author":{"name":"Chris Oden","email":"coden@its.jnj.com"},"license":"Apache-2.0","main":"lib/index.js","sideEffects":false,"typings":"lib/index.d.ts","repository":{"type":"git","url":"git+https://github.com/johnsonandjohnson/bodiless-js.git"},"publishConfig":{"access":"public"},"scripts":{"build":"tsc -p ./tsconfig.json && npm run build:api-doc","build:watch":"npm run build -- --watch","build:api-doc":"typedoc --out doc/api src","lint":"tslint ./src/**/*.ts?x","clean":"rimraf \"lib/*\" && rimraf tsconfig.tsbuildinfo && rimraf \"doc/api\""},"dependencies":{"@asemirsk/cli":"^1.0.2","@asemirsk/fclasses":"^1.0.2","@types/react-tag-autocomplete":"^6.0.0","axios":"^0.21.0","enzyme":"^3.9.0","informed":"^3.34.0","lodash":"^4.17.19","rc-tooltip":"^5.1.1","react-tag-autocomplete":"^6.0.0","resize-observer-polyfill":"^1.5.1","universal-cookie":"^4.0.3","uuid":"^3.3.2"},"peerDependencies":{"mobx":"^6.0.0","mobx-react":"^7.2.1","react":"^17.0.2","react-dom":"^17.0.2"},"devDependencies":{"@types/rc-tooltip":"^3.7.1","@types/react-tag-autocomplete":"^6.0.0","@types/uuid":"^3.4.4"},"gitHead":"19cad7cb078a3029c7b40248078ddfa4d413bdcc","bugs":{"url":"https://github.com/johnsonandjohnson/bodiless-js/issues"},"homepage":"https://github.com/johnsonandjohnson/bodiless-js#readme","_id":"@asemirsk/core@1.0.2","_nodeVersion":"16.13.2","_npmVersion":"lerna/4.0.0/node@v16.13.2+x64 (linux)","dist":{"integrity":"sha512-9/GNglxRQLU2kwio8dIIfx3Pk7KJPazjtqYWGprDJ1UUqvkXATLkD8MoGXOBBueZOZ6tCz+Ib3kdv/Tny3ye5Q==","shasum":"d876413e2f1bd8eb2e7decb45d8ba94254956908","tarball":"https://registry.npmjs.org/@asemirsk/core/-/core-1.0.2.tgz","fileCount":4,"unpackedSize":27644,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQDCK1tkrbofpH+MFnYNGe3zOkiVh/t43B6iLVCB+nMAkwIgSj8TtlGLqwzf5aT7Colr5Uz3gBvkpnFZKLWUnEpdRBg="}],"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJiUJl9ACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmqPHg//RpeT5+t3OO/Rog074kCgOasuEv791mHp7w5nLBtraDHBeQS0\r\nyUfuxbznvI5/6opWxKD8Q5E+1JIHx4+VqWtvABz4TTR2iITkZosKg3YBEZLw\r\nQg0oZoruDGnxtBX/EuToeZMes3gwxmCKl0v+pgUckvgLz9pVSiJhDIXLVPQp\r\nRHgtBVCcJ9pB7yUVBTwwuoNWWqwtI7+SEoYzE1G1pw+3thxqwkmlFGKROPhl\r\nRXBUZTR0jRkhFlOJBMaiE/ptrOCt20rWJfCvvkl4mxwpmSeQgeZQxhY5csOH\r\nMxxuYEOq6oWkTMfXUAQ8sDfNyA5SKFOhYFiUBzOA5tFR2SavIA2/TQcA+Ri7\r\nleHDJippbS1XexXW961y/q/JFBd+Du38bzS4zjt7E7anVAHJlJ1WtYKRwxP+\r\n5EG1C7n6dOy0/2abQXsxwFi2fVlMgNXLDTR3GdGiw0hjg7FAoFIf6xHlrdHB\r\nuj7f9cn5d0g60c7d3eQNb2bac0QbIB1F4NeWFJvkSikAYrfVeLttiQ2WRV4r\r\nHuP/c4FAq9oeLqb+a3N6e4Tso+OGQcAWFeohzyBp5UboN+6NlRmo4bcqRlxV\r\n1JMg9j19bRhVH+0fv8uM4xqpuuA3NmKfl4jGcdqfjYNV71XW+szpPVYU4Prm\r\nXgKDex+NuLDrQDmhVd5AbtkcZ4ZgrjdEUpw=\r\n=S7y3\r\n-----END PGP SIGNATURE-----\r\n"},"_npmUser":{"name":"asemirsk","email":"al.semirski@gmail.com"},"directories":{},"maintainers":[{"name":"asemirsk","email":"al.semirski@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/core_1.0.2_1649449341586_0.4951170182366784"},"_hasShrinkwrap":false},"1.0.3":{"name":"@asemirsk/core","version":"1.0.3","description":"Edit context for BodilessJS site editor","author":{"name":"Chris Oden","email":"coden@its.jnj.com"},"license":"Apache-2.0","main":"lib/index.js","sideEffects":false,"typings":"lib/index.d.ts","repository":{"type":"git","url":"git+https://github.com/johnsonandjohnson/bodiless-js.git"},"publishConfig":{"access":"public"},"scripts":{"build":"tsc -p ./tsconfig.json && npm run build:api-doc","build:watch":"npm run build -- --watch","build:api-doc":"typedoc --out doc/api src","lint":"tslint ./src/**/*.ts?x","clean":"rimraf \"lib/*\" && rimraf tsconfig.tsbuildinfo && rimraf \"doc/api\""},"dependencies":{"@asemirsk/cli":"^1.0.2","@asemirsk/fclasses":"^1.0.3","@types/react-tag-autocomplete":"^6.0.0","axios":"^0.21.0","enzyme":"^3.9.0","informed":"^3.34.0","lodash":"^4.17.19","rc-tooltip":"^5.1.1","react-tag-autocomplete":"^6.0.0","resize-observer-polyfill":"^1.5.1","universal-cookie":"^4.0.3","uuid":"^3.3.2"},"peerDependencies":{"mobx":"^6.0.0","mobx-react":"^7.2.1","react":"^17.0.2","react-dom":"^17.0.2"},"devDependencies":{"@types/rc-tooltip":"^3.7.1","@types/react-tag-autocomplete":"^6.0.0","@types/uuid":"^3.4.4"},"gitHead":"b46d91ae36b8631daaeaf0e17b4197086a98c2f9","bugs":{"url":"https://github.com/johnsonandjohnson/bodiless-js/issues"},"homepage":"https://github.com/johnsonandjohnson/bodiless-js#readme","_id":"@asemirsk/core@1.0.3","_nodeVersion":"16.13.2","_npmVersion":"lerna/4.0.0/node@v16.13.2+x64 (linux)","dist":{"integrity":"sha512-Z4bYUD2ZTWx1dX0xNc0hqlo1kmfCdCSSH1+gbDOiEBr1t4zmOIWxUfEgrZfrvOuYmZiae45wRtRRRGW2upYyAw==","shasum":"df8886436ddeb90bbd0ba94448953d24bee461ca","tarball":"https://registry.npmjs.org/@asemirsk/core/-/core-1.0.3.tgz","fileCount":4,"unpackedSize":27644,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIFmLKC6nAOUg5M875bfn9jOJm5pbBQOatkrmpWYDVJ0MAiEArBj13QMJUULd5iNcqEI+XbIRNkV+XM/qOZ5Z4XFBy8M="}],"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJiUJpxACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmpWHg//Ve2U6Mdp+Fg+9jYhXJO8fTNS5hlUA1RvJsoVQAxvFLBJli/E\r\np12vq+1FaDFZDtDbrZh6WCKVRd/CO85lVIQRdNby9+pDA/Jpg+zOuBw93RDN\r\nXx9Ien8O5fNA5v9PfNRoFWdVTm3VuTPjyc2AzcR2lfbn6csB2NTdbH1xfkfb\r\nIg43N8V280CfEo61WwGr7yDY3x8JMbV0NPkQC1cQ48DQzsVceOgJIlHE88bZ\r\nC3RBj/5blNGVlTLFF1lLwtWz5Sy/nwNkmwByQY5IviFanDJNHJbWYw8W/cz5\r\nFfRMoWljT9SD/qRI6e3unuJNPYTodFqTyOiRrM0rBT9l6IzeAcDRdxOgA9kh\r\nZ9VqvqPEPMWqzh8jvqCXQg9vpmHNve7CeBtPSLW05dJvKuhWtaeWkHsKxFsL\r\nP+8xd6hTnRH2V0U5+HLpHt/MqT4HGvE1xcYT5T+1ELovQxhHgsPcUP2KkwTA\r\nNDHUeq0bBg+qDvu8uRiTcAGkrzdKAKmWBKzs6HzfvltS8tuDiRoq3NHIuxVQ\r\nXCAJWyaz5jB6VR/UCeZE7x8zg4kOrpsq8pnxUSN+0Xmn0MkGVsFjW0iCJj8P\r\n1njfa2C3lqGei6nLK4fLgxDftO+RdIC6vw6/tl0nOIxxtsO5H9h6/XeFkKS7\r\nPoTrr68Un+kQvPKkteO7XhTAf/J93rjCRWk=\r\n=zK0i\r\n-----END PGP SIGNATURE-----\r\n"},"_npmUser":{"name":"asemirsk","email":"al.semirski@gmail.com"},"directories":{},"maintainers":[{"name":"asemirsk","email":"al.semirski@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/core_1.0.3_1649449585245_0.4480752251410769"},"_hasShrinkwrap":false},"1.1.0":{"name":"@asemirsk/core","version":"1.1.0","description":"Edit context for BodilessJS site editor","author":{"name":"Chris Oden","email":"coden@its.jnj.com"},"license":"Apache-2.0","main":"lib/index.js","sideEffects":false,"typings":"lib/index.d.ts","repository":{"type":"git","url":"git+https://github.com/johnsonandjohnson/bodiless-js.git"},"publishConfig":{"access":"public"},"scripts":{"build":"tsc -p ./tsconfig.json && npm run build:api-doc","build:watch":"npm run build -- --watch","build:api-doc":"typedoc --out doc/api src","lint":"tslint ./src/**/*.ts?x","clean":"rimraf \"lib/*\" && rimraf tsconfig.tsbuildinfo && rimraf \"doc/api\""},"dependencies":{"@asemirsk/cli":"^1.1.0","@asemirsk/fclasses":"^1.1.0","@types/react-tag-autocomplete":"^6.0.0","axios":"^0.21.0","enzyme":"^3.9.0","informed":"^3.34.0","lodash":"^4.17.19","rc-tooltip":"^5.1.1","react-tag-autocomplete":"^6.0.0","resize-observer-polyfill":"^1.5.1","universal-cookie":"^4.0.3","uuid":"^3.3.2"},"peerDependencies":{"mobx":"^6.0.0","mobx-react":"^7.2.1","react":"^17.0.2","react-dom":"^17.0.2"},"devDependencies":{"@types/rc-tooltip":"^3.7.1","@types/react-tag-autocomplete":"^6.0.0","@types/uuid":"^3.4.4"},"gitHead":"e767bae774cc17eba5d92b638b7628cdc9236086","bugs":{"url":"https://github.com/johnsonandjohnson/bodiless-js/issues"},"homepage":"https://github.com/johnsonandjohnson/bodiless-js#readme","_id":"@asemirsk/core@1.1.0","_nodeVersion":"16.13.2","_npmVersion":"lerna/4.0.0/node@v16.13.2+x64 (linux)","dist":{"integrity":"sha512-jExRW7aeyBy7+IWWmw6QOSQrUA5yM+YWew5S4vat8gBXz+EiukdBKPWfZWTh54Yw11FeKjaFt1C2u7u3nrhvMg==","shasum":"0e122249791ba199d1bb5dbfee64c3a70f981ffd","tarball":"https://registry.npmjs.org/@asemirsk/core/-/core-1.1.0.tgz","fileCount":5,"unpackedSize":28478,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIBgEZDUad+EhyMZeJquvIDDAqmoX5mPvXOfHEQYXOX7kAiEAt1mdjFw7FLTiofddZhALGx95Ct/b8vD02Pz1hDYc8ak="}],"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJiXtAgACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2Vmo1jA//Z6dDWR1SaNf7MY2GHJh8xHtQ3TJrdwnO8x+5meU/G4zWdYW4\r\n2NQ2GhXNFktdkAuZMKj8sTjCvX89AzRsOoIUHyzK+wx4ypZXpysNB5IqPTMp\r\n4e69EEnwxejZ6l9bXEoK/VdP/fI7YkcmLN4pYnwPN+j2NUVq8WZs/GFAY/oo\r\nVF3YUepltGPeqntjEfg3KP9IPGgh7ULY//ye1fiTdFdf0np6HroUYCzjAjq8\r\n6Q/BiOHamwaHHN/y3QwoIgVef5LEqcQTpnL7/6x7xdatDcMBghWjTugKDzc9\r\ngqkR/OoGjBw4vWqBZkS3z4EeWSCqN72iO8pVd0MBFivP9yF8vxJzazKKtWcs\r\nAcoEWq8KtxS2GngChsiJ7WGS4lY8ba+RUhqew/0zoQFKHMU6Ew36H4p/bkWE\r\nSdj7HHkj9Y23jP+xaJgoSjPd86QnDPJXojaT2RzYgFnFMq6OMeUUudrJe9Fz\r\n45N3tsbpqIUynGWpUkbDI9tDuOXBQBXNlyiTjyADO+0qyaPfTCIxV48hFzEo\r\nG1xAzW0RBNg9jGhqwSqolui7oerq/Z2Uzd4brEd/KJJL4qiWfKq5YyDfCo7f\r\nKoAx0hG/Q/GcoaMyYdYs5wy3LYZ7DvtgCUxHGH0gJNb3R0IEfYad+aCkW23i\r\nPCXI4xJWnRUJnAEjbsGaxqBuhrjF0YiUgkU=\r\n=QYh7\r\n-----END PGP SIGNATURE-----\r\n"},"_npmUser":{"name":"asemirsk","email":"al.semirski@gmail.com"},"directories":{},"maintainers":[{"name":"asemirsk","email":"al.semirski@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/core_1.1.0_1650380831927_0.4035665667106543"},"_hasShrinkwrap":false},"2.0.1-beta.0":{"name":"@asemirsk/core","version":"2.0.1-beta.0","description":"Edit context for BodilessJS site editor","author":{"name":"Chris Oden","email":"coden@its.jnj.com"},"license":"Apache-2.0","main":"lib/index.js","sideEffects":false,"typings":"lib/index.d.ts","repository":{"type":"git","url":"git+https://github.com/johnsonandjohnson/bodiless-js.git"},"publishConfig":{"access":"public"},"scripts":{"build":"tsc -p ./tsconfig.json && npm run build:api-doc","build:watch":"npm run build -- --watch","build:api-doc":"typedoc --out doc/api src","lint":"tslint ./src/**/*.ts?x","clean":"rimraf \"lib/*\" && rimraf tsconfig.tsbuildinfo && rimraf \"doc/api\""},"dependencies":{"@asemirsk/cli":"^1.0.0-beta.6","@asemirsk/fclasses":"^2.0.1-beta.0","@types/react-tag-autocomplete":"^6.0.0","axios":"^0.21.0","enzyme":"^3.9.0","informed":"^3.34.0","lodash":"^4.17.19","rc-tooltip":"^5.1.1","react-tag-autocomplete":"^6.0.0","resize-observer-polyfill":"^1.5.1","universal-cookie":"^4.0.3","uuid":"^3.3.2"},"peerDependencies":{"mobx":"^6.0.0","mobx-react":"^7.2.1","react":"^17.0.2","react-dom":"^17.0.2"},"devDependencies":{"@types/rc-tooltip":"^3.7.1","@types/react-tag-autocomplete":"^6.0.0","@types/uuid":"^3.4.4"},"gitHead":"2aebb64eab582696b080b48c5f3475c2759f6408","bugs":{"url":"https://github.com/johnsonandjohnson/bodiless-js/issues"},"homepage":"https://github.com/johnsonandjohnson/bodiless-js#readme","_id":"@asemirsk/core@2.0.1-beta.0","_nodeVersion":"16.9.1","_npmVersion":"lerna/4.0.0/node@v16.9.1+x64 (linux)","dist":{"integrity":"sha512-vTwuiYZq4rHE/7veYmoknry2iJYOuXlDJue2GUkf1o+G+Bm7mdTMFdszMdTsSFB2hXe64VhpTYi2GnP9aXsXdA==","shasum":"5ab84b5fe8528be0b01f6e76ae6eea187dd1ad92","tarball":"https://registry.npmjs.org/@asemirsk/core/-/core-2.0.1-beta.0.tgz","fileCount":5,"unpackedSize":28499,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQDORDdCqEJmJcRMC34ElidYrQzNvLREvXd7HTeN79Mo/AIgNDZ5KYn3c7E+S66OlWwHYp7RLfLs6Y99fcILsOb5XHU="}],"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJia/elACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmrDUg/9FRtiDEMfKXhwNgnOFk/oIHX7A56mlr9Zxoti5kyrjAeqEeIm\r\nbR9oIQz+xs/BmXrywUqRBeJT/cltlnq7x4TjjQRTszojL3oRXwB5dElL9S0a\r\nSwXbZv7T8AwfJ6fgisqwYB5nYfCHFuqmcZXqZFqCirG1YFOjTiBN2SnP4t6c\r\n4+smUj11p9/tNlsGx7u429dHBPbPyqZI/4wfKDZlSRFjybr9TY0kfcsgu5C/\r\n4pTGL6jfu3rYrwpVKa5r5mfPvAbQw6bd2Cm7sTXimMlw0hu/b9gQqDnm/p9E\r\ndaV22R8YKuL4hkgos463bNU9f//p9YQ1IYXierw+4eVb9NXfHBKstO6iAzEg\r\n22CV9S5LRJpJN8gz3/jtKrw5KGpdcOs1yAEMGKJMoJKSpTCsgu0W9a36Bu+G\r\nL/lERKY/pZttGKTW6QIls1uMWI6bY+tQ2e70YtpH2KZEkPW8nKyXpY2/WBYV\r\ntIiwFUt+thi2qgOCDGjJUh6S3OLZr0Jg7EGZfwtzwq6yMGdf15opFJ3c1DMD\r\nD0FMySMW5JUFQiUYV0pSfmikwNjC/jPD5lPWPLk6PHkTbzeFzpcaBWOPvUHt\r\n0svgHXJRERQAahbC95iE8wYyHVzpcUXjcUBeINAjuFuoBCunukmSPRQ2YjcM\r\nFsDD0x1ZstCOM3Syz1cGllJKkBLxXMo3A4c=\r\n=1uaW\r\n-----END PGP SIGNATURE-----\r\n"},"_npmUser":{"name":"asemirsk","email":"al.semirski@gmail.com"},"directories":{},"maintainers":[{"name":"asemirsk","email":"al.semirski@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/core_2.0.1-beta.0_1651242917051_0.009335395740841967"},"_hasShrinkwrap":false},"2.0.1-beta.1":{"name":"@asemirsk/core","version":"2.0.1-beta.1","description":"Edit context for BodilessJS site editor","author":{"name":"Chris Oden","email":"coden@its.jnj.com"},"license":"Apache-2.0","main":"lib/index.js","sideEffects":false,"typings":"lib/index.d.ts","repository":{"type":"git","url":"git+https://github.com/johnsonandjohnson/bodiless-js.git"},"publishConfig":{"access":"public"},"scripts":{"build":"tsc -p ./tsconfig.json && npm run build:api-doc","build:watch":"npm run build -- --watch","build:api-doc":"typedoc --out doc/api src","lint":"tslint ./src/**/*.ts?x","clean":"rimraf \"lib/*\" && rimraf tsconfig.tsbuildinfo && rimraf \"doc/api\""},"dependencies":{"@asemirsk/cli":"^1.0.0-beta.6","@asemirsk/fclasses":"^2.0.1-beta.1","@types/react-tag-autocomplete":"^6.0.0","axios":"^0.21.0","enzyme":"^3.9.0","informed":"^3.34.0","lodash":"^4.17.19","rc-tooltip":"^5.1.1","react-tag-autocomplete":"^6.0.0","resize-observer-polyfill":"^1.5.1","universal-cookie":"^4.0.3","uuid":"^3.3.2"},"peerDependencies":{"mobx":"^6.0.0","mobx-react":"^7.2.1","react":"^17.0.2","react-dom":"^17.0.2"},"devDependencies":{"@types/rc-tooltip":"^3.7.1","@types/react-tag-autocomplete":"^6.0.0","@types/uuid":"^3.4.4"},"gitHead":"8651151ffab8ce28ac9424085c9325e3485ba177","readme":"# Edit Context and UI State\n\n## Overview\n\nBodiless uses the [React Context API](https://reactjs.org/docs/context.html) to\nprovide a \"page edit context\"\n(../packages/bodiless-core/src/PageEditContext/index.tsx) instance to all\ncomponents on the page. This is an interface which allows a component to\ncontribute contextual elements to the page edit UI (currently limited to toolbar\nmenu items). For example, an image component might contribute a menu item which\nallows a user to upload an image.\n\nThe edit context also provides an interface to a global store which defines the\nUI state, including:\n\n- which edit context is \"active\" (ie, which component has the current focus),\n  and what related menu options should be available.\n- whether edit mode is active (ie, whether components should expose their edit\n  interfaces)\n- in future, anything else which might affect all components on the page (eg\n  whether the current changes have been pushed, whether there are upstream\n  changes to be pulled, etc).\n\n## Flow\n\nThe following diagram illustrates the flow of data for page edit contexts:\n\n![context](assets/context.jpeg)\n\n1. The `PageEditor`(../packages/bodiless-core/src/components/PageEditor.tsx) (the\n   top-level component of an editable page) defines a base edit context,\n   contributing menu options which apply to the page as a whole (e.g. whether\n   edit mode should be enabled). This is set as the default value of a React\n   context type named \"PageEditContext\". Provider and Consumer components for\n   this context are available as static properties of the PageEditContext class\n   (`PageEditContext.Provider` and `PageEditContext.Consumer`).\n2. Any item on the page which wishes to define a more specific context can do so\n   using the `spawn()` method of the `PageEditContext` instance. This will\n   create a new instance with the current instance as its parent. This can then\n   be set as the new context value using `PageEditContext.Provider`. For\n   convenience, a `ContextProvider`(../packages/bodiless-core/src/PageContextProvider.tsx)\n   component is provided which creates a new context and injects it into its\n   children (provided as a render prop).\n3. A component which does not want to create a new context can either consume\n   the current context (either via `PageEditContext.Consumer` or via the\n   `useEditContext()` hook), or simply ignore it, if it doesn't care about the\n   current context or UI state.\n4. The `PageEditContext` instance exposes an `activate()` method which can be\n   used by a component to declare that its context is active (usually when the\n   user focuses there).  This sets the active context in the store. If the active\n   context has parents, then these are also considered active, and the set of\n   all active contexts is referred to as the <a id=\"context-trail\"></a>\n   _*context trail*_. For convenience a `useContextActivator()` hook is provided.\n5. This, in turn triggers any observer component to re-render (for example to\n   highlight the fact that it is active).\n6. The \"global context menu\"(../packages/bodiless-core/src/components/PageEditor.tsx)\n   is one such observer component, and re-renders itself with all menu options\n   contributed by the newly activated context and all its parents. Thus, for\n   example, when you activate the context of an image within a grid, the menu\n   options of the image (eg set source) are added to those of the grid (eg\n   insert/delete items), which in turn are added to the page level items (eg\n   toggle edit on/off).\n7. Similarly, the\n   \"local context menu\"(../packages/bodiless-core/src/components/LocalContextMenu.tsx)\n   is another observer which renders a subset of context menu options as a tooltip\n   near the component providing current innermost active context.\n\n## Working with edit context\n\nThere are several ways in which a component can provide or consume the edit context.\n\n### Providing a new context\n\nTo provide a new context value (usually to add menu options which should appear\nwhen a component has focus), you may use the supplied `PageContextProvider`\n\n```javascript\nconst getExampleMenuOptions = useGetter([{\n  // An array of context menu option objects\n}]);\n\nconst Example: React.FC = ({ children }) => (\n  <PageContextProvider getMenuOptions={getExampleMenuOptions} name=\"Example\">\n    {children}\n  </PageContextProvider}\n```\nHere we provide a menu options callback as a prop to the `ContextProvider`\ncomponent. This will be invoked when this context or any descendant is\nactivated. It should return any menu options this component wishes to provide\n(see [Context Menu Options](#context-menu-options) below for more information).\n\n> Note: It is important to memoize our `getMenuOptions()` callback so as to\n> prevent unnecessary renders of subscribers to the edit context. Here, we do\n> this with the bodilesscore `useGetter()` hook, which creates an invariant\n> callback even if the menu options it returns change.\n\nIt is actually unusual to invoke the provider directly in this manner. Instead,\nuse the `withMenuOptions` hoc to attach options to your component. First you\ndefine a custom hook which will create your `getMenuOptions()` callback. This\nhook will be invoked when the component is rendered, and will receive its props\nas an argument:\n\n```\nconst useMenuOptions = (props) => {\n  return [{\n    // An array of context menu option objects\n  }];\n};\n```\n\nThen, pass that along with a unique name to `withMenuOptions` to create an HOC\nwhich will add the options to your component.\n\n```\nconst ComponentWithMyOptions = withMenuOptions({\n  useMenuOptions,\n  name: 'my-component'\n})(AnyComponent);\n...\n<ComponentWithMyOptions propUsedInOption=\"foo\" />\n```\nNote here that we don't need to memoize the list of menu options: memoization of\nthe callback is handled within `withMenuOptions`. However, certain callbacks\nprovided as properties of the menu option object may need to be memoized (see\n[Context Menu Options](#context-menu-options) below for more information).\n\nNote that the menu options you are defining here will only be available when\nyour component (or one of its children) declares itself as \"active\". To do so,\nit will need to use a method on the current context.\n\n### Consuming the context with hooks\n\nTo access the current page edit context, simply use the `useEditContext()`\nand/or `useContextActivator()` hooks.\n\n```javascript\nimport { observer } from 'mobx-react';\nconst Example = observer(props => {\n  const { isActive, isEdit } = useEditContext();\n  const { onClick } = useContextActivator();\n  return (\n    <React.Fragment>\n      <div>{isActive ? 'Active' : 'Not Active'}</div>\n      {isEdit ?\n        <button onClick={onClick} />\n        : <span>Not editing!</span>\n      }\n    </React.Fragment>\n  );\n});\n```\n\nNote that `useContextActivator()` can be used to provide a handler for other\nevents besides `onClick`, and can invoke another handler passed in as a prop:\n\n```\nconst TextareaActivator = ({ onFocus: onFocus$1, ...rest }) => {\n  const { onFocus } = useContextActivator('onFocus', onFocus$1);\n  return <textarea onFocus={onFocus} {...rest} />\n}\n```\nIn the above example, the `textarea` will activate the context on focus, and\nwill then invoke any 'onFocus' handler passed to it.\n\nNote that we are using the `isActive` property of the context to render\ndifferently if the current context is active, and using the `isEdit` property to\ndetermine if we are in edit mode. Note also that we wrap our component in a\n[mobx observer](https://mobx-react.js.org/observer-hoc) to ensure that it\nupdates properly if the active context or edit mode changes.\n\n> Important! For functional components using hooks, we must use the version of\n> `observer` from mobx-react.\n\nIt is also possible to use the `withContextActivator` HOC to provide an activation\nevent to a pre-existing component:\n```\nconst ComponentWithMenuOption = flowRight(\n  withMenuOptions({ ... }), // See abo ve\n  withContextActivator('onClick'),\n)(AnyComponent)\n```\n\nNow, when you click on `AnyComponent` it will activate the provided context, and\nthe associated menu options will be displayed.\n\nNote - the above will only work if `AnyComponent` can accept an `onClick` prop.\nIf not, you will want to wrap it in an element which can, for example a `div`:\n```\nconst ComponentWithMenuOption = flowRight(\n  withMenuOptions({ ... }), // See above\n  withContextActivator('onClick'),\n  withActivatorWrapper('div'),\n)(AnyComponent)\n```\n\nFinally, you will want to be sure that none of the above HOC's are applied when not in edit mode.\nFor this, `ifEditable` comes in handy:\n```\nconst ComponentWithMenuOption = ifEditable(\n  withMenuOptions({ ... }), // See above\n  withContextActivator('onClick'),\n  withActivatorWrapper('div'),\n)(AnyComponent)\n```\nNote that the order of these HOC's is important.\n\n## Context Menu Options\n\nThe Global Context Menu aggregates menu options provided by all contexts within\nthe [active context trail](#context-trail). It is a wrapper around the generic\n`ContextMenu` (../packages/bodiless-core/src/components/ContextMenu.tsx)\ncomponent, which provides a mechanism for displaying a set of menu options\nprovided in an \"options\" prop. Each item is an object with the following\nmembers:\n- name: a unique machine name for this item.\n- group: the machine name of the group to which this button belongs\n- Component: An optional component to use to render this button.\n- icon: the name of a [material design icon](https://material.io/tools/icons/?style=baseline)\n  to display for this item.\n- label: a human readable label to display beneath the icon.\n- isActive; a boolean specifying the option or its flyout panel is currently\n  \"active\". This is usually used for toggles.\n- isDisabled; a boolean specifying whether the option is currently \"disabled\".\n  If true, the menu button will be greyed out and bnot clickable.\n- isHidden: a boolean specifying whether the option is currently hidden. If\n  true, the menu button will not be displayed.\n- handler: a callback to invoke when the menu button is clicked.\n\n> Note all of the above properties except `handler`, `name`, `group` and\n> `Component` can be provided either as a primitive value or as a function\n> returning that value - eg `{ isActive: true }` or `{ isActive: () => true; }`\n> The latter is useful if the value depends on some external state which is not\n> used by the component providing the button, to allow the button to update\n> without re-rendering the component. If you provide callbacks, it is important\n> to memoize any such callbacks to avoid unnecessary renders of the button\n> itself. The `handler` callback, however, need not be memoized.\n\n A simple context menu implementation with a single option might look like this:\n ```javascript\nconst [isUp, setIsUp] = React.useState(false);\n\nconst options = [\n{\n  name: 'say_yes',\n  icon: isUp ? 'thumb_up' : 'thumb_down',\n  label: 'Yes!',\n  isActive: isUp,\n  handler: () => {\n    if (!isUp) {\n      alert('Yes!');\n    }\n    setIsUp(isUp => !isUp);\n  },\n];\n\nconst MyContextMenu = props => (\n  <ContextMenu options={options} />\n);\n```\n\n#### Global and Local Context Menu\n\nBy default, Bodiless provides two `ContextMenu` instances to display menu options\nprovided by edit contexts. \n\nThe `GlobalContextMenu` is a part of the `PageEditor` and is intended to provide\na single, top-level menu for the page.\n\nThe `LocalContextMenu` component may be used to add a tooltip version of the\ncontext menu to any component, as\n\n```\n<ContextProvider getMenuOptions={myComponentMenuOptionGetter} name=\"My Component\">\n  <LocalContextMenu>\n    <MyComponent>\n    ...\n    </MyComponent>\n  </LocalContextMenu>\n</ContextProvider>\n```\n\nThis will provide a tooltip for `MyComponent` which displays a local version of the context menu.\n\nWhen defining a menu option, there are two flags you can use to control whether\nit should appear on the global context menu, the local context menu or both:\n\n```javascript\nconst myMenuOption = {\n  ...,\n  global: true // Show on global context menu\n  local: false // Hide on local context menu\n}\n```\n\n#### Context menu forms\n\nThe handler function for a menu option can, optionally, return a render\nfunction. If it does, when the menu item is selected, this will be invoked to\nrender the contents of a fly-out panel (usually a form). This allows menu\noptions to trigger display of a configuration form to collect additional user\ninput. For example, an image component might provide a menu option to configure\nits content. This might then display a form for uploading the image, entering\nalt-text, etc.\n\n## Styling\n\nThis library includes components which render visible, interactive pieces of the edit UI. These include:\n\n- ContextMenu\n- PageEditor\n- LocalContextMenu\n- ContextWrapper\n\nThese components allow injection of UI elements via a `ui` prop. This prop is a\njavascript object enumerating the UI elements which the component will use. For example,\nthe UI specification for a ContextMenu is:\n\n```javascript\nexport type UI = {\n  Icon?: ComponentType<HTMLProps<HTMLSpanElement>>;\n  Toolbar?: ComponentType<HTMLProps<HTMLDivElement>>;\n  ToolbarButton?: ComponentType<ButtonVariantProps>;\n  FormWrapper?: ComponentType<HTMLProps<HTMLDivElement>>;\n  ToolbarDivider?: ComponentType<HTMLProps<HTMLHRElement>>;\n};\n```\n\nThe `ui` prop fo `ContextMenu` accepts low-level UI elements which are composed\nto render the menu. In contrast, that for `LocalContextMenu` and `PageEditor`\nsimply lists the menu component which should be used to render the currently\nselected options:\n\n```\ntype UI = {\n  LocalContextMenu?: ComponentType<ContextMenuProps>;\n};\n```\n\nThis allows us to create customized versions of the context menu, as is done in\nthe canvas-x\nstylable `ContextMenu` (../packages/bodiless-core-ui/src/GlobalContextMenu.tsx)\nand\n `LocalContextMenu` (../packages/bodiless-core-ui/src/LocalContextMenu.tsx).\n\n## Activate Context System\n\nThe activate Context system allow for one component to store an id of another\ncomponent that should activate its context on creation.\n\nThis is a three step process.\n\n### Step 1 Ensure there is a Activate Context in place\n\nOne can wrap there components in a `<ActivateContextProvider>` component or one\ncan use the withActivateContext() HOC that will wrap a component in the Provider\n\n### Step 2 set the Id\n\nThen one needs to set the id of the component to be activated by pulling `setId` from `useActivateContext`.\n\n``` tsx\nconst { setId } from useActivateContext();\nsetId(id);\n```\n\n### Step 3  Add useActivateOnEffect hook\n\nFinally, the component needs to have the `useActivateOnEffect` hook so that it will activate if the id match the one stored\n\n``` tsx\nuseActivateOnEffect(id);\n```\n\n","readmeFilename":"README.md","bugs":{"url":"https://github.com/johnsonandjohnson/bodiless-js/issues"},"homepage":"https://github.com/johnsonandjohnson/bodiless-js#readme","_id":"@asemirsk/core@2.0.1-beta.1","_nodeVersion":"16.13.2","_npmVersion":"lerna/4.0.0/node@v16.13.2+x64 (linux)","dist":{"integrity":"sha512-lxeybcVPi+B4rMbmjMkxFmnGe+LwdGDGljqEDFLl4jGIxZ8bebaVEigYepGIH7+rbUKzQAaw8NMrRahWS3j7Ww==","shasum":"7bdc4b6190a1ec5ebcd4a0de600d03e6241ef596","tarball":"https://registry.npmjs.org/@asemirsk/core/-/core-2.0.1-beta.1.tgz","fileCount":5,"unpackedSize":28499,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDfYtPXMhUl7eAmU/jywoNbtA+TTpMbwLmYvmGH6geGDAIhAOw+O+XladBenvSFobXPc6EAin1ruCDCb3UOKdZa1VrL"}],"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJia/kiACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmqTjQ/9EwBvHkuHVeWMfJ9cwd4ki6sFGzCuwmeQGsX4TRe1M5P93Fs9\r\n11OblBcso4yhYwRcydwJSJx+vVVZC/19TLM3nhxwDaAihenRaM919RUA0Zty\r\nJ/FT8f5yyoK2MiPel6a++jCm4mYCWHPgMnqtCETPuYYMwKWuOepUIGtes68C\r\nXG2honJDgRezwHM287AEScILThKlkaQTKLIyDUmPdbTnZbkx+d9glzSqXmWs\r\nNaU5+dfGROTBL5q8j3+dXAAn/yBkkLI36gt2zo9lMXNz/bfcaJ2p6Czf+If3\r\nhRpuLz7XaCKUFwlKF2JXZN5pbZcpY/8xIMTYKrbLPLRInTFap6n2spClw8mg\r\nriPkJF21z+cE4CCp/Kr4tey4SoBjwyhS0k+k5kGHYFzABpDPTLkTL0tKSjr5\r\njsd85zrUM7Wzc9i06Qy9SmrCaQpP2RIWSl3BMzvp9PzgBjRKJjhimLsUEkIU\r\n91SE3jH4svhTnvezkWsdKbJmyzNMVG3FFJqkVf5BycNevtJWJAECNLPu/5Ue\r\nwtg8oQvMHzbbAc0Nqejpxg8VWhV6h78g7B4vEOUjQSk06fSkeJo+eddMJdzh\r\nzphXSbT8rJ2f8cR4ctdjD59nOsSLa6KmANTGU19GZUELy+VlTCGQq4ZbGA9u\r\nxaRzfJLId2c/Vhvrodkkcskc7DMUUY6+xP0=\r\n=JOyl\r\n-----END PGP SIGNATURE-----\r\n"},"_npmUser":{"name":"asemirsk","email":"al.semirski@gmail.com"},"directories":{},"maintainers":[{"name":"asemirsk","email":"al.semirski@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/core_2.0.1-beta.1_1651243297948_0.8301150049546817"},"_hasShrinkwrap":false},"2.0.3-beta.0":{"name":"@asemirsk/core","version":"2.0.3-beta.0","description":"Edit context for BodilessJS site editor","author":{"name":"Chris Oden","email":"coden@its.jnj.com"},"license":"Apache-2.0","main":"lib/index.js","sideEffects":false,"typings":"lib/index.d.ts","repository":{"type":"git","url":"git+https://github.com/johnsonandjohnson/bodiless-js.git"},"publishConfig":{"access":"public"},"scripts":{"build":"tsc -p ./tsconfig.json && npm run build:api-doc","build:watch":"npm run build -- --watch","build:api-doc":"typedoc --out doc/api src","lint":"tslint ./src/**/*.ts?x","clean":"rimraf \"lib/*\" && rimraf tsconfig.tsbuildinfo && rimraf \"doc/api\""},"dependencies":{"@asemirsk/cli":"^1.0.0-beta.6","@asemirsk/fclasses":"^2.0.3-beta.0","@types/react-tag-autocomplete":"^6.0.0","axios":"^0.21.0","enzyme":"^3.9.0","informed":"^3.34.0","lodash":"^4.17.19","rc-tooltip":"^5.1.1","react-tag-autocomplete":"^6.0.0","resize-observer-polyfill":"^1.5.1","universal-cookie":"^4.0.3","uuid":"^3.3.2"},"peerDependencies":{"mobx":"^6.0.0","mobx-react":"^7.2.1","react":"^17.0.2","react-dom":"^17.0.2"},"devDependencies":{"@types/rc-tooltip":"^3.7.1","@types/react-tag-autocomplete":"^6.0.0","@types/uuid":"^3.4.4"},"gitHead":"95b0d427717d1ca0bfedc22f3f23323acc223e94","bugs":{"url":"https://github.com/johnsonandjohnson/bodiless-js/issues"},"homepage":"https://github.com/johnsonandjohnson/bodiless-js#readme","_id":"@asemirsk/core@2.0.3-beta.0","_nodeVersion":"16.9.1","_npmVersion":"lerna/4.0.0/node@v16.9.1+x64 (linux)","dist":{"integrity":"sha512-TOuuz3HTpCQ0R1QXz6nbPBvC36PfvDa8MYdQlKHtted1CaYKQCPd2xBxOJtMr6Mq0XYoKEsTEQxyiHub3KN89w==","shasum":"5bd176f040836ecf763099c5815a03b6f4d8ef97","tarball":"https://registry.npmjs.org/@asemirsk/core/-/core-2.0.3-beta.0.tgz","fileCount":5,"unpackedSize":28499,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDVznJQ4VAX9psHI7qWkwVFTDoh4AhbwT8tUaBnsHAjVQIhAKL5gcSkyflU21LVrPdYqCQvpE4FA9k7rikFbvqc5Kex"}],"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJia/sLACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2Vmqseg//dE4meeoJ5COhd5/r66nrV//B64FVet+QHTpSmgAe+j6vXAZy\r\n5GamRb/wNLIQyHk39ojMKYnsbLagi/7wZx64rPn8vOw3qE5PBgEqidKkGUrE\r\nu/U0emUBdMA2qRmmEa4p8Nu1FOYYoGfkHJrnwBdimykTryZTLWCsF7JvXXXH\r\nPpz1lSCWYuIQ6y1qFlup85jJ6Tc1LuhLCeuPbDPA1t+LEX/Z1DCpcSo1MR7L\r\nc1+s9OFQPK5Oi+mVlssMszWgJG2AMszoNoFqL69h4kOnEtR2wUwh+2cG2zVB\r\ne+GRgOEkmO766vaOgDPFVn09w1ufRV1cmMVH38Ahw+Luw1j+tB3wDKRRNeoY\r\n2qaqoVAkBPvlcwcq6BI+ubHe9gCN8rpA+nI2okk+08D8bHjksgJq211Axcvf\r\nuzE8ROLw3r3t1RRr7/vM80NsVPyj1mrYQTMeyFqxJuB+fX7SXNVJAeNh5fic\r\np61LvMAsfYM4sLXPtAQXH8SvyBhvpiVALRxAaJdv3byYrYwYdUiu8T0HsCr7\r\ngCr1o7iz7TzBnHKmeqmoi1I116scZqYSTTpoxrmH2Gn+gHGY+ESgPXnZWLil\r\n4St9bReRpjMkHht3osfh9rQl3gaIorf5qWrKbJju8+DsYvLyf2YgBquRDAuP\r\nFyyPV/AY/N+9ggzvzJ9NiwEOB8sCxjYt+k4=\r\n=gzsz\r\n-----END PGP SIGNATURE-----\r\n"},"_npmUser":{"name":"asemirsk","email":"al.semirski@gmail.com"},"directories":{},"maintainers":[{"name":"asemirsk","email":"al.semirski@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/core_2.0.3-beta.0_1651243786936_0.3301156806234531"},"_hasShrinkwrap":false},"2.0.3-beta.1":{"name":"@asemirsk/core","version":"2.0.3-beta.1","description":"Edit context for BodilessJS site editor","author":{"name":"Chris Oden","email":"coden@its.jnj.com"},"license":"Apache-2.0","main":"lib/index.js","sideEffects":false,"typings":"lib/index.d.ts","repository":{"type":"git","url":"git+https://github.com/johnsonandjohnson/bodiless-js.git"},"publishConfig":{"access":"public"},"scripts":{"build":"tsc -p ./tsconfig.json && npm run build:api-doc","build:watch":"npm run build -- --watch","build:api-doc":"typedoc --out doc/api src","lint":"tslint ./src/**/*.ts?x","clean":"rimraf \"lib/*\" && rimraf tsconfig.tsbuildinfo && rimraf \"doc/api\""},"dependencies":{"@asemirsk/cli":"^1.0.0-beta.6","@asemirsk/fclasses":"^2.0.3-beta.1","@types/react-tag-autocomplete":"^6.0.0","axios":"^0.21.0","enzyme":"^3.9.0","informed":"^3.34.0","lodash":"^4.17.19","rc-tooltip":"^5.1.1","react-tag-autocomplete":"^6.0.0","resize-observer-polyfill":"^1.5.1","universal-cookie":"^4.0.3","uuid":"^3.3.2"},"peerDependencies":{"mobx":"^6.0.0","mobx-react":"^7.2.1","react":"^17.0.2","react-dom":"^17.0.2"},"devDependencies":{"@types/rc-tooltip":"^3.7.1","@types/react-tag-autocomplete":"^6.0.0","@types/uuid":"^3.4.4"},"gitHead":"1be4d3bb997d192b34b946c04f840a2a82b34a5b","readme":"# Edit Context and UI State\n\n## Overview\n\nBodiless uses the [React Context API](https://reactjs.org/docs/context.html) to\nprovide a \"page edit context\"\n(../packages/bodiless-core/src/PageEditContext/index.tsx) instance to all\ncomponents on the page. This is an interface which allows a component to\ncontribute contextual elements to the page edit UI (currently limited to toolbar\nmenu items). For example, an image component might contribute a menu item which\nallows a user to upload an image.\n\nThe edit context also provides an interface to a global store which defines the\nUI state, including:\n\n- which edit context is \"active\" (ie, which component has the current focus),\n  and what related menu options should be available.\n- whether edit mode is active (ie, whether components should expose their edit\n  interfaces)\n- in future, anything else which might affect all components on the page (eg\n  whether the current changes have been pushed, whether there are upstream\n  changes to be pulled, etc).\n\n## Flow\n\nThe following diagram illustrates the flow of data for page edit contexts:\n\n![context](assets/context.jpeg)\n\n1. The `PageEditor`(../packages/bodiless-core/src/components/PageEditor.tsx) (the\n   top-level component of an editable page) defines a base edit context,\n   contributing menu options which apply to the page as a whole (e.g. whether\n   edit mode should be enabled). This is set as the default value of a React\n   context type named \"PageEditContext\". Provider and Consumer components for\n   this context are available as static properties of the PageEditContext class\n   (`PageEditContext.Provider` and `PageEditContext.Consumer`).\n2. Any item on the page which wishes to define a more specific context can do so\n   using the `spawn()` method of the `PageEditContext` instance. This will\n   create a new instance with the current instance as its parent. This can then\n   be set as the new context value using `PageEditContext.Provider`. For\n   convenience, a `ContextProvider`(../packages/bodiless-core/src/PageContextProvider.tsx)\n   component is provided which creates a new context and injects it into its\n   children (provided as a render prop).\n3. A component which does not want to create a new context can either consume\n   the current context (either via `PageEditContext.Consumer` or via the\n   `useEditContext()` hook), or simply ignore it, if it doesn't care about the\n   current context or UI state.\n4. The `PageEditContext` instance exposes an `activate()` method which can be\n   used by a component to declare that its context is active (usually when the\n   user focuses there).  This sets the active context in the store. If the active\n   context has parents, then these are also considered active, and the set of\n   all active contexts is referred to as the <a id=\"context-trail\"></a>\n   _*context trail*_. For convenience a `useContextActivator()` hook is provided.\n5. This, in turn triggers any observer component to re-render (for example to\n   highlight the fact that it is active).\n6. The \"global context menu\"(../packages/bodiless-core/src/components/PageEditor.tsx)\n   is one such observer component, and re-renders itself with all menu options\n   contributed by the newly activated context and all its parents. Thus, for\n   example, when you activate the context of an image within a grid, the menu\n   options of the image (eg set source) are added to those of the grid (eg\n   insert/delete items), which in turn are added to the page level items (eg\n   toggle edit on/off).\n7. Similarly, the\n   \"local context menu\"(../packages/bodiless-core/src/components/LocalContextMenu.tsx)\n   is another observer which renders a subset of context menu options as a tooltip\n   near the component providing current innermost active context.\n\n## Working with edit context\n\nThere are several ways in which a component can provide or consume the edit context.\n\n### Providing a new context\n\nTo provide a new context value (usually to add menu options which should appear\nwhen a component has focus), you may use the supplied `PageContextProvider`\n\n```javascript\nconst getExampleMenuOptions = useGetter([{\n  // An array of context menu option objects\n}]);\n\nconst Example: React.FC = ({ children }) => (\n  <PageContextProvider getMenuOptions={getExampleMenuOptions} name=\"Example\">\n    {children}\n  </PageContextProvider}\n```\nHere we provide a menu options callback as a prop to the `ContextProvider`\ncomponent. This will be invoked when this context or any descendant is\nactivated. It should return any menu options this component wishes to provide\n(see [Context Menu Options](#context-menu-options) below for more information).\n\n> Note: It is important to memoize our `getMenuOptions()` callback so as to\n> prevent unnecessary renders of subscribers to the edit context. Here, we do\n> this with the bodilesscore `useGetter()` hook, which creates an invariant\n> callback even if the menu options it returns change.\n\nIt is actually unusual to invoke the provider directly in this manner. Instead,\nuse the `withMenuOptions` hoc to attach options to your component. First you\ndefine a custom hook which will create your `getMenuOptions()` callback. This\nhook will be invoked when the component is rendered, and will receive its props\nas an argument:\n\n```\nconst useMenuOptions = (props) => {\n  return [{\n    // An array of context menu option objects\n  }];\n};\n```\n\nThen, pass that along with a unique name to `withMenuOptions` to create an HOC\nwhich will add the options to your component.\n\n```\nconst ComponentWithMyOptions = withMenuOptions({\n  useMenuOptions,\n  name: 'my-component'\n})(AnyComponent);\n...\n<ComponentWithMyOptions propUsedInOption=\"foo\" />\n```\nNote here that we don't need to memoize the list of menu options: memoization of\nthe callback is handled within `withMenuOptions`. However, certain callbacks\nprovided as properties of the menu option object may need to be memoized (see\n[Context Menu Options](#context-menu-options) below for more information).\n\nNote that the menu options you are defining here will only be available when\nyour component (or one of its children) declares itself as \"active\". To do so,\nit will need to use a method on the current context.\n\n### Consuming the context with hooks\n\nTo access the current page edit context, simply use the `useEditContext()`\nand/or `useContextActivator()` hooks.\n\n```javascript\nimport { observer } from 'mobx-react';\nconst Example = observer(props => {\n  const { isActive, isEdit } = useEditContext();\n  const { onClick } = useContextActivator();\n  return (\n    <React.Fragment>\n      <div>{isActive ? 'Active' : 'Not Active'}</div>\n      {isEdit ?\n        <button onClick={onClick} />\n        : <span>Not editing!</span>\n      }\n    </React.Fragment>\n  );\n});\n```\n\nNote that `useContextActivator()` can be used to provide a handler for other\nevents besides `onClick`, and can invoke another handler passed in as a prop:\n\n```\nconst TextareaActivator = ({ onFocus: onFocus$1, ...rest }) => {\n  const { onFocus } = useContextActivator('onFocus', onFocus$1);\n  return <textarea onFocus={onFocus} {...rest} />\n}\n```\nIn the above example, the `textarea` will activate the context on focus, and\nwill then invoke any 'onFocus' handler passed to it.\n\nNote that we are using the `isActive` property of the context to render\ndifferently if the current context is active, and using the `isEdit` property to\ndetermine if we are in edit mode. Note also that we wrap our component in a\n[mobx observer](https://mobx-react.js.org/observer-hoc) to ensure that it\nupdates properly if the active context or edit mode changes.\n\n> Important! For functional components using hooks, we must use the version of\n> `observer` from mobx-react.\n\nIt is also possible to use the `withContextActivator` HOC to provide an activation\nevent to a pre-existing component:\n```\nconst ComponentWithMenuOption = flowRight(\n  withMenuOptions({ ... }), // See abo ve\n  withContextActivator('onClick'),\n)(AnyComponent)\n```\n\nNow, when you click on `AnyComponent` it will activate the provided context, and\nthe associated menu options will be displayed.\n\nNote - the above will only work if `AnyComponent` can accept an `onClick` prop.\nIf not, you will want to wrap it in an element which can, for example a `div`:\n```\nconst ComponentWithMenuOption = flowRight(\n  withMenuOptions({ ... }), // See above\n  withContextActivator('onClick'),\n  withActivatorWrapper('div'),\n)(AnyComponent)\n```\n\nFinally, you will want to be sure that none of the above HOC's are applied when not in edit mode.\nFor this, `ifEditable` comes in handy:\n```\nconst ComponentWithMenuOption = ifEditable(\n  withMenuOptions({ ... }), // See above\n  withContextActivator('onClick'),\n  withActivatorWrapper('div'),\n)(AnyComponent)\n```\nNote that the order of these HOC's is important.\n\n## Context Menu Options\n\nThe Global Context Menu aggregates menu options provided by all contexts within\nthe [active context trail](#context-trail). It is a wrapper around the generic\n`ContextMenu` (../packages/bodiless-core/src/components/ContextMenu.tsx)\ncomponent, which provides a mechanism for displaying a set of menu options\nprovided in an \"options\" prop. Each item is an object with the following\nmembers:\n- name: a unique machine name for this item.\n- group: the machine name of the group to which this button belongs\n- Component: An optional component to use to render this button.\n- icon: the name of a [material design icon](https://material.io/tools/icons/?style=baseline)\n  to display for this item.\n- label: a human readable label to display beneath the icon.\n- isActive; a boolean specifying the option or its flyout panel is currently\n  \"active\". This is usually used for toggles.\n- isDisabled; a boolean specifying whether the option is currently \"disabled\".\n  If true, the menu button will be greyed out and bnot clickable.\n- isHidden: a boolean specifying whether the option is currently hidden. If\n  true, the menu button will not be displayed.\n- handler: a callback to invoke when the menu button is clicked.\n\n> Note all of the above properties except `handler`, `name`, `group` and\n> `Component` can be provided either as a primitive value or as a function\n> returning that value - eg `{ isActive: true }` or `{ isActive: () => true; }`\n> The latter is useful if the value depends on some external state which is not\n> used by the component providing the button, to allow the button to update\n> without re-rendering the component. If you provide callbacks, it is important\n> to memoize any such callbacks to avoid unnecessary renders of the button\n> itself. The `handler` callback, however, need not be memoized.\n\n A simple context menu implementation with a single option might look like this:\n ```javascript\nconst [isUp, setIsUp] = React.useState(false);\n\nconst options = [\n{\n  name: 'say_yes',\n  icon: isUp ? 'thumb_up' : 'thumb_down',\n  label: 'Yes!',\n  isActive: isUp,\n  handler: () => {\n    if (!isUp) {\n      alert('Yes!');\n    }\n    setIsUp(isUp => !isUp);\n  },\n];\n\nconst MyContextMenu = props => (\n  <ContextMenu options={options} />\n);\n```\n\n#### Global and Local Context Menu\n\nBy default, Bodiless provides two `ContextMenu` instances to display menu options\nprovided by edit contexts. \n\nThe `GlobalContextMenu` is a part of the `PageEditor` and is intended to provide\na single, top-level menu for the page.\n\nThe `LocalContextMenu` component may be used to add a tooltip version of the\ncontext menu to any component, as\n\n```\n<ContextProvider getMenuOptions={myComponentMenuOptionGetter} name=\"My Component\">\n  <LocalContextMenu>\n    <MyComponent>\n    ...\n    </MyComponent>\n  </LocalContextMenu>\n</ContextProvider>\n```\n\nThis will provide a tooltip for `MyComponent` which displays a local version of the context menu.\n\nWhen defining a menu option, there are two flags you can use to control whether\nit should appear on the global context menu, the local context menu or both:\n\n```javascript\nconst myMenuOption = {\n  ...,\n  global: true // Show on global context menu\n  local: false // Hide on local context menu\n}\n```\n\n#### Context menu forms\n\nThe handler function for a menu option can, optionally, return a render\nfunction. If it does, when the menu item is selected, this will be invoked to\nrender the contents of a fly-out panel (usually a form). This allows menu\noptions to trigger display of a configuration form to collect additional user\ninput. For example, an image component might provide a menu option to configure\nits content. This might then display a form for uploading the image, entering\nalt-text, etc.\n\n## Styling\n\nThis library includes components which render visible, interactive pieces of the edit UI. These include:\n\n- ContextMenu\n- PageEditor\n- LocalContextMenu\n- ContextWrapper\n\nThese components allow injection of UI elements via a `ui` prop. This prop is a\njavascript object enumerating the UI elements which the component will use. For example,\nthe UI specification for a ContextMenu is:\n\n```javascript\nexport type UI = {\n  Icon?: ComponentType<HTMLProps<HTMLSpanElement>>;\n  Toolbar?: ComponentType<HTMLProps<HTMLDivElement>>;\n  ToolbarButton?: ComponentType<ButtonVariantProps>;\n  FormWrapper?: ComponentType<HTMLProps<HTMLDivElement>>;\n  ToolbarDivider?: ComponentType<HTMLProps<HTMLHRElement>>;\n};\n```\n\nThe `ui` prop fo `ContextMenu` accepts low-level UI elements which are composed\nto render the menu. In contrast, that for `LocalContextMenu` and `PageEditor`\nsimply lists the menu component which should be used to render the currently\nselected options:\n\n```\ntype UI = {\n  LocalContextMenu?: ComponentType<ContextMenuProps>;\n};\n```\n\nThis allows us to create customized versions of the context menu, as is done in\nthe canvas-x\nstylable `ContextMenu` (../packages/bodiless-core-ui/src/GlobalContextMenu.tsx)\nand\n `LocalContextMenu` (../packages/bodiless-core-ui/src/LocalContextMenu.tsx).\n\n## Activate Context System\n\nThe activate Context system allow for one component to store an id of another\ncomponent that should activate its context on creation.\n\nThis is a three step process.\n\n### Step 1 Ensure there is a Activate Context in place\n\nOne can wrap there components in a `<ActivateContextProvider>` component or one\ncan use the withActivateContext() HOC that will wrap a component in the Provider\n\n### Step 2 set the Id\n\nThen one needs to set the id of the component to be activated by pulling `setId` from `useActivateContext`.\n\n``` tsx\nconst { setId } from useActivateContext();\nsetId(id);\n```\n\n### Step 3  Add useActivateOnEffect hook\n\nFinally, the component needs to have the `useActivateOnEffect` hook so that it will activate if the id match the one stored\n\n``` tsx\nuseActivateOnEffect(id);\n```\n\n","readmeFilename":"README.md","bugs":{"url":"https://github.com/johnsonandjohnson/bodiless-js/issues"},"homepage":"https://github.com/johnsonandjohnson/bodiless-js#readme","_id":"@asemirsk/core@2.0.3-beta.1","_nodeVersion":"16.13.2","_npmVersion":"lerna/4.0.0/node@v16.13.2+x64 (linux)","dist":{"integrity":"sha512-LjoS7Iks3iFreZRan/e5YYXu/lEaYaSO1PkR/yLL7tbSsMvwZU+ZFJFh2UejOBLlwIcS+K/xKlUAglwsVcMe4w==","shasum":"27fad52a44f42cd1dd42982718e5cc9bbea528ea","tarball":"https://registry.npmjs.org/@asemirsk/core/-/core-2.0.3-beta.1.tgz","fileCount":5,"unpackedSize":28499,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIHL+dFnoF4/PKqIk3NPRwtFzbT0B0CNLgOhT7K6IN/yRAiEAjqLYUP15onWSqvg2CldrVXZWPW35n4+w8PEIPBAKKuM="}],"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJia/t8ACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmoQJA/+OJ+npckH+YKkAAw9QD09u52EMbC+2IYY16Gp7A/pyILQ1Ebl\r\nrjGH2Q9/Bi48+4XO1IckIHREyboLEB2QCs+CelxNoX/0byLqUKI8pjc8EEQn\r\nvtfUeEFlWBmVxUV07SJbKVfKTP4PvT3t4eSYospYPWgmXlPwh1eeJ9uuMwbg\r\nrSG8Suz90XU4XfSo0un55lW2kuhfOdAZb/ZHPjleHSaZR714XoKxqrmvJTyM\r\n/0zwFg1Jz9YLKW2Xb4m7u1Lo/+2wdBLApPxZU76wNDZmmzb446qTpjqYs6WI\r\naC/qBXUBc/fR7MbXfnnFLa236sPI/QRjyTOy+IpqBNeHuvp6hlL/IZRMujKZ\r\n1P3dil+5U12f353Af53bKW90urb6E9Vk7eV+fLmWHGPOt9qXosg5Atj3BfGf\r\nRwgpmoVIuTBwbPY48tVOUqEVWJsE8/izhMbF3tEIMUdMoOWne7OTEsdg4aIb\r\niomIfQaw/z1Aox1RaBXS75qjSvafr/rRHMIkE6nP5BrarhK8f0eBt+hDGwU/\r\nXKIX4yN1UvmNTqQqLBDsLQHe/PX3cpayLk7mjFO+7sQ6sUOtkzAnXXKQ2jlx\r\n6zRDukL7/ruXY3e24o1Wc8o12hdTEfXK7R3AujOoulBsG78nKBB105uYUntr\r\ndQyydKtrltE2BIyLTu2HQGinNU3Dw31PvJ4=\r\n=vPdc\r\n-----END PGP SIGNATURE-----\r\n"},"_npmUser":{"name":"asemirsk","email":"al.semirski@gmail.com"},"directories":{},"maintainers":[{"name":"asemirsk","email":"al.semirski@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/core_2.0.3-beta.1_1651243900534_0.2787716105734812"},"_hasShrinkwrap":false},"2.0.5-beta.0":{"name":"@asemirsk/core","version":"2.0.5-beta.0","description":"Edit context for BodilessJS site editor","author":{"name":"Chris Oden","email":"coden@its.jnj.com"},"license":"Apache-2.0","main":"lib/index.js","sideEffects":false,"typings":"lib/index.d.ts","repository":{"type":"git","url":"git+https://github.com/johnsonandjohnson/bodiless-js.git"},"publishConfig":{"access":"public"},"scripts":{"build":"tsc -p ./tsconfig.json && npm run build:api-doc","build:watch":"npm run build -- --watch","build:api-doc":"typedoc --out doc/api src","lint":"tslint ./src/**/*.ts?x","clean":"rimraf \"lib/*\" && rimraf tsconfig.tsbuildinfo && rimraf \"doc/api\""},"dependencies":{"@asemirsk/cli":"^1.0.0-beta.6","@asemirsk/fclasses":"^2.0.5-beta.0","@types/react-tag-autocomplete":"^6.0.0","axios":"^0.21.0","enzyme":"^3.9.0","informed":"^3.34.0","lodash":"^4.17.19","rc-tooltip":"^5.1.1","react-tag-autocomplete":"^6.0.0","resize-observer-polyfill":"^1.5.1","universal-cookie":"^4.0.3","uuid":"^3.3.2"},"peerDependencies":{"mobx":"^6.0.0","mobx-react":"^7.2.1","react":"^17.0.2","react-dom":"^17.0.2"},"devDependencies":{"@types/rc-tooltip":"^3.7.1","@types/react-tag-autocomplete":"^6.0.0","@types/uuid":"^3.4.4"},"gitHead":"9b047b3a72ac828e21a6e31cb9a8e77e5225f5c9","bugs":{"url":"https://github.com/johnsonandjohnson/bodiless-js/issues"},"homepage":"https://github.com/johnsonandjohnson/bodiless-js#readme","_id":"@asemirsk/core@2.0.5-beta.0","_nodeVersion":"16.9.1","_npmVersion":"lerna/4.0.0/node@v16.9.1+x64 (linux)","dist":{"integrity":"sha512-CY/cRW2iFOcxpbA3PsFkEJBbKZaM9uCR+BSdsJiRSBFb85p8cJSw+pRIVFqe9uGD+yMKQvB/Yd1yECnzHXsieA==","shasum":"9cd3d951442cb586d8def72eafec525c47e4a359","tarball":"https://registry.npmjs.org/@asemirsk/core/-/core-2.0.5-beta.0.tgz","fileCount":5,"unpackedSize":28499,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCID+AwYPsV9WDAFUdZC5GI/C8bsGvQ+i3sMgwJlDHdM2oAiEAuqVBEL52d0Dwmu3WgxtKm0fIHq/DeJjJzgUSujEn5hc="}],"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJia/8IACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2Vmombw//aJ6lz6qWIJRR9S5T5IqOMypb4QdX1ot133ktJCtw3GZVJoOt\r\n5KjURN98MZvpGIHR3NncTmb+W3SV9dIdIXV+JXbjmbU3N7VVtn6tTvifGS6b\r\nGjGyVSuzds4Jt47BP122ghuv7lXwa0B+Ky/zrVq1mEhUz0I59PFoF5lzHL1f\r\nrtICTZgIqCdi7JDN9yj+LK5nP/neVF9BjQZIGwBhWhA0H83Fk4JeOLZK+SUC\r\ny3gN7HpWTqqrY9FfFq7DzAiQXA6IndX5Q5AqIzI3nnr8LNaqkMCpt8g8ATtx\r\n9vCiBHGLB+2aDOESW1HvFSMCth+vjhSTiTwO+jYVkuGnYO9uqL7o3hjH5jAF\r\nSBHov6fuNSHgP/3KHyXXEXHzpMmv3NRj4RGeWFvD5c++LrplW3v+emuE3c4G\r\ncIzNEQrjHWRaLyxVauhZCDfli7NSMyb1Uhz7awlS1f4cVwpD6CU6frInXOQj\r\nZtULTUfFePl+LdcwoKWTK5Kyu54dQfl5aws9ldttuMFdj8lRJ0YSJ0Wkk/Kn\r\nw9xyggXgjTzpAzHpRM0SbxrkjxYQxsYrORCUeHuv9mj2h9xsiv6c9mDDh4S6\r\nDyZ5DkOXnziYN3SWnwkcSc0Bv3+C0+CzXlmdUsP4XgGqJ7HsSYRLnI0kL5o4\r\nUVIkPr05cSoZ+Ps97Ohbw9ocXkH1ZBAkXXM=\r\n=fboU\r\n-----END PGP SIGNATURE-----\r\n"},"_npmUser":{"name":"asemirsk","email":"al.semirski@gmail.com"},"directories":{},"maintainers":[{"name":"asemirsk","email":"al.semirski@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/core_2.0.5-beta.0_1651244808384_0.33015377139482105"},"_hasShrinkwrap":false},"2.0.6-beta.0":{"name":"@asemirsk/core","version":"2.0.6-beta.0","description":"Edit context for BodilessJS site editor","author":{"name":"Chris Oden","email":"coden@its.jnj.com"},"license":"Apache-2.0","main":"lib/index.js","sideEffects":false,"typings":"lib/index.d.ts","repository":{"type":"git","url":"git+https://github.com/johnsonandjohnson/bodiless-js.git"},"publishConfig":{"access":"public"},"scripts":{"build":"tsc -p ./tsconfig.json && npm run build:api-doc","build:watch":"npm run build -- --watch","build:api-doc":"typedoc --out doc/api src","lint":"tslint ./src/**/*.ts?x","clean":"rimraf \"lib/*\" && rimraf tsconfig.tsbuildinfo && rimraf \"doc/api\""},"dependencies":{"@asemirsk/cli":"^1.0.0-beta.6","@asemirsk/fclasses":"^2.0.6-beta.0","@types/react-tag-autocomplete":"^6.0.0","axios":"^0.21.0","enzyme":"^3.9.0","informed":"^3.34.0","lodash":"^4.17.19","rc-tooltip":"^5.1.1","react-tag-autocomplete":"^6.0.0","resize-observer-polyfill":"^1.5.1","universal-cookie":"^4.0.3","uuid":"^3.3.2"},"peerDependencies":{"mobx":"^6.0.0","mobx-react":"^7.2.1","react":"^17.0.2","react-dom":"^17.0.2"},"devDependencies":{"@types/rc-tooltip":"^3.7.1","@types/react-tag-autocomplete":"^6.0.0","@types/uuid":"^3.4.4"},"gitHead":"e98b014aabae41353460b67b25ad4d50f0133fca","bugs":{"url":"https://github.com/johnsonandjohnson/bodiless-js/issues"},"homepage":"https://github.com/johnsonandjohnson/bodiless-js#readme","_id":"@asemirsk/core@2.0.6-beta.0","_nodeVersion":"16.9.1","_npmVersion":"lerna/4.0.0/node@v16.9.1+x64 (linux)","dist":{"integrity":"sha512-c+JdP3Ij5OUyBmQBHxglWt6c/WX8DwRhnd0M1/vifCSsT+HeVRGfr7G2D45DBLdWKgw62M6XKCB+4QWyO/htIA==","shasum":"990532f3fafd040241926494253254dfbe4eb1e9","tarball":"https://registry.npmjs.org/@asemirsk/core/-/core-2.0.6-beta.0.tgz","fileCount":5,"unpackedSize":28499,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQCSoLcduqofb5CG2Q6O/iC8AJV0gpMiWxLGsbuqBxCSxAIhAK1vFbCEBbYqfPdJXFsaruAA07H0NGXFKy5Xfbz81DOC"}],"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJib62OACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmrRzhAAhF83aZM3FQeh5IatsMx0FVIVTCSN4y7jm4goQrOAwCg75Fsy\r\nqcCiiloqyjz7BqNJ3AppNbd9BGDUCjaKB+RaL5U+sfO69yAEmWloI12zYZAW\r\ntfFq2fUUs4tNYXKWAP55K43nNfpXp3LDn1Zre5rMjDGW+2FUn8KTOZMoCqti\r\nUS+9R7CuOWwRPmPcnfbXS6Wuz72TLZV+1VHlTTF1/UBVRUlIajr8LHsVjdhA\r\nsn3cJ29l3Yoqqo9zRQbHtBvjKT85N3ignEUHHug14wuK0xjPZe50g+q5n3m7\r\njdfgT9uQ1k4Vi2C0Ky0SaMFXhyKtdGRgU5MQ2YDJn04u7Q2WrLJ3g2KX8QFZ\r\ny03rKoVJ9HDqv5eFy4DPZkssjJUdZmLGewcULVAMoIij9hpO9cTl1gGRBWt6\r\nQvZ5cLfaNQveJkRiOZJag+tuaNJgFWsuZTFCvL3e+V//d1RkFAdZWTaEUFhq\r\nWJeHmX2hyIcTIA3XXCQHwQu6TuKPUPYWsyIOqVgx8EVsHQCUqgTnOLBbpQW/\r\nGivQnjSD4fv4m8lCjCmKKl21hzwKbol5bHpJzR/gFUJw7/6L76PkBhfUo35u\r\nZhT7eJcmhQFy0t+3GCbVeTbQfKB5SGUCZDpNgc0zucEvcBee9op2cYHe1FbM\r\nu5GLbdasj427OBC/C0zhReYwUw7qrHIWtUo=\r\n=BxAS\r\n-----END PGP SIGNATURE-----\r\n"},"_npmUser":{"name":"asemirsk","email":"al.semirski@gmail.com"},"directories":{},"maintainers":[{"name":"asemirsk","email":"al.semirski@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/core_2.0.6-beta.0_1651486094219_0.6704322914945973"},"_hasShrinkwrap":false},"3.0.0":{"name":"@asemirsk/core","version":"3.0.0","description":"Edit context for BodilessJS site editor","author":{"name":"Chris Oden","email":"coden@its.jnj.com"},"license":"Apache-2.0","main":"lib/index.js","sideEffects":false,"typings":"lib/index.d.ts","repository":{"type":"git","url":"git+https://github.com/johnsonandjohnson/bodiless-js.git"},"publishConfig":{"access":"public"},"scripts":{"build":"tsc -p ./tsconfig.json && npm run build:api-doc","build:watch":"npm run build -- --watch","build:api-doc":"typedoc --out doc/api src","lint":"tslint ./src/**/*.ts?x","clean":"rimraf \"lib/*\" && rimraf tsconfig.tsbuildinfo && rimraf \"doc/api\""},"dependencies":{"@asemirsk/cli":"^1.0.0-beta.6","@asemirsk/fclasses":"^3.0.0","@types/react-tag-autocomplete":"^6.0.0","axios":"^0.21.0","enzyme":"^3.9.0","informed":"^3.34.0","lodash":"^4.17.19","rc-tooltip":"^5.1.1","react-tag-autocomplete":"^6.0.0","resize-observer-polyfill":"^1.5.1","universal-cookie":"^4.0.3","uuid":"^3.3.2"},"peerDependencies":{"mobx":"^6.0.0","mobx-react":"^7.2.1","react":"^17.0.2","react-dom":"^17.0.2"},"devDependencies":{"@types/rc-tooltip":"^3.7.1","@types/react-tag-autocomplete":"^6.0.0","@types/uuid":"^3.4.4"},"gitHead":"6cf8b4df75f61c7e648a17bd5eafcd3965785c77","bugs":{"url":"https://github.com/johnsonandjohnson/bodiless-js/issues"},"homepage":"https://github.com/johnsonandjohnson/bodiless-js#readme","_id":"@asemirsk/core@3.0.0","_nodeVersion":"16.9.1","_npmVersion":"lerna/4.0.0/node@v16.9.1+x64 (linux)","dist":{"integrity":"sha512-2DfUbnJbZbVN4s+95OWCTbYhkrRyPoGcWVoqbBwmcEETCmLIIHmXBFfYHWhmHBiQ7DvPqWUbBhiJVWgoD8eMcg==","shasum":"b4d265acb89efcdd323825a7ccfbd4da33a67800","tarball":"https://registry.npmjs.org/@asemirsk/core/-/core-3.0.0.tgz","fileCount":5,"unpackedSize":28485,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQCL/fxg9tl5z3M9Zk5gdeK+IDq6V+jA9s7VW/RU0WF/aAIgBTESszPUlzxgjU1s1CV+eC1nX2ZIFEA0h4Xsg359+6k="}],"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJib7C3ACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2Vmrc1A//f2RoszpSdtXMR3zQK39qmqwflHXW9jbnjYIjfTA2dOPwMill\r\nRrMeL34BNzR54TxQ2KjGDVnwohTQVcDRxctz9a9aTiuNM0R/40hXrqtqmVno\r\nn83P5p7FbBlcZiAQR1b4n7foD+SAejkHWbyoSnGdaMmINEOnklRDt3ojtVMC\r\nebU+/wymi3MwsBuPprdP3RCo2LL37W4fDz1UXZCac1LXl5nrWdQbPnyluxEU\r\ngSGbib1QVDOo8PSV52LblTlW89ArOsoGN39ABI+b/w8+8aZD9WSCb26W/ocr\r\nIFs2iXvEJyfsYfwRiUJlkgTnnh2DD5rJdtnAEqD/Wy6CAw4BzRttLch1KnoV\r\n+YHlv/zyILCS0nnBwoEuuY8+dwHHsfXBtrXq0zxFgvZ11x6rERF2LuuGuQ3Y\r\nkZTcujgDzMEDjws0vicaamoasUqjl0pH7uzQUUf5UBT9W+n/ezTSxaarIV2T\r\n4kBLCSsrA0ZvwN5/zGu5kMAWv7aea8ppPWsIGh2/akjPWU0DL7b77i0PiglZ\r\nDoLRd//hEkPfXfJ7m8YzH0b302QEsujvh8c5ai/zG6reU79IpigZqvbMdHKQ\r\nNJnjlkUaKSd3WMD+wD115ps9+cC0iLppHp5GSkKpKluCFkxtVaL/1gmNnOfR\r\nI+BEErH63N/OhSFL4xUcjCVCzeDvMTRaNYA=\r\n=8Tz3\r\n-----END PGP SIGNATURE-----\r\n"},"_npmUser":{"name":"asemirsk","email":"al.semirski@gmail.com"},"directories":{},"maintainers":[{"name":"asemirsk","email":"al.semirski@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/core_3.0.0_1651486903750_0.6101699370003542"},"_hasShrinkwrap":false},"1.0.0-beta.16":{"name":"@asemirsk/core","version":"1.0.0-beta.16","description":"Edit context for BodilessJS site editor","author":{"name":"Chris Oden","email":"coden@its.jnj.com"},"license":"Apache-2.0","main":"lib/index.js","sideEffects":false,"typings":"lib/index.d.ts","repository":{"type":"git","url":"git+https://github.com/johnsonandjohnson/bodiless-js.git"},"publishConfig":{"access":"public"},"scripts":{"build":"tsc -p ./tsconfig.json && npm run build:api-doc","build:watch":"npm run build -- --watch","build:api-doc":"typedoc --out doc/api src","lint":"tslint ./src/**/*.ts?x","clean":"rimraf \"lib/*\" && rimraf tsconfig.tsbuildinfo && rimraf \"doc/api\""},"dependencies":{"@asemirsk/cli":"^1.0.0-beta.15","@asemirsk/fclasses":"^1.0.0-beta.16","@types/react-tag-autocomplete":"^6.0.0","axios":"^0.21.0","enzyme":"^3.9.0","informed":"^3.34.0","lodash":"^4.17.19","rc-tooltip":"^5.1.1","react-tag-autocomplete":"^6.0.0","resize-observer-polyfill":"^1.5.1","universal-cookie":"^4.0.3","uuid":"^3.3.2"},"peerDependencies":{"mobx":"^6.0.0","mobx-react":"^7.2.1","react":"^17.0.2","react-dom":"^17.0.2"},"devDependencies":{"@types/rc-tooltip":"^3.7.1","@types/react-tag-autocomplete":"^6.0.0","@types/uuid":"^3.4.4"},"gitHead":"0ff46789373c0ccb710fa463d6cbd035aeb8eb1f","readme":"# Edit Context and UI State\n\n## Overview\n\nBodiless uses the [React Context API](https://reactjs.org/docs/context.html) to\nprovide a \"page edit context\"\n(../packages/bodiless-core/src/PageEditContext/index.tsx) instance to all\ncomponents on the page. This is an interface which allows a component to\ncontribute contextual elements to the page edit UI (currently limited to toolbar\nmenu items). For example, an image component might contribute a menu item which\nallows a user to upload an image.\n\nThe edit context also provides an interface to a global store which defines the\nUI state, including:\n\n- which edit context is \"active\" (ie, which component has the current focus),\n  and what related menu options should be available.\n- whether edit mode is active (ie, whether components should expose their edit\n  interfaces)\n- in future, anything else which might affect all components on the page (eg\n  whether the current changes have been pushed, whether there are upstream\n  changes to be pulled, etc).\n\n## Flow\n\nThe following diagram illustrates the flow of data for page edit contexts:\n\n![context](assets/context.jpeg)\n\n1. The `PageEditor`(../packages/bodiless-core/src/components/PageEditor.tsx) (the\n   top-level component of an editable page) defines a base edit context,\n   contributing menu options which apply to the page as a whole (e.g. whether\n   edit mode should be enabled). This is set as the default value of a React\n   context type named \"PageEditContext\". Provider and Consumer components for\n   this context are available as static properties of the PageEditContext class\n   (`PageEditContext.Provider` and `PageEditContext.Consumer`).\n2. Any item on the page which wishes to define a more specific context can do so\n   using the `spawn()` method of the `PageEditContext` instance. This will\n   create a new instance with the current instance as its parent. This can then\n   be set as the new context value using `PageEditContext.Provider`. For\n   convenience, a `ContextProvider`(../packages/bodiless-core/src/PageContextProvider.tsx)\n   component is provided which creates a new context and injects it into its\n   children (provided as a render prop).\n3. A component which does not want to create a new context can either consume\n   the current context (either via `PageEditContext.Consumer` or via the\n   `useEditContext()` hook), or simply ignore it, if it doesn't care about the\n   current context or UI state.\n4. The `PageEditContext` instance exposes an `activate()` method which can be\n   used by a component to declare that its context is active (usually when the\n   user focuses there).  This sets the active context in the store. If the active\n   context has parents, then these are also considered active, and the set of\n   all active contexts is referred to as the <a id=\"context-trail\"></a>\n   _*context trail*_. For convenience a `useContextActivator()` hook is provided.\n5. This, in turn triggers any observer component to re-render (for example to\n   highlight the fact that it is active).\n6. The \"global context menu\"(../packages/bodiless-core/src/components/PageEditor.tsx)\n   is one such observer component, and re-renders itself with all menu options\n   contributed by the newly activated context and all its parents. Thus, for\n   example, when you activate the context of an image within a grid, the menu\n   options of the image (eg set source) are added to those of the grid (eg\n   insert/delete items), which in turn are added to the page level items (eg\n   toggle edit on/off).\n7. Similarly, the\n   \"local context menu\"(../packages/bodiless-core/src/components/LocalContextMenu.tsx)\n   is another observer which renders a subset of context menu options as a tooltip\n   near the component providing current innermost active context.\n\n## Working with edit context\n\nThere are several ways in which a component can provide or consume the edit context.\n\n### Providing a new context\n\nTo provide a new context value (usually to add menu options which should appear\nwhen a component has focus), you may use the supplied `PageContextProvider`\n\n```javascript\nconst getExampleMenuOptions = useGetter([{\n  // An array of context menu option objects\n}]);\n\nconst Example: React.FC = ({ children }) => (\n  <PageContextProvider getMenuOptions={getExampleMenuOptions} name=\"Example\">\n    {children}\n  </PageContextProvider}\n```\nHere we provide a menu options callback as a prop to the `ContextProvider`\ncomponent. This will be invoked when this context or any descendant is\nactivated. It should return any menu options this component wishes to provide\n(see [Context Menu Options](#context-menu-options) below for more information).\n\n> Note: It is important to memoize our `getMenuOptions()` callback so as to\n> prevent unnecessary renders of subscribers to the edit context. Here, we do\n> this with the bodilesscore `useGetter()` hook, which creates an invariant\n> callback even if the menu options it returns change.\n\nIt is actually unusual to invoke the provider directly in this manner. Instead,\nuse the `withMenuOptions` hoc to attach options to your component. First you\ndefine a custom hook which will create your `getMenuOptions()` callback. This\nhook will be invoked when the component is rendered, and will receive its props\nas an argument:\n\n```\nconst useMenuOptions = (props) => {\n  return [{\n    // An array of context menu option objects\n  }];\n};\n```\n\nThen, pass that along with a unique name to `withMenuOptions` to create an HOC\nwhich will add the options to your component.\n\n```\nconst ComponentWithMyOptions = withMenuOptions({\n  useMenuOptions,\n  name: 'my-component'\n})(AnyComponent);\n...\n<ComponentWithMyOptions propUsedInOption=\"foo\" />\n```\nNote here that we don't need to memoize the list of menu options: memoization of\nthe callback is handled within `withMenuOptions`. However, certain callbacks\nprovided as properties of the menu option object may need to be memoized (see\n[Context Menu Options](#context-menu-options) below for more information).\n\nNote that the menu options you are defining here will only be available when\nyour component (or one of its children) declares itself as \"active\". To do so,\nit will need to use a method on the current context.\n\n### Consuming the context with hooks\n\nTo access the current page edit context, simply use the `useEditContext()`\nand/or `useContextActivator()` hooks.\n\n```javascript\nimport { observer } from 'mobx-react';\nconst Example = observer(props => {\n  const { isActive, isEdit } = useEditContext();\n  const { onClick } = useContextActivator();\n  return (\n    <React.Fragment>\n      <div>{isActive ? 'Active' : 'Not Active'}</div>\n      {isEdit ?\n        <button onClick={onClick} />\n        : <span>Not editing!</span>\n      }\n    </React.Fragment>\n  );\n});\n```\n\nNote that `useContextActivator()` can be used to provide a handler for other\nevents besides `onClick`, and can invoke another handler passed in as a prop:\n\n```\nconst TextareaActivator = ({ onFocus: onFocus$1, ...rest }) => {\n  const { onFocus } = useContextActivator('onFocus', onFocus$1);\n  return <textarea onFocus={onFocus} {...rest} />\n}\n```\nIn the above example, the `textarea` will activate the context on focus, and\nwill then invoke any 'onFocus' handler passed to it.\n\nNote that we are using the `isActive` property of the context to render\ndifferently if the current context is active, and using the `isEdit` property to\ndetermine if we are in edit mode. Note also that we wrap our component in a\n[mobx observer](https://mobx-react.js.org/observer-hoc) to ensure that it\nupdates properly if the active context or edit mode changes.\n\n> Important! For functional components using hooks, we must use the version of\n> `observer` from mobx-react.\n\nIt is also possible to use the `withContextActivator` HOC to provide an activation\nevent to a pre-existing component:\n```\nconst ComponentWithMenuOption = flowRight(\n  withMenuOptions({ ... }), // See abo ve\n  withContextActivator('onClick'),\n)(AnyComponent)\n```\n\nNow, when you click on `AnyComponent` it will activate the provided context, and\nthe associated menu options will be displayed.\n\nNote - the above will only work if `AnyComponent` can accept an `onClick` prop.\nIf not, you will want to wrap it in an element which can, for example a `div`:\n```\nconst ComponentWithMenuOption = flowRight(\n  withMenuOptions({ ... }), // See above\n  withContextActivator('onClick'),\n  withActivatorWrapper('div'),\n)(AnyComponent)\n```\n\nFinally, you will want to be sure that none of the above HOC's are applied when not in edit mode.\nFor this, `ifEditable` comes in handy:\n```\nconst ComponentWithMenuOption = ifEditable(\n  withMenuOptions({ ... }), // See above\n  withContextActivator('onClick'),\n  withActivatorWrapper('div'),\n)(AnyComponent)\n```\nNote that the order of these HOC's is important.\n\n## Context Menu Options\n\nThe Global Context Menu aggregates menu options provided by all contexts within\nthe [active context trail](#context-trail). It is a wrapper around the generic\n`ContextMenu` (../packages/bodiless-core/src/components/ContextMenu.tsx)\ncomponent, which provides a mechanism for displaying a set of menu options\nprovided in an \"options\" prop. Each item is an object with the following\nmembers:\n- name: a unique machine name for this item.\n- group: the machine name of the group to which this button belongs\n- Component: An optional component to use to render this button.\n- icon: the name of a [material design icon](https://material.io/tools/icons/?style=baseline)\n  to display for this item.\n- label: a human readable label to display beneath the icon.\n- isActive; a boolean specifying the option or its flyout panel is currently\n  \"active\". This is usually used for toggles.\n- isDisabled; a boolean specifying whether the option is currently \"disabled\".\n  If true, the menu button will be greyed out and bnot clickable.\n- isHidden: a boolean specifying whether the option is currently hidden. If\n  true, the menu button will not be displayed.\n- handler: a callback to invoke when the menu button is clicked.\n\n> Note all of the above properties except `handler`, `name`, `group` and\n> `Component` can be provided either as a primitive value or as a function\n> returning that value - eg `{ isActive: true }` or `{ isActive: () => true; }`\n> The latter is useful if the value depends on some external state which is not\n> used by the component providing the button, to allow the button to update\n> without re-rendering the component. If you provide callbacks, it is important\n> to memoize any such callbacks to avoid unnecessary renders of the button\n> itself. The `handler` callback, however, need not be memoized.\n\n A simple context menu implementation with a single option might look like this:\n ```javascript\nconst [isUp, setIsUp] = React.useState(false);\n\nconst options = [\n{\n  name: 'say_yes',\n  icon: isUp ? 'thumb_up' : 'thumb_down',\n  label: 'Yes!',\n  isActive: isUp,\n  handler: () => {\n    if (!isUp) {\n      alert('Yes!');\n    }\n    setIsUp(isUp => !isUp);\n  },\n];\n\nconst MyContextMenu = props => (\n  <ContextMenu options={options} />\n);\n```\n\n#### Global and Local Context Menu\n\nBy default, Bodiless provides two `ContextMenu` instances to display menu options\nprovided by edit contexts. \n\nThe `GlobalContextMenu` is a part of the `PageEditor` and is intended to provide\na single, top-level menu for the page.\n\nThe `LocalContextMenu` component may be used to add a tooltip version of the\ncontext menu to any component, as\n\n```\n<ContextProvider getMenuOptions={myComponentMenuOptionGetter} name=\"My Component\">\n  <LocalContextMenu>\n    <MyComponent>\n    ...\n    </MyComponent>\n  </LocalContextMenu>\n</ContextProvider>\n```\n\nThis will provide a tooltip for `MyComponent` which displays a local version of the context menu.\n\nWhen defining a menu option, there are two flags you can use to control whether\nit should appear on the global context menu, the local context menu or both:\n\n```javascript\nconst myMenuOption = {\n  ...,\n  global: true // Show on global context menu\n  local: false // Hide on local context menu\n}\n```\n\n#### Context menu forms\n\nThe handler function for a menu option can, optionally, return a render\nfunction. If it does, when the menu item is selected, this will be invoked to\nrender the contents of a fly-out panel (usually a form). This allows menu\noptions to trigger display of a configuration form to collect additional user\ninput. For example, an image component might provide a menu option to configure\nits content. This might then display a form for uploading the image, entering\nalt-text, etc.\n\n## Styling\n\nThis library includes components which render visible, interactive pieces of the edit UI. These include:\n\n- ContextMenu\n- PageEditor\n- LocalContextMenu\n- ContextWrapper\n\nThese components allow injection of UI elements via a `ui` prop. This prop is a\njavascript object enumerating the UI elements which the component will use. For example,\nthe UI specification for a ContextMenu is:\n\n```javascript\nexport type UI = {\n  Icon?: ComponentType<HTMLProps<HTMLSpanElement>>;\n  Toolbar?: ComponentType<HTMLProps<HTMLDivElement>>;\n  ToolbarButton?: ComponentType<ButtonVariantProps>;\n  FormWrapper?: ComponentType<HTMLProps<HTMLDivElement>>;\n  ToolbarDivider?: ComponentType<HTMLProps<HTMLHRElement>>;\n};\n```\n\nThe `ui` prop fo `ContextMenu` accepts low-level UI elements which are composed\nto render the menu. In contrast, that for `LocalContextMenu` and `PageEditor`\nsimply lists the menu component which should be used to render the currently\nselected options:\n\n```\ntype UI = {\n  LocalContextMenu?: ComponentType<ContextMenuProps>;\n};\n```\n\nThis allows us to create customized versions of the context menu, as is done in\nthe canvas-x\nstylable `ContextMenu` (../packages/bodiless-core-ui/src/GlobalContextMenu.tsx)\nand\n `LocalContextMenu` (../packages/bodiless-core-ui/src/LocalContextMenu.tsx).\n\n## Activate Context System\n\nThe activate Context system allow for one component to store an id of another\ncomponent that should activate its context on creation.\n\nThis is a three step process.\n\n### Step 1 Ensure there is a Activate Context in place\n\nOne can wrap there components in a `<ActivateContextProvider>` component or one\ncan use the withActivateContext() HOC that will wrap a component in the Provider\n\n### Step 2 set the Id\n\nThen one needs to set the id of the component to be activated by pulling `setId` from `useActivateContext`.\n\n``` tsx\nconst { setId } from useActivateContext();\nsetId(id);\n```\n\n### Step 3  Add useActivateOnEffect hook\n\nFinally, the component needs to have the `useActivateOnEffect` hook so that it will activate if the id match the one stored\n\n``` tsx\nuseActivateOnEffect(id);\n```\n\n","readmeFilename":"README.md","bugs":{"url":"https://github.com/johnsonandjohnson/bodiless-js/issues"},"homepage":"https://github.com/johnsonandjohnson/bodiless-js#readme","_id":"@asemirsk/core@1.0.0-beta.16","_nodeVersion":"16.13.2","_npmVersion":"lerna/4.0.0/node@v16.13.2+x64 (linux)","dist":{"integrity":"sha512-1Lu7h9u7TfqmGiQWVVW2S/PYxgjrwESFCAwoM4l/TCGp+XwvuYZ97G5RgP4mFFANMxR9kplrgJPHDn9NKNRPYQ==","shasum":"9c5baeefd8c72179073ca6c528eed0fa261d6152","tarball":"https://registry.npmjs.org/@asemirsk/core/-/core-1.0.0-beta.16.tgz","fileCount":5,"unpackedSize":28502,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIB5LlNMTHhENHZVUptGtRLDqzHQ/49yanGe/03bYfR5ZAiBmZgy5HAsset/kOBYVy34f7V8YuoYdk7OejKioI367EQ=="}],"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJidOeZACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmqbzQ/+IKXqCb2EJpb9eVRtVNGYLyy6guR2DJWj6RCyosES4HhVvwQY\r\nHWOtdgOh5ysIPX9MbQRMIz1YsYD0BgYwolqhulwkd0LDDaFmelPYtpkd3qsx\r\nI8l9Wgi8KMWOp6M7kp+X43eX+BpdTKqd7CKAvvquxFNBff0bAoO70oFDB3Ux\r\nKL+tHfjEcAV3hCvOYfkX1e/OTJbSv9yQ2HMUwtRlhH5uTVaVpOm79kJvaYjn\r\n1N/vf5ftbWKqgddvLq2Hs2XOgrhP7AulyRv6QlUN1SMgblIRgPJHVHhrBlt+\r\nhiPxd9hJh8BD94NNqQ8ltRigSy0uFpNeNR/IfZWHqEpdyfU6TEw/EHgPfqw+\r\nuWFralNBnvf3op4Dsds2PT+cvEx68+tieAzv+a3v4aA5Noi+dey4DGXaWbjT\r\nAxypxPVOLDK7/hy60mvp8ls2THEC5rk3nG/50E/Km3m2llCm84hw+f1LeRag\r\nHmMGgrYN0FQ6jApjhJs9sXpGD7tTaTJs8/qIOmhzr/DoHw8RknHad6GJNr2x\r\niAaRfZCG/iMm1Dds7aaKADrxjY425+dp4dZj0MmFunjMSN+uTphcirDT9NgX\r\nrhmL3WyLnHJTWHY5VdIRfy+wvVFU8A3WQNNW0LLJ/KgnY1juCuZe9kqmnU/r\r\n0t3cW9cCf3kJ2hs/VmHmHcaIeZqmUGHFgJc=\r\n=D9C+\r\n-----END PGP SIGNATURE-----\r\n"},"_npmUser":{"name":"asemirsk","email":"al.semirski@gmail.com"},"directories":{},"maintainers":[{"name":"asemirsk","email":"al.semirski@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/core_1.0.0-beta.16_1651828633268_0.7168942370762275"},"_hasShrinkwrap":false},"1.0.0-canary-24-22.0":{"name":"@asemirsk/core","version":"1.0.0-canary-24-22.0","description":"Edit context for BodilessJS site editor","author":{"name":"Chris Oden","email":"coden@its.jnj.com"},"license":"Apache-2.0","main":"lib/index.js","sideEffects":false,"typings":"lib/index.d.ts","repository":{"type":"git","url":"git+https://github.com/johnsonandjohnson/bodiless-js.git"},"publishConfig":{"access":"public"},"scripts":{"build":"run-p build:lib","build:lib":"tsc -p ./tsconfig.json","build:watch":"npm run build:lib -- --watch","build:api-doc":"typedoc --options ../../typedoc.js --out doc/api ./src","clean":"rimraf \"lib/*\" && rimraf tsconfig.tsbuildinfo && rimraf \"doc/api\""},"dependencies":{"@asemirsk/fclasses":"1.0.0-canary-24-22.0+a48e91c","@types/react-tag-autocomplete":"^6.0.0","axios":"^0.21.0","enzyme":"^3.9.0","informed":"^3.34.0","lodash":"^4.17.19","rc-tooltip":"^5.1.1","react-tag-autocomplete":"^6.0.0","resize-observer-polyfill":"^1.5.1","universal-cookie":"^4.0.3","uuid":"^3.3.2"},"peerDependencies":{"mobx":"^6.0.0","mobx-react":"^7.2.1","react":"^17.0.2","react-dom":"^17.0.2"},"devDependencies":{"@types/rc-tooltip":"^3.7.1","@types/react-tag-autocomplete":"^6.0.0","@types/uuid":"^3.4.4"},"gitHead":"a48e91c77704024ccd08e24d4f85d1cd6971ccb1","readme":"# Edit Context and UI State\n\n## Overview\n\nBodiless uses the [React Context API](https://reactjs.org/docs/context.html) to\nprovide a \"page edit context\"\n(../packages/bodiless-core/src/PageEditContext/index.tsx) instance to all\ncomponents on the page. This is an interface which allows a component to\ncontribute contextual elements to the page edit UI (currently limited to toolbar\nmenu items). For example, an image component might contribute a menu item which\nallows a user to upload an image.\n\nThe edit context also provides an interface to a global store which defines the\nUI state, including:\n\n- which edit context is \"active\" (ie, which component has the current focus),\n  and what related menu options should be available.\n- whether edit mode is active (ie, whether components should expose their edit\n  interfaces)\n- in future, anything else which might affect all components on the page (eg\n  whether the current changes have been pushed, whether there are upstream\n  changes to be pulled, etc).\n\n## Flow\n\nThe following diagram illustrates the flow of data for page edit contexts:\n\n![context](assets/context.jpeg)\n\n1. The `PageEditor`(../packages/bodiless-core/src/components/PageEditor.tsx) (the\n   top-level component of an editable page) defines a base edit context,\n   contributing menu options which apply to the page as a whole (e.g. whether\n   edit mode should be enabled). This is set as the default value of a React\n   context type named \"PageEditContext\". Provider and Consumer components for\n   this context are available as static properties of the PageEditContext class\n   (`PageEditContext.Provider` and `PageEditContext.Consumer`).\n2. Any item on the page which wishes to define a more specific context can do so\n   using the `spawn()` method of the `PageEditContext` instance. This will\n   create a new instance with the current instance as its parent. This can then\n   be set as the new context value using `PageEditContext.Provider`. For\n   convenience, a `ContextProvider`(../packages/bodiless-core/src/PageContextProvider.tsx)\n   component is provided which creates a new context and injects it into its\n   children (provided as a render prop).\n3. A component which does not want to create a new context can either consume\n   the current context (either via `PageEditContext.Consumer` or via the\n   `useEditContext()` hook), or simply ignore it, if it doesn't care about the\n   current context or UI state.\n4. The `PageEditContext` instance exposes an `activate()` method which can be\n   used by a component to declare that its context is active (usually when the\n   user focuses there).  This sets the active context in the store. If the active\n   context has parents, then these are also considered active, and the set of\n   all active contexts is referred to as the <a id=\"context-trail\"></a>\n   _*context trail*_. For convenience a `useContextActivator()` hook is provided.\n5. This, in turn triggers any observer component to re-render (for example to\n   highlight the fact that it is active).\n6. The \"global context menu\"(../packages/bodiless-core/src/components/PageEditor.tsx)\n   is one such observer component, and re-renders itself with all menu options\n   contributed by the newly activated context and all its parents. Thus, for\n   example, when you activate the context of an image within a grid, the menu\n   options of the image (eg set source) are added to those of the grid (eg\n   insert/delete items), which in turn are added to the page level items (eg\n   toggle edit on/off).\n7. Similarly, the\n   \"local context menu\"(../packages/bodiless-core/src/components/LocalContextMenu.tsx)\n   is another observer which renders a subset of context menu options as a tooltip\n   near the component providing current innermost active context.\n\n## Working with edit context\n\nThere are several ways in which a component can provide or consume the edit context.\n\n### Providing a new context\n\nTo provide a new context value (usually to add menu options which should appear\nwhen a component has focus), you may use the supplied `PageContextProvider`\n\n```javascript\nconst getExampleMenuOptions = useGetter([{\n  // An array of context menu option objects\n}]);\n\nconst Example: React.FC = ({ children }) => (\n  <PageContextProvider getMenuOptions={getExampleMenuOptions} name=\"Example\">\n    {children}\n  </PageContextProvider}\n```\nHere we provide a menu options callback as a prop to the `ContextProvider`\ncomponent. This will be invoked when this context or any descendant is\nactivated. It should return any menu options this component wishes to provide\n(see [Context Menu Options](#context-menu-options) below for more information).\n\n> Note: It is important to memoize our `getMenuOptions()` callback so as to\n> prevent unnecessary renders of subscribers to the edit context. Here, we do\n> this with the bodilesscore `useGetter()` hook, which creates an invariant\n> callback even if the menu options it returns change.\n\nIt is actually unusual to invoke the provider directly in this manner. Instead,\nuse the `withMenuOptions` hoc to attach options to your component. First you\ndefine a custom hook which will create your `getMenuOptions()` callback. This\nhook will be invoked when the component is rendered, and will receive its props\nas an argument:\n\n```\nconst useMenuOptions = (props) => {\n  return [{\n    // An array of context menu option objects\n  }];\n};\n```\n\nThen, pass that along with a unique name to `withMenuOptions` to create an HOC\nwhich will add the options to your component.\n\n```\nconst ComponentWithMyOptions = withMenuOptions({\n  useMenuOptions,\n  name: 'my-component'\n})(AnyComponent);\n...\n<ComponentWithMyOptions propUsedInOption=\"foo\" />\n```\nNote here that we don't need to memoize the list of menu options: memoization of\nthe callback is handled within `withMenuOptions`. However, certain callbacks\nprovided as properties of the menu option object may need to be memoized (see\n[Context Menu Options](#context-menu-options) below for more information).\n\nNote that the menu options you are defining here will only be available when\nyour component (or one of its children) declares itself as \"active\". To do so,\nit will need to use a method on the current context.\n\n### Consuming the context with hooks\n\nTo access the current page edit context, simply use the `useEditContext()`\nand/or `useContextActivator()` hooks.\n\n```javascript\nimport { observer } from 'mobx-react';\nconst Example = observer(props => {\n  const { isActive, isEdit } = useEditContext();\n  const { onClick } = useContextActivator();\n  return (\n    <React.Fragment>\n      <div>{isActive ? 'Active' : 'Not Active'}</div>\n      {isEdit ?\n        <button onClick={onClick} />\n        : <span>Not editing!</span>\n      }\n    </React.Fragment>\n  );\n});\n```\n\nNote that `useContextActivator()` can be used to provide a handler for other\nevents besides `onClick`, and can invoke another handler passed in as a prop:\n\n```\nconst TextareaActivator = ({ onFocus: onFocus$1, ...rest }) => {\n  const { onFocus } = useContextActivator('onFocus', onFocus$1);\n  return <textarea onFocus={onFocus} {...rest} />\n}\n```\nIn the above example, the `textarea` will activate the context on focus, and\nwill then invoke any 'onFocus' handler passed to it.\n\nNote that we are using the `isActive` property of the context to render\ndifferently if the current context is active, and using the `isEdit` property to\ndetermine if we are in edit mode. Note also that we wrap our component in a\n[mobx observer](https://mobx-react.js.org/observer-hoc) to ensure that it\nupdates properly if the active context or edit mode changes.\n\n> Important! For functional components using hooks, we must use the version of\n> `observer` from mobx-react.\n\nIt is also possible to use the `withContextActivator` HOC to provide an activation\nevent to a pre-existing component:\n```\nconst ComponentWithMenuOption = flowRight(\n  withMenuOptions({ ... }), // See abo ve\n  withContextActivator('onClick'),\n)(AnyComponent)\n```\n\nNow, when you click on `AnyComponent` it will activate the provided context, and\nthe associated menu options will be displayed.\n\nNote - the above will only work if `AnyComponent` can accept an `onClick` prop.\nIf not, you will want to wrap it in an element which can, for example a `div`:\n```\nconst ComponentWithMenuOption = flowRight(\n  withMenuOptions({ ... }), // See above\n  withContextActivator('onClick'),\n  withActivatorWrapper('div'),\n)(AnyComponent)\n```\n\nFinally, you will want to be sure that none of the above HOC's are applied when not in edit mode.\nFor this, `ifEditable` comes in handy:\n```\nconst ComponentWithMenuOption = ifEditable(\n  withMenuOptions({ ... }), // See above\n  withContextActivator('onClick'),\n  withActivatorWrapper('div'),\n)(AnyComponent)\n```\nNote that the order of these HOC's is important.\n\n## Context Menu Options\n\nThe Global Context Menu aggregates menu options provided by all contexts within\nthe [active context trail](#context-trail). It is a wrapper around the generic\n`ContextMenu` (../packages/bodiless-core/src/components/ContextMenu.tsx)\ncomponent, which provides a mechanism for displaying a set of menu options\nprovided in an \"options\" prop. Each item is an object with the following\nmembers:\n- name: a unique machine name for this item.\n- group: the machine name of the group to which this button belongs\n- Component: An optional component to use to render this button.\n- icon: the name of a [material design icon](https://material.io/tools/icons/?style=baseline)\n  to display for this item.\n- label: a human readable label to display beneath the icon.\n- isActive; a boolean specifying the option or its flyout panel is currently\n  \"active\". This is usually used for toggles.\n- isDisabled; a boolean specifying whether the option is currently \"disabled\".\n  If true, the menu button will be greyed out and bnot clickable.\n- isHidden: a boolean specifying whether the option is currently hidden. If\n  true, the menu button will not be displayed.\n- handler: a callback to invoke when the menu button is clicked.\n\n> Note all of the above properties except `handler`, `name`, `group` and\n> `Component` can be provided either as a primitive value or as a function\n> returning that value - eg `{ isActive: true }` or `{ isActive: () => true; }`\n> The latter is useful if the value depends on some external state which is not\n> used by the component providing the button, to allow the button to update\n> without re-rendering the component. If you provide callbacks, it is important\n> to memoize any such callbacks to avoid unnecessary renders of the button\n> itself. The `handler` callback, however, need not be memoized.\n\n A simple context menu implementation with a single option might look like this:\n ```javascript\nconst [isUp, setIsUp] = React.useState(false);\n\nconst options = [\n{\n  name: 'say_yes',\n  icon: isUp ? 'thumb_up' : 'thumb_down',\n  label: 'Yes!',\n  isActive: isUp,\n  handler: () => {\n    if (!isUp) {\n      alert('Yes!');\n    }\n    setIsUp(isUp => !isUp);\n  },\n];\n\nconst MyContextMenu = props => (\n  <ContextMenu options={options} />\n);\n```\n\n#### Global and Local Context Menu\n\nBy default, Bodiless provides two `ContextMenu` instances to display menu options\nprovided by edit contexts. \n\nThe `GlobalContextMenu` is a part of the `PageEditor` and is intended to provide\na single, top-level menu for the page.\n\nThe `LocalContextMenu` component may be used to add a tooltip version of the\ncontext menu to any component, as\n\n```\n<ContextProvider getMenuOptions={myComponentMenuOptionGetter} name=\"My Component\">\n  <LocalContextMenu>\n    <MyComponent>\n    ...\n    </MyComponent>\n  </LocalContextMenu>\n</ContextProvider>\n```\n\nThis will provide a tooltip for `MyComponent` which displays a local version of the context menu.\n\nWhen defining a menu option, there are two flags you can use to control whether\nit should appear on the global context menu, the local context menu or both:\n\n```javascript\nconst myMenuOption = {\n  ...,\n  global: true // Show on global context menu\n  local: false // Hide on local context menu\n}\n```\n\n#### Context menu forms\n\nThe handler function for a menu option can, optionally, return a render\nfunction. If it does, when the menu item is selected, this will be invoked to\nrender the contents of a fly-out panel (usually a form). This allows menu\noptions to trigger display of a configuration form to collect additional user\ninput. For example, an image component might provide a menu option to configure\nits content. This might then display a form for uploading the image, entering\nalt-text, etc.\n\n## Styling\n\nThis library includes components which render visible, interactive pieces of the edit UI. These include:\n\n- ContextMenu\n- PageEditor\n- LocalContextMenu\n- ContextWrapper\n\nThese components allow injection of UI elements via a `ui` prop. This prop is a\njavascript object enumerating the UI elements which the component will use. For example,\nthe UI specification for a ContextMenu is:\n\n```javascript\nexport type UI = {\n  Icon?: ComponentType<HTMLProps<HTMLSpanElement>>;\n  Toolbar?: ComponentType<HTMLProps<HTMLDivElement>>;\n  ToolbarButton?: ComponentType<ButtonVariantProps>;\n  FormWrapper?: ComponentType<HTMLProps<HTMLDivElement>>;\n  ToolbarDivider?: ComponentType<HTMLProps<HTMLHRElement>>;\n};\n```\n\nThe `ui` prop fo `ContextMenu` accepts low-level UI elements which are composed\nto render the menu. In contrast, that for `LocalContextMenu` and `PageEditor`\nsimply lists the menu component which should be used to render the currently\nselected options:\n\n```\ntype UI = {\n  LocalContextMenu?: ComponentType<ContextMenuProps>;\n};\n```\n\nThis allows us to create customized versions of the context menu, as is done in\nthe canvas-x\nstylable `ContextMenu` (../packages/bodiless-core-ui/src/GlobalContextMenu.tsx)\nand\n `LocalContextMenu` (../packages/bodiless-core-ui/src/LocalContextMenu.tsx).\n\n## Activate Context System\n\nThe activate Context system allow for one component to store an id of another\ncomponent that should activate its context on creation.\n\nThis is a three step process.\n\n### Step 1 Ensure there is a Activate Context in place\n\nOne can wrap there components in a `<ActivateContextProvider>` component or one\ncan use the withActivateContext() HOC that will wrap a component in the Provider\n\n### Step 2 set the Id\n\nThen one needs to set the id of the component to be activated by pulling `setId` from `useActivateContext`.\n\n``` tsx\nconst { setId } from useActivateContext();\nsetId(id);\n```\n\n### Step 3  Add useActivateOnEffect hook\n\nFinally, the component needs to have the `useActivateOnEffect` hook so that it will activate if the id match the one stored\n\n``` tsx\nuseActivateOnEffect(id);\n```\n\n","readmeFilename":"README.md","bugs":{"url":"https://github.com/johnsonandjohnson/bodiless-js/issues"},"homepage":"https://github.com/johnsonandjohnson/bodiless-js#readme","_id":"@asemirsk/core@1.0.0-canary-24-22.0","_nodeVersion":"16.13.2","_npmVersion":"lerna/4.0.0/node@v16.13.2+x64 (linux)","dist":{"integrity":"sha512-D16+gvkPiFj3MyC34/HerogQZ/TnzOGCoJ+wfGs9ubzgkt3zb1PHdpyNcR+t+V1KzIAW05Uyy16XfgRL8kCIyw==","shasum":"7d75a7a05348f3693c592077b6984a99b6fab1f9","tarball":"https://registry.npmjs.org/@asemirsk/core/-/core-1.0.0-canary-24-22.0.tgz","fileCount":5,"unpackedSize":28498,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIG410Tq67N8k/MJ0Z62Jgbp0HKRxsuoaLO5igGzECmwpAiEAtQcDuzSmi9ujtGLp/Kk2AyqJy/4+AZ89EI4yE9x2AR0="}],"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJijezdACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmpXKg/+MUgoDTYc9Y4rYzy3YtNsgdFPyAX5W+kS5NyBx2idhZqPJhVm\r\npAIBmfcKaWeSEOQJEyeaKsDFrGJIEdACmUB27gTYfH4XscboRLqMRTDCApq+\r\n+F5DPof8z5tCG4JuEAsSU9lvI6Z7qlwLvoGrG05SCIG21eyh29W0V8Aud2QU\r\nhc4qYC0hb+YqGI5sqxKiwtbor8lei2nZdGBvKpGmDh7YGBL8iveTwDuYuKT6\r\n67d51uj3PQ4PuHGEExsG9I8ixrrKfEMrJ/eevu1IYgOWQC1CnZEB8vun81kR\r\nKuGjoJO8WrYX4ppuku10p76M1j9ckBs9U9wYhtnVEHQ2Rk9qxqVBq1+JMyRi\r\nseoROGUUd6kXjUm6EE0bwX8vSy31v/SOhXWBar0CdLdVflMPtX3srv4nsURS\r\njCei2uSaBI6u1jVFX/QYs5ujsD4EVSaIZNeF/sIiv1fT/npKD7I8J6PHGfa/\r\nd3thfaeJLbva9Mx5sjIx9R+4fc+18AgrGsE3b4n7RJJy6ax6JWxYnflGaNP+\r\n9QOaNsejlIuMybnTGaBX+0wKp9APg57Yj51++BkLkowRzoNLrkRFvH5Osmhj\r\nbIUHOHMHWNhSlEZ5Bfxw9JrrnHkA0DNM2v9Q23TLn4O0IALAt+6miN2X1gCU\r\n6ORFXU+bdDYhsUntu4p6vuKQNHa9szi8m+8=\r\n=Ge3W\r\n-----END PGP SIGNATURE-----\r\n"},"_npmUser":{"name":"asemirsk","email":"al.semirski@gmail.com"},"directories":{},"maintainers":[{"name":"asemirsk","email":"al.semirski@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/core_1.0.0-canary-24-22.0_1653468381687_0.0315539022653788"},"_hasShrinkwrap":false},"1.0.0-canary-24-23.0":{"name":"@asemirsk/core","version":"1.0.0-canary-24-23.0","description":"Edit context for BodilessJS site editor","author":{"name":"Chris Oden","email":"coden@its.jnj.com"},"license":"Apache-2.0","main":"lib/index.js","sideEffects":false,"typings":"lib/index.d.ts","repository":{"type":"git","url":"git+https://github.com/johnsonandjohnson/bodiless-js.git"},"publishConfig":{"access":"public"},"scripts":{"build":"run-p build:lib","build:lib":"tsc -p ./tsconfig.json","build:watch":"npm run build:lib -- --watch","build:api-doc":"typedoc --options ../../typedoc.js --out doc/api ./src","clean":"rimraf \"lib/*\" && rimraf tsconfig.tsbuildinfo && rimraf \"doc/api\""},"dependencies":{"@asemirsk/fclasses":"1.0.0-canary-24-23.0+2cab1ea","@types/react-tag-autocomplete":"^6.0.0","axios":"^0.21.0","enzyme":"^3.9.0","informed":"^3.34.0","lodash":"^4.17.19","rc-tooltip":"^5.1.1","react-tag-autocomplete":"^6.0.0","resize-observer-polyfill":"^1.5.1","universal-cookie":"^4.0.3","uuid":"^3.3.2"},"peerDependencies":{"mobx":"^6.0.0","mobx-react":"^7.2.1","react":"^17.0.2","react-dom":"^17.0.2"},"devDependencies":{"@types/rc-tooltip":"^3.7.1","@types/react-tag-autocomplete":"^6.0.0","@types/uuid":"^3.4.4"},"gitHead":"2cab1eaf4ac4d1a8ef3ef53ba3228ff16f7504eb","readme":"# Edit Context and UI State\n\n## Overview\n\nBodiless uses the [React Context API](https://reactjs.org/docs/context.html) to\nprovide a \"page edit context\"\n(../packages/bodiless-core/src/PageEditContext/index.tsx) instance to all\ncomponents on the page. This is an interface which allows a component to\ncontribute contextual elements to the page edit UI (currently limited to toolbar\nmenu items). For example, an image component might contribute a menu item which\nallows a user to upload an image.\n\nThe edit context also provides an interface to a global store which defines the\nUI state, including:\n\n- which edit context is \"active\" (ie, which component has the current focus),\n  and what related menu options should be available.\n- whether edit mode is active (ie, whether components should expose their edit\n  interfaces)\n- in future, anything else which might affect all components on the page (eg\n  whether the current changes have been pushed, whether there are upstream\n  changes to be pulled, etc).\n\n## Flow\n\nThe following diagram illustrates the flow of data for page edit contexts:\n\n![context](assets/context.jpeg)\n\n1. The `PageEditor`(../packages/bodiless-core/src/components/PageEditor.tsx) (the\n   top-level component of an editable page) defines a base edit context,\n   contributing menu options which apply to the page as a whole (e.g. whether\n   edit mode should be enabled). This is set as the default value of a React\n   context type named \"PageEditContext\". Provider and Consumer components for\n   this context are available as static properties of the PageEditContext class\n   (`PageEditContext.Provider` and `PageEditContext.Consumer`).\n2. Any item on the page which wishes to define a more specific context can do so\n   using the `spawn()` method of the `PageEditContext` instance. This will\n   create a new instance with the current instance as its parent. This can then\n   be set as the new context value using `PageEditContext.Provider`. For\n   convenience, a `ContextProvider`(../packages/bodiless-core/src/PageContextProvider.tsx)\n   component is provided which creates a new context and injects it into its\n   children (provided as a render prop).\n3. A component which does not want to create a new context can either consume\n   the current context (either via `PageEditContext.Consumer` or via the\n   `useEditContext()` hook), or simply ignore it, if it doesn't care about the\n   current context or UI state.\n4. The `PageEditContext` instance exposes an `activate()` method which can be\n   used by a component to declare that its context is active (usually when the\n   user focuses there).  This sets the active context in the store. If the active\n   context has parents, then these are also considered active, and the set of\n   all active contexts is referred to as the <a id=\"context-trail\"></a>\n   _*context trail*_. For convenience a `useContextActivator()` hook is provided.\n5. This, in turn triggers any observer component to re-render (for example to\n   highlight the fact that it is active).\n6. The \"global context menu\"(../packages/bodiless-core/src/components/PageEditor.tsx)\n   is one such observer component, and re-renders itself with all menu options\n   contributed by the newly activated context and all its parents. Thus, for\n   example, when you activate the context of an image within a grid, the menu\n   options of the image (eg set source) are added to those of the grid (eg\n   insert/delete items), which in turn are added to the page level items (eg\n   toggle edit on/off).\n7. Similarly, the\n   \"local context menu\"(../packages/bodiless-core/src/components/LocalContextMenu.tsx)\n   is another observer which renders a subset of context menu options as a tooltip\n   near the component providing current innermost active context.\n\n## Working with edit context\n\nThere are several ways in which a component can provide or consume the edit context.\n\n### Providing a new context\n\nTo provide a new context value (usually to add menu options which should appear\nwhen a component has focus), you may use the supplied `PageContextProvider`\n\n```javascript\nconst getExampleMenuOptions = useGetter([{\n  // An array of context menu option objects\n}]);\n\nconst Example: React.FC = ({ children }) => (\n  <PageContextProvider getMenuOptions={getExampleMenuOptions} name=\"Example\">\n    {children}\n  </PageContextProvider}\n```\nHere we provide a menu options callback as a prop to the `ContextProvider`\ncomponent. This will be invoked when this context or any descendant is\nactivated. It should return any menu options this component wishes to provide\n(see [Context Menu Options](#context-menu-options) below for more information).\n\n> Note: It is important to memoize our `getMenuOptions()` callback so as to\n> prevent unnecessary renders of subscribers to the edit context. Here, we do\n> this with the bodilesscore `useGetter()` hook, which creates an invariant\n> callback even if the menu options it returns change.\n\nIt is actually unusual to invoke the provider directly in this manner. Instead,\nuse the `withMenuOptions` hoc to attach options to your component. First you\ndefine a custom hook which will create your `getMenuOptions()` callback. This\nhook will be invoked when the component is rendered, and will receive its props\nas an argument:\n\n```\nconst useMenuOptions = (props) => {\n  return [{\n    // An array of context menu option objects\n  }];\n};\n```\n\nThen, pass that along with a unique name to `withMenuOptions` to create an HOC\nwhich will add the options to your component.\n\n```\nconst ComponentWithMyOptions = withMenuOptions({\n  useMenuOptions,\n  name: 'my-component'\n})(AnyComponent);\n...\n<ComponentWithMyOptions propUsedInOption=\"foo\" />\n```\nNote here that we don't need to memoize the list of menu options: memoization of\nthe callback is handled within `withMenuOptions`. However, certain callbacks\nprovided as properties of the menu option object may need to be memoized (see\n[Context Menu Options](#context-menu-options) below for more information).\n\nNote that the menu options you are defining here will only be available when\nyour component (or one of its children) declares itself as \"active\". To do so,\nit will need to use a method on the current context.\n\n### Consuming the context with hooks\n\nTo access the current page edit context, simply use the `useEditContext()`\nand/or `useContextActivator()` hooks.\n\n```javascript\nimport { observer } from 'mobx-react';\nconst Example = observer(props => {\n  const { isActive, isEdit } = useEditContext();\n  const { onClick } = useContextActivator();\n  return (\n    <React.Fragment>\n      <div>{isActive ? 'Active' : 'Not Active'}</div>\n      {isEdit ?\n        <button onClick={onClick} />\n        : <span>Not editing!</span>\n      }\n    </React.Fragment>\n  );\n});\n```\n\nNote that `useContextActivator()` can be used to provide a handler for other\nevents besides `onClick`, and can invoke another handler passed in as a prop:\n\n```\nconst TextareaActivator = ({ onFocus: onFocus$1, ...rest }) => {\n  const { onFocus } = useContextActivator('onFocus', onFocus$1);\n  return <textarea onFocus={onFocus} {...rest} />\n}\n```\nIn the above example, the `textarea` will activate the context on focus, and\nwill then invoke any 'onFocus' handler passed to it.\n\nNote that we are using the `isActive` property of the context to render\ndifferently if the current context is active, and using the `isEdit` property to\ndetermine if we are in edit mode. Note also that we wrap our component in a\n[mobx observer](https://mobx-react.js.org/observer-hoc) to ensure that it\nupdates properly if the active context or edit mode changes.\n\n> Important! For functional components using hooks, we must use the version of\n> `observer` from mobx-react.\n\nIt is also possible to use the `withContextActivator` HOC to provide an activation\nevent to a pre-existing component:\n```\nconst ComponentWithMenuOption = flowRight(\n  withMenuOptions({ ... }), // See abo ve\n  withContextActivator('onClick'),\n)(AnyComponent)\n```\n\nNow, when you click on `AnyComponent` it will activate the provided context, and\nthe associated menu options will be displayed.\n\nNote - the above will only work if `AnyComponent` can accept an `onClick` prop.\nIf not, you will want to wrap it in an element which can, for example a `div`:\n```\nconst ComponentWithMenuOption = flowRight(\n  withMenuOptions({ ... }), // See above\n  withContextActivator('onClick'),\n  withActivatorWrapper('div'),\n)(AnyComponent)\n```\n\nFinally, you will want to be sure that none of the above HOC's are applied when not in edit mode.\nFor this, `ifEditable` comes in handy:\n```\nconst ComponentWithMenuOption = ifEditable(\n  withMenuOptions({ ... }), // See above\n  withContextActivator('onClick'),\n  withActivatorWrapper('div'),\n)(AnyComponent)\n```\nNote that the order of these HOC's is important.\n\n## Context Menu Options\n\nThe Global Context Menu aggregates menu options provided by all contexts within\nthe [active context trail](#context-trail). It is a wrapper around the generic\n`ContextMenu` (../packages/bodiless-core/src/components/ContextMenu.tsx)\ncomponent, which provides a mechanism for displaying a set of menu options\nprovided in an \"options\" prop. Each item is an object with the following\nmembers:\n- name: a unique machine name for this item.\n- group: the machine name of the group to which this button belongs\n- Component: An optional component to use to render this button.\n- icon: the name of a [material design icon](https://material.io/tools/icons/?style=baseline)\n  to display for this item.\n- label: a human readable label to display beneath the icon.\n- isActive; a boolean specifying the option or its flyout panel is currently\n  \"active\". This is usually used for toggles.\n- isDisabled; a boolean specifying whether the option is currently \"disabled\".\n  If true, the menu button will be greyed out and bnot clickable.\n- isHidden: a boolean specifying whether the option is currently hidden. If\n  true, the menu button will not be displayed.\n- handler: a callback to invoke when the menu button is clicked.\n\n> Note all of the above properties except `handler`, `name`, `group` and\n> `Component` can be provided either as a primitive value or as a function\n> returning that value - eg `{ isActive: true }` or `{ isActive: () => true; }`\n> The latter is useful if the value depends on some external state which is not\n> used by the component providing the button, to allow the button to update\n> without re-rendering the component. If you provide callbacks, it is important\n> to memoize any such callbacks to avoid unnecessary renders of the button\n> itself. The `handler` callback, however, need not be memoized.\n\n A simple context menu implementation with a single option might look like this:\n ```javascript\nconst [isUp, setIsUp] = React.useState(false);\n\nconst options = [\n{\n  name: 'say_yes',\n  icon: isUp ? 'thumb_up' : 'thumb_down',\n  label: 'Yes!',\n  isActive: isUp,\n  handler: () => {\n    if (!isUp) {\n      alert('Yes!');\n    }\n    setIsUp(isUp => !isUp);\n  },\n];\n\nconst MyContextMenu = props => (\n  <ContextMenu options={options} />\n);\n```\n\n#### Global and Local Context Menu\n\nBy default, Bodiless provides two `ContextMenu` instances to display menu options\nprovided by edit contexts. \n\nThe `GlobalContextMenu` is a part of the `PageEditor` and is intended to provide\na single, top-level menu for the page.\n\nThe `LocalContextMenu` component may be used to add a tooltip version of the\ncontext menu to any component, as\n\n```\n<ContextProvider getMenuOptions={myComponentMenuOptionGetter} name=\"My Component\">\n  <LocalContextMenu>\n    <MyComponent>\n    ...\n    </MyComponent>\n  </LocalContextMenu>\n</ContextProvider>\n```\n\nThis will provide a tooltip for `MyComponent` which displays a local version of the context menu.\n\nWhen defining a menu option, there are two flags you can use to control whether\nit should appear on the global context menu, the local context menu or both:\n\n```javascript\nconst myMenuOption = {\n  ...,\n  global: true // Show on global context menu\n  local: false // Hide on local context menu\n}\n```\n\n#### Context menu forms\n\nThe handler function for a menu option can, optionally, return a render\nfunction. If it does, when the menu item is selected, this will be invoked to\nrender the contents of a fly-out panel (usually a form). This allows menu\noptions to trigger display of a configuration form to collect additional user\ninput. For example, an image component might provide a menu option to configure\nits content. This might then display a form for uploading the image, entering\nalt-text, etc.\n\n## Styling\n\nThis library includes components which render visible, interactive pieces of the edit UI. These include:\n\n- ContextMenu\n- PageEditor\n- LocalContextMenu\n- ContextWrapper\n\nThese components allow injection of UI elements via a `ui` prop. This prop is a\njavascript object enumerating the UI elements which the component will use. For example,\nthe UI specification for a ContextMenu is:\n\n```javascript\nexport type UI = {\n  Icon?: ComponentType<HTMLProps<HTMLSpanElement>>;\n  Toolbar?: ComponentType<HTMLProps<HTMLDivElement>>;\n  ToolbarButton?: ComponentType<ButtonVariantProps>;\n  FormWrapper?: ComponentType<HTMLProps<HTMLDivElement>>;\n  ToolbarDivider?: ComponentType<HTMLProps<HTMLHRElement>>;\n};\n```\n\nThe `ui` prop fo `ContextMenu` accepts low-level UI elements which are composed\nto render the menu. In contrast, that for `LocalContextMenu` and `PageEditor`\nsimply lists the menu component which should be used to render the currently\nselected options:\n\n```\ntype UI = {\n  LocalContextMenu?: ComponentType<ContextMenuProps>;\n};\n```\n\nThis allows us to create customized versions of the context menu, as is done in\nthe canvas-x\nstylable `ContextMenu` (../packages/bodiless-core-ui/src/GlobalContextMenu.tsx)\nand\n `LocalContextMenu` (../packages/bodiless-core-ui/src/LocalContextMenu.tsx).\n\n## Activate Context System\n\nThe activate Context system allow for one component to store an id of another\ncomponent that should activate its context on creation.\n\nThis is a three step process.\n\n### Step 1 Ensure there is a Activate Context in place\n\nOne can wrap there components in a `<ActivateContextProvider>` component or one\ncan use the withActivateContext() HOC that will wrap a component in the Provider\n\n### Step 2 set the Id\n\nThen one needs to set the id of the component to be activated by pulling `setId` from `useActivateContext`.\n\n``` tsx\nconst { setId } from useActivateContext();\nsetId(id);\n```\n\n### Step 3  Add useActivateOnEffect hook\n\nFinally, the component needs to have the `useActivateOnEffect` hook so that it will activate if the id match the one stored\n\n``` tsx\nuseActivateOnEffect(id);\n```\n\n","readmeFilename":"README.md","bugs":{"url":"https://github.com/johnsonandjohnson/bodiless-js/issues"},"homepage":"https://github.com/johnsonandjohnson/bodiless-js#readme","_id":"@asemirsk/core@1.0.0-canary-24-23.0","_nodeVersion":"16.13.2","_npmVersion":"lerna/4.0.0/node@v16.13.2+x64 (linux)","dist":{"integrity":"sha512-atEaUNESj27tN+Olt1Ksl4T7aVKhjDgeR1TsvsL+G2dUqD718wKb9W37pqyO8h9az0R8m9vGrxGEqr64iIWLzw==","shasum":"03b98177da083a6c766bdf30f7bd28405a8d8657","tarball":"https://registry.npmjs.org/@asemirsk/core/-/core-1.0.0-canary-24-23.0.tgz","fileCount":281,"unpackedSize":647854,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIBVILVrmKYHAYuF8Wbhn5qAoRMBOmWmqcsRyb3OJ70HvAiEAueU1v/rXgN9Mg/zykVKQ+Cq8hkkQKXoCe29NCDFJHRA="}],"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJijfHrACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmrCyA//UsgW8LaQCJg+WlQb861BcpCfQYt1R5oDHZdeNXsduK7a8fOQ\r\nqiOER6/Ikvz1G+WzJ6WQZr5UV65xdDxFD3aj5Gy1BVdo5IipotqddYhmosAO\r\njgCA1yQZO619YKFNiKFrS2mGnt2s4jJsXB1HVhSLtH4gVWXF6wzk8oyJcjAl\r\nbuGJ2gd4CMjBUTXvC5/A2MZG+7PSNTuJmao3y3VTODvLr5xQp+ICzhBhb05B\r\nnCglXMDQsvs8SlBUhxkRnAkdTy+ki1x73vlCRNv9ItXXXMn3ZHKv+KTdsqef\r\niUooAokAVKMwp/mmCnk8ntxTu4YiL5C7EGgcSu/FGlcZG1CJlH9hHnwuY0c+\r\nhA/mJyiOAMomKu0z6hJXf9F1v5plHvyHzvbpCi5OLCIVAksNO9tPYAbFD6VR\r\nz3Pedu773+ggxX7264dX1cBSeRuBluGBEvKvXIVdWqNTEHF3G/P7eVWw6p3Q\r\n0EJTNie7AhtwCRXrkus+U3E9A9q1ayOIX+Yw7dNmxck4G7UxPelgP3VBHpEM\r\nLRiiDLTT5azJBLnzMNqXq1/od5fFWrKvTsCd9BWwLFrDGnAnNyO3gy3D6xjl\r\nU2cajHmUR68tTOhyprY8OEbVkiA3YZeZpRRnlchMh1L4WsximnPVgpx8AENc\r\nTIec6fYyLjc3TRoXfhJNxxiN7wAvEyxVJcM=\r\n=3kVD\r\n-----END PGP SIGNATURE-----\r\n"},"_npmUser":{"name":"asemirsk","email":"al.semirski@gmail.com"},"directories":{},"maintainers":[{"name":"asemirsk","email":"al.semirski@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/core_1.0.0-canary-24-23.0_1653469674841_0.3920130847890335"},"_hasShrinkwrap":false},"1.0.0-canary-24-24.0":{"name":"@asemirsk/core","version":"1.0.0-canary-24-24.0","description":"Edit context for BodilessJS site editor","author":{"name":"Chris Oden","email":"coden@its.jnj.com"},"license":"Apache-2.0","main":"lib/index.js","sideEffects":false,"typings":"lib/index.d.ts","repository":{"type":"git","url":"git+https://github.com/johnsonandjohnson/bodiless-js.git"},"publishConfig":{"access":"public"},"scripts":{"build":"run-p build:lib","build:lib":"tsc -p ./tsconfig.json","build:watch":"npm run build:lib -- --watch","build:api-doc":"typedoc --options ../../typedoc.js --out doc/api ./src","clean":"rimraf \"lib/*\" && rimraf tsconfig.tsbuildinfo && rimraf \"doc/api\""},"dependencies":{"@asemirsk/fclasses":"1.0.0-canary-24-24.0+af2c8ac","@types/react-tag-autocomplete":"^6.0.0","axios":"^0.21.0","enzyme":"^3.9.0","informed":"^3.34.0","lodash":"^4.17.19","rc-tooltip":"^5.1.1","react-tag-autocomplete":"^6.0.0","resize-observer-polyfill":"^1.5.1","universal-cookie":"^4.0.3","uuid":"^3.3.2"},"peerDependencies":{"mobx":"^6.0.0","mobx-react":"^7.2.1","react":"^17.0.2","react-dom":"^17.0.2"},"devDependencies":{"@types/rc-tooltip":"^3.7.1","@types/react-tag-autocomplete":"^6.0.0","@types/uuid":"^3.4.4"},"gitHead":"af2c8ace172b3dc3a1f237c51eb55be1e21978bb","readme":"# Edit Context and UI State\n\n## Overview\n\nBodiless uses the [React Context API](https://reactjs.org/docs/context.html) to\nprovide a \"page edit context\"\n(../packages/bodiless-core/src/PageEditContext/index.tsx) instance to all\ncomponents on the page. This is an interface which allows a component to\ncontribute contextual elements to the page edit UI (currently limited to toolbar\nmenu items). For example, an image component might contribute a menu item which\nallows a user to upload an image.\n\nThe edit context also provides an interface to a global store which defines the\nUI state, including:\n\n- which edit context is \"active\" (ie, which component has the current focus),\n  and what related menu options should be available.\n- whether edit mode is active (ie, whether components should expose their edit\n  interfaces)\n- in future, anything else which might affect all components on the page (eg\n  whether the current changes have been pushed, whether there are upstream\n  changes to be pulled, etc).\n\n## Flow\n\nThe following diagram illustrates the flow of data for page edit contexts:\n\n![context](assets/context.jpeg)\n\n1. The `PageEditor`(../packages/bodiless-core/src/components/PageEditor.tsx) (the\n   top-level component of an editable page) defines a base edit context,\n   contributing menu options which apply to the page as a whole (e.g. whether\n   edit mode should be enabled). This is set as the default value of a React\n   context type named \"PageEditContext\". Provider and Consumer components for\n   this context are available as static properties of the PageEditContext class\n   (`PageEditContext.Provider` and `PageEditContext.Consumer`).\n2. Any item on the page which wishes to define a more specific context can do so\n   using the `spawn()` method of the `PageEditContext` instance. This will\n   create a new instance with the current instance as its parent. This can then\n   be set as the new context value using `PageEditContext.Provider`. For\n   convenience, a `ContextProvider`(../packages/bodiless-core/src/PageContextProvider.tsx)\n   component is provided which creates a new context and injects it into its\n   children (provided as a render prop).\n3. A component which does not want to create a new context can either consume\n   the current context (either via `PageEditContext.Consumer` or via the\n   `useEditContext()` hook), or simply ignore it, if it doesn't care about the\n   current context or UI state.\n4. The `PageEditContext` instance exposes an `activate()` method which can be\n   used by a component to declare that its context is active (usually when the\n   user focuses there).  This sets the active context in the store. If the active\n   context has parents, then these are also considered active, and the set of\n   all active contexts is referred to as the <a id=\"context-trail\"></a>\n   _*context trail*_. For convenience a `useContextActivator()` hook is provided.\n5. This, in turn triggers any observer component to re-render (for example to\n   highlight the fact that it is active).\n6. The \"global context menu\"(../packages/bodiless-core/src/components/PageEditor.tsx)\n   is one such observer component, and re-renders itself with all menu options\n   contributed by the newly activated context and all its parents. Thus, for\n   example, when you activate the context of an image within a grid, the menu\n   options of the image (eg set source) are added to those of the grid (eg\n   insert/delete items), which in turn are added to the page level items (eg\n   toggle edit on/off).\n7. Similarly, the\n   \"local context menu\"(../packages/bodiless-core/src/components/LocalContextMenu.tsx)\n   is another observer which renders a subset of context menu options as a tooltip\n   near the component providing current innermost active context.\n\n## Working with edit context\n\nThere are several ways in which a component can provide or consume the edit context.\n\n### Providing a new context\n\nTo provide a new context value (usually to add menu options which should appear\nwhen a component has focus), you may use the supplied `PageContextProvider`\n\n```javascript\nconst getExampleMenuOptions = useGetter([{\n  // An array of context menu option objects\n}]);\n\nconst Example: React.FC = ({ children }) => (\n  <PageContextProvider getMenuOptions={getExampleMenuOptions} name=\"Example\">\n    {children}\n  </PageContextProvider}\n```\nHere we provide a menu options callback as a prop to the `ContextProvider`\ncomponent. This will be invoked when this context or any descendant is\nactivated. It should return any menu options this component wishes to provide\n(see [Context Menu Options](#context-menu-options) below for more information).\n\n> Note: It is important to memoize our `getMenuOptions()` callback so as to\n> prevent unnecessary renders of subscribers to the edit context. Here, we do\n> this with the bodilesscore `useGetter()` hook, which creates an invariant\n> callback even if the menu options it returns change.\n\nIt is actually unusual to invoke the provider directly in this manner. Instead,\nuse the `withMenuOptions` hoc to attach options to your component. First you\ndefine a custom hook which will create your `getMenuOptions()` callback. This\nhook will be invoked when the component is rendered, and will receive its props\nas an argument:\n\n```\nconst useMenuOptions = (props) => {\n  return [{\n    // An array of context menu option objects\n  }];\n};\n```\n\nThen, pass that along with a unique name to `withMenuOptions` to create an HOC\nwhich will add the options to your component.\n\n```\nconst ComponentWithMyOptions = withMenuOptions({\n  useMenuOptions,\n  name: 'my-component'\n})(AnyComponent);\n...\n<ComponentWithMyOptions propUsedInOption=\"foo\" />\n```\nNote here that we don't need to memoize the list of menu options: memoization of\nthe callback is handled within `withMenuOptions`. However, certain callbacks\nprovided as properties of the menu option object may need to be memoized (see\n[Context Menu Options](#context-menu-options) below for more information).\n\nNote that the menu options you are defining here will only be available when\nyour component (or one of its children) declares itself as \"active\". To do so,\nit will need to use a method on the current context.\n\n### Consuming the context with hooks\n\nTo access the current page edit context, simply use the `useEditContext()`\nand/or `useContextActivator()` hooks.\n\n```javascript\nimport { observer } from 'mobx-react';\nconst Example = observer(props => {\n  const { isActive, isEdit } = useEditContext();\n  const { onClick } = useContextActivator();\n  return (\n    <React.Fragment>\n      <div>{isActive ? 'Active' : 'Not Active'}</div>\n      {isEdit ?\n        <button onClick={onClick} />\n        : <span>Not editing!</span>\n      }\n    </React.Fragment>\n  );\n});\n```\n\nNote that `useContextActivator()` can be used to provide a handler for other\nevents besides `onClick`, and can invoke another handler passed in as a prop:\n\n```\nconst TextareaActivator = ({ onFocus: onFocus$1, ...rest }) => {\n  const { onFocus } = useContextActivator('onFocus', onFocus$1);\n  return <textarea onFocus={onFocus} {...rest} />\n}\n```\nIn the above example, the `textarea` will activate the context on focus, and\nwill then invoke any 'onFocus' handler passed to it.\n\nNote that we are using the `isActive` property of the context to render\ndifferently if the current context is active, and using the `isEdit` property to\ndetermine if we are in edit mode. Note also that we wrap our component in a\n[mobx observer](https://mobx-react.js.org/observer-hoc) to ensure that it\nupdates properly if the active context or edit mode changes.\n\n> Important! For functional components using hooks, we must use the version of\n> `observer` from mobx-react.\n\nIt is also possible to use the `withContextActivator` HOC to provide an activation\nevent to a pre-existing component:\n```\nconst ComponentWithMenuOption = flowRight(\n  withMenuOptions({ ... }), // See abo ve\n  withContextActivator('onClick'),\n)(AnyComponent)\n```\n\nNow, when you click on `AnyComponent` it will activate the provided context, and\nthe associated menu options will be displayed.\n\nNote - the above will only work if `AnyComponent` can accept an `onClick` prop.\nIf not, you will want to wrap it in an element which can, for example a `div`:\n```\nconst ComponentWithMenuOption = flowRight(\n  withMenuOptions({ ... }), // See above\n  withContextActivator('onClick'),\n  withActivatorWrapper('div'),\n)(AnyComponent)\n```\n\nFinally, you will want to be sure that none of the above HOC's are applied when not in edit mode.\nFor this, `ifEditable` comes in handy:\n```\nconst ComponentWithMenuOption = ifEditable(\n  withMenuOptions({ ... }), // See above\n  withContextActivator('onClick'),\n  withActivatorWrapper('div'),\n)(AnyComponent)\n```\nNote that the order of these HOC's is important.\n\n## Context Menu Options\n\nThe Global Context Menu aggregates menu options provided by all contexts within\nthe [active context trail](#context-trail). It is a wrapper around the generic\n`ContextMenu` (../packages/bodiless-core/src/components/ContextMenu.tsx)\ncomponent, which provides a mechanism for displaying a set of menu options\nprovided in an \"options\" prop. Each item is an object with the following\nmembers:\n- name: a unique machine name for this item.\n- group: the machine name of the group to which this button belongs\n- Component: An optional component to use to render this button.\n- icon: the name of a [material design icon](https://material.io/tools/icons/?style=baseline)\n  to display for this item.\n- label: a human readable label to display beneath the icon.\n- isActive; a boolean specifying the option or its flyout panel is currently\n  \"active\". This is usually used for toggles.\n- isDisabled; a boolean specifying whether the option is currently \"disabled\".\n  If true, the menu button will be greyed out and bnot clickable.\n- isHidden: a boolean specifying whether the option is currently hidden. If\n  true, the menu button will not be displayed.\n- handler: a callback to invoke when the menu button is clicked.\n\n> Note all of the above properties except `handler`, `name`, `group` and\n> `Component` can be provided either as a primitive value or as a function\n> returning that value - eg `{ isActive: true }` or `{ isActive: () => true; }`\n> The latter is useful if the value depends on some external state which is not\n> used by the component providing the button, to allow the button to update\n> without re-rendering the component. If you provide callbacks, it is important\n> to memoize any such callbacks to avoid unnecessary renders of the button\n> itself. The `handler` callback, however, need not be memoized.\n\n A simple context menu implementation with a single option might look like this:\n ```javascript\nconst [isUp, setIsUp] = React.useState(false);\n\nconst options = [\n{\n  name: 'say_yes',\n  icon: isUp ? 'thumb_up' : 'thumb_down',\n  label: 'Yes!',\n  isActive: isUp,\n  handler: () => {\n    if (!isUp) {\n      alert('Yes!');\n    }\n    setIsUp(isUp => !isUp);\n  },\n];\n\nconst MyContextMenu = props => (\n  <ContextMenu options={options} />\n);\n```\n\n#### Global and Local Context Menu\n\nBy default, Bodiless provides two `ContextMenu` instances to display menu options\nprovided by edit contexts. \n\nThe `GlobalContextMenu` is a part of the `PageEditor` and is intended to provide\na single, top-level menu for the page.\n\nThe `LocalContextMenu` component may be used to add a tooltip version of the\ncontext menu to any component, as\n\n```\n<ContextProvider getMenuOptions={myComponentMenuOptionGetter} name=\"My Component\">\n  <LocalContextMenu>\n    <MyComponent>\n    ...\n    </MyComponent>\n  </LocalContextMenu>\n</ContextProvider>\n```\n\nThis will provide a tooltip for `MyComponent` which displays a local version of the context menu.\n\nWhen defining a menu option, there are two flags you can use to control whether\nit should appear on the global context menu, the local context menu or both:\n\n```javascript\nconst myMenuOption = {\n  ...,\n  global: true // Show on global context menu\n  local: false // Hide on local context menu\n}\n```\n\n#### Context menu forms\n\nThe handler function for a menu option can, optionally, return a render\nfunction. If it does, when the menu item is selected, this will be invoked to\nrender the contents of a fly-out panel (usually a form). This allows menu\noptions to trigger display of a configuration form to collect additional user\ninput. For example, an image component might provide a menu option to configure\nits content. This might then display a form for uploading the image, entering\nalt-text, etc.\n\n## Styling\n\nThis library includes components which render visible, interactive pieces of the edit UI. These include:\n\n- ContextMenu\n- PageEditor\n- LocalContextMenu\n- ContextWrapper\n\nThese components allow injection of UI elements via a `ui` prop. This prop is a\njavascript object enumerating the UI elements which the component will use. For example,\nthe UI specification for a ContextMenu is:\n\n```javascript\nexport type UI = {\n  Icon?: ComponentType<HTMLProps<HTMLSpanElement>>;\n  Toolbar?: ComponentType<HTMLProps<HTMLDivElement>>;\n  ToolbarButton?: ComponentType<ButtonVariantProps>;\n  FormWrapper?: ComponentType<HTMLProps<HTMLDivElement>>;\n  ToolbarDivider?: ComponentType<HTMLProps<HTMLHRElement>>;\n};\n```\n\nThe `ui` prop fo `ContextMenu` accepts low-level UI elements which are composed\nto render the menu. In contrast, that for `LocalContextMenu` and `PageEditor`\nsimply lists the menu component which should be used to render the currently\nselected options:\n\n```\ntype UI = {\n  LocalContextMenu?: ComponentType<ContextMenuProps>;\n};\n```\n\nThis allows us to create customized versions of the context menu, as is done in\nthe canvas-x\nstylable `ContextMenu` (../packages/bodiless-core-ui/src/GlobalContextMenu.tsx)\nand\n `LocalContextMenu` (../packages/bodiless-core-ui/src/LocalContextMenu.tsx).\n\n## Activate Context System\n\nThe activate Context system allow for one component to store an id of another\ncomponent that should activate its context on creation.\n\nThis is a three step process.\n\n### Step 1 Ensure there is a Activate Context in place\n\nOne can wrap there components in a `<ActivateContextProvider>` component or one\ncan use the withActivateContext() HOC that will wrap a component in the Provider\n\n### Step 2 set the Id\n\nThen one needs to set the id of the component to be activated by pulling `setId` from `useActivateContext`.\n\n``` tsx\nconst { setId } from useActivateContext();\nsetId(id);\n```\n\n### Step 3  Add useActivateOnEffect hook\n\nFinally, the component needs to have the `useActivateOnEffect` hook so that it will activate if the id match the one stored\n\n``` tsx\nuseActivateOnEffect(id);\n```\n\n","readmeFilename":"README.md","bugs":{"url":"https://github.com/johnsonandjohnson/bodiless-js/issues"},"homepage":"https://github.com/johnsonandjohnson/bodiless-js#readme","_id":"@asemirsk/core@1.0.0-canary-24-24.0","_nodeVersion":"16.13.2","_npmVersion":"lerna/4.0.0/node@v16.13.2+x64 (linux)","dist":{"integrity":"sha512-IMFxZTH3+/4QT2AkgRzGHWNR8or4CQAJdcbVyhgSc+DLEK8Db1NuwQzCkCGNRSzucGGxKOF5Ixaf5Wy0Kr/xfQ==","shasum":"bc170df09b1680a2927d9d8d27d5660552343a96","tarball":"https://registry.npmjs.org/@asemirsk/core/-/core-1.0.0-canary-24-24.0.tgz","fileCount":281,"unpackedSize":647854,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQCX2n0WhYdDbZX+azl72o7ghAjNLQOOl4DJdE61cfJ/YQIgT9j+T/HwYCikwURBbAFQEtJFL6qBpIzN6Dtnty7WrE0="}],"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJijfQ/ACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmrdVBAAgzzJfXAQOi/z3G9FOhaBsI2zVvM7QE8C3sfWNPNZatwd19T4\r\nNTYI1dcGAbS5qh5ZGMW1WIvoWS2WSWCKEwmqdw88E/Rp/6USIbxnSAZBiTtH\r\ngp4GuXBkft8XLzPErRy+4OnS1eL1f68KRHN2oabY+Tka4NVAGA/QzuQWWcZ8\r\nDnVS8XeeTAgzVlnQM6FXdDQP95PH/XeoV4In7fX45PsNLOxtsG4PbuFnuX9L\r\nivvayI98lFPC/FaLC65VHqywECzUkfwigzYBqDJiGoorFuqGeSgRQdr+XNv4\r\nKda9mGYZkzVrzXMjOeipKa/wrubsPYVbeztMxbWq66GdwGRIiYevWjFO6WLv\r\nBV83vLSCh/tkGbOuuNpO1Cw6BZPreFnrwFI1Dz8BPcjYOifXtGYCMlrI8YOw\r\nTK7UfTJ1vJZDv+4PvpuDSAlvMrCmW6EN9jgLF+726lgg51DyLz4euE6YMSiF\r\nHrMuY5VxCPC48iNnorpWSZL56IJXrrsoZ5CXMl5ncbnFqB0yuXxZAXUtXXSC\r\nwSp5vp/esDyQvd7+M7WYLX3iNOtxuk3sJkRuNFHUTydZHet3mE9+a9kaFH3k\r\nJY7UbXcyyu0sW9+ap/Vr/yVhREBdADf+3+u9OuaXWtOSwkM7S0UU8LTrhWWI\r\nRJdCHPKDnxAPYAaSbCidTaC+wmH14g4wQZA=\r\n=/1LW\r\n-----END PGP SIGNATURE-----\r\n"},"_npmUser":{"name":"asemirsk","email":"al.semirski@gmail.com"},"directories":{},"maintainers":[{"name":"asemirsk","email":"al.semirski@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/core_1.0.0-canary-24-24.0_1653470271179_0.17176813172629468"},"_hasShrinkwrap":false}},"maintainers":[{"name":"asemirsk","email":"al.semirski@gmail.com"}],"description":"Edit context for BodilessJS site editor","homepage":"https://github.com/johnsonandjohnson/bodiless-js#readme","repository":{"type":"git","url":"git+https://github.com/johnsonandjohnson/bodiless-js.git"},"author":{"name":"Chris Oden","email":"coden@its.jnj.com"},"bugs":{"url":"https://github.com/johnsonandjohnson/bodiless-js/issues"},"license":"Apache-2.0","readme":"","readmeFilename":""}