Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

streamlit-notification-center-component

Package Overview
Dependencies
Maintainers
1
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

streamlit-notification-center-component

Streamlit component that listens for postMessage events and reports back

  • 0.0.13
  • PyPI
  • Socket score

Maintainers
1

streamlit-notification_center-component

Streamlit component that listens to postMessage() traffic with a certain format.

Installation instructions

pip install streamlit_notification_center_component

Testing instructions

If you wish to test the component outside of another project, open two shells. In one, you'll want to run the react code in one shell and the python __init__.py in another. However, before you do this you need to open __init__.py first and change the flag _RELEASE from True to False or you'll get no output.

In shell no.1

cd notification_center/frontend
npm install # if you haven't done this before
npm start

In shell no.2

source venv/bin/activate
streamlit run notification_center/__init__.py

If all went well, you'll see a window open up with some text and some buttons.

Usage instructions

import streamlit as st
from streamlit_notification_center_component import notification_center

notification_center(key="globalnc")

Helper JavaScript methods

function ncSendMessage(target, body) {
  Array.from(window.parent.frames).forEach((frame) => {
    frame.postMessage({ type: 'nc', target: target, payload: body }, "*")
  })
}

function ncClearTarget(target) {
  Array.from(window.parent.frames).forEach((frame) => {
    frame.postMessage({ type: 'nc', target: target, command: 'clear' }, "*")
  })
}

function ncClearAll() {
  Array.from(window.parent.frames).forEach((frame) => {
    frame.postMessage({ type: 'nc', command: 'clear-all' }, "*")
  })
}

Let's talk about what's going on here. Each of these three functions loop over all frames in the parent and invoke a postMessage on them. The type: 'nc' code MUST be present or the notification_center widget will ignore the message.

In cases where clearing the data from state on the streamlit side is desired, a command: property needs to be included. There are two possible values:

  • 'clear': when present, a target: property must also be included to identify what to clear
  • 'clear-all': when present, no other value than type: 'nc' needs be present. All messages stored in the st.session_state._nc_data dict will be removed.

Lastly, when it comes to a normal message to be queued, the way this works is that streamlit will receive the value from the component's call to Streamlit.setComponentValue(). This will be a pre-vetted object conforming to the following TypeScript type:

export type NotificationMessage = {
 type: 'nc';
 target?: string;
 payload?: any;
 from?: string;
 command?: null | 'clear' | 'clear-all';
}

When not sending a command to clear and type, target and payload are all present, then the dict st.session_state._nc_data will have its key .setdefault(target, []). This ensures that there is at least an empty array of values for the supplied target. Then the payload object is appended to this array. All streamlit code can check for and use values.

Helper Python Methods

In addition to providing the streamlit component for use, the component comes with several useful python functions that make working with the component much easier. These functions are obviously provided to aid on the streamlit/python side of things.

nc_ensure()

The nc_ensure() function is provided to ensure that the environment is setup properly for use. It is heavily used by the internal functions and can be used in your own scripts. For example, if you wish to setup your use of notification_center() in a separate file, you might do something like this:


from notification_center import nc_ensure, nc_add_style

def nc_setup():
  # Ensure notification center globals are ready
  nc_ensure()
  
  # Add styles for use with nc_html()
  nc_add_style(f'* {{font-size:200%;}}')

This function would ensure that any poking or prodding to st.session_state._nc_ variables would be ready to go if this is something you needed to do.

nc_get(key, default_value=None)

The nc_get() function allows the retrieval of the array of messages sent to the key in question. If no value exists with the supplied key then the default_value, defaulting to None, is what is returned.


let button_presses = nc_get('button_clicks')
if button_presses:
  # do something

The value returned here will be a list or array of zero or more values sent to the key specified.

nc_get_last(key, default_value=None)

Similar to nc_get() however this function returns the last value in the list associated with the supplied key. If the list is empty or none exists with that key, then the default_value is returned.

let last_command = nc_get_last('executed_commands')
st.write(f'The last command executed was {last_command}')

nc_get_all()

This function returns the entire dictionary of values stored on the session state

let dictionary = nc_get_all()
# work on the dictionary

nc_set(key, value)

This is the value executed when postMessage() function is called and the NotificationCenter processes the contents successfully. The key value is used as a dictionary key and the value is appended to the array of previous messages for the same target.

nc_set('person', {'name': 'Brielle', 'gender': 'Female'})

nc_has(key)

This function returns True if the supplied key exists in the dictionary of NotificationCenter values, or False otherwise.

if nc_has('people'):
  people = nc_get('people')
  # do work on the people list

nc_clear(key)

This function will locate an existing key in the dictionary. These keys have a list as their value. The .clear() function will be invoked on the list.

# User clicked on delete all people
nc_clear('people')

nc_clear_all()

This function removes all keys and values from the NotificationCenter data store.

nc_clear_all()

nc_add_substitution(str_or_re, val_or_fn)

This function allows you to specify a replacement or compiled regular expression that is used to search the contents of a to-be-rendered HTML snippet. The value or function (Callable) will be used in the place of the sought value. If the regular expression captured groups, they will be passed to the function as parameters.

nc_add_substitution(re.compile(r':exit-button:(.*?):'), lambda title: f'<button>{title}</button>')
nc_html(
  f'''
  <body>:exit-button:Click to exit:</body>
  '''
)

This code would replace the ":exit-button:Click to exit:" with "Click to exit" as an example.

nc_add_script_src(src, attributes=None, raw=False)

This function takes a script source (URL or path) and optionally a dictionary of HTML attributes, and appends a formatted script tag or raw source to the session state list. If 'raw' is False, the 'src' and 'attributes' are used to create a ' calls.

nc_add_script_src('https://remotejslibrary.com/script.js')
nc_html(...) # remotejslibrary is automatically inserted into the nc_html

This function takes a link source (URL or path) and optionally a dictionary of HTML attributes, and appends a formatted link tag or raw source to the '_nc_link_rels' session state list. If 'raw' is False, the 'href' and 'attributes' are used to create a '' tag using BeautifulSoup. Each of these tags are inserted in subsequent nc_html() calls.

nc_add_link_rel('https://remotescript.com/style.css')
nc_html(...) # remote css script is automatically inserted into the nc_html

nc_add_script(script)

The function nc_html() is a more function version of the st.components.v1.html(). The nc_html() function will pre-inject CSS, JavaScript and any HTML snippets previously setup as global pre-defines. The nc_add_script() function allows you to add a snippet of JavaScript to all nc_html() calls before the supplied HTML string.

nc_add_script(
  f'''
  Object.defineProperty(Array.prototype, 'first', { get: function() { return this?.[0] } })
  '''
)

After running this function, the next time nc_html() is invoked, the component added to the page will have access to JavaScript arrays with a getter on each instance called .first that dynamically returns the first value or null if there isn't one.

nc_add_style(style)

The function nc_html() is a more function version of the st.components.v1.html(). The nc_html() function will pre-inject CSS, JavaScript and any HTML snippets previously setup as global pre-defines. The nc_add_style() function allows you to add a snippet of CSS to all nc_html() calls before the supplied HTML string.

nc_add_styles(f'* {{ font-size: 200%; }}')

When executed, all subsequent nc_html() calls will find their contents' font sizes increased to 200%.

nc_add_html(html)

This function adds a new snippet of html that will be included in all calls to nc_html().

nc_add_html(f'<hr/>')

When next nc_html() is executed, it will always be preceded by a horizontal rule.

nc_reset_script_srcs()

This function removes all previously specified script sources.

This function removes all previously specified link tags.

nc_reset_subs()

This function removes all previously specified substitutions.

nc_reset_scripts()

Resets all JavaScript snippets to the defaults. The defaults are the three aforementioned JavaScript Helper functions.

nc_reset_styles()

This function removes all globally added CSS snippets previously added using nc_add_style().

nc_reset_html()

Simlar in function to nc_reset_scripts() or nc_reset_styles(), the function nc_reset_html() removes all previously added HTML snippets.

nc_listen(to, callback)

Probably one of the most useful function, this call will execute the supplied callback whenever any new value is set with a key/target that matches the to value supplied to this function.

unsub = nc_listen('add_person', lambda person: print(person))
# when done
unsub()

This function also returns a function or Callable that when invoked will trigger an unsubscribe on the internal event manager.

nc_html(html, extra_js=[], extra_css=[], width=None, height=None, scrolling=False)

The creme of the crop, injects a component of HTML into the page. This HTML iframe that gets injected will be prefixed by any previous calls to nc_add_html(), nc_add_style(), and nc_add_script(). Even when no other code is injected, at least the three helper JavaScript functions above will be present. This allows simple code like this to work:

nc_html(f"<a onclick=\"ncSendMessage('target', {{...}})\">Send Message</a>")

ns_use_bootstrap()

This function invokes both nc_add_link_rel() and nc_add_script_src() to include Bootstrap 5.13 CSS and JavaScript libraries for each subsequent use of nc_html().

Usage Examples

Okay, down to brass tacks. The streamlit_notification_center_component's primary purpose is to allow code in other custom components to send data back to streamlit in one shared location or to allow HTML in a call to st.components.v1.html to communicate back to the Python streamlit side of things. Currently layout is very limited in streamlit from an HTML perspective. Being able to write a quick layout using something like a table or set of inline-block divs is doable using the st.components.v1.html approach. However as soon as you have an action or other feature that needs to send data back to streamlit, you've moved into custom component territory.

Working with st.components.v1.html

Let's take the most common example first. If you wanted to introduce the following HTML with a stylized link that looks like a button

from notification_center import notification_center, nc_html, nc_add_style, nc_listen

notification_center(key="globalnc")

nc_listen('data', lambda data: print('data added', data))

nc_add_script(
  f'''
  function sendData() {{ ncSendMessage('data', {{msg: 'Stylized Button Pressed'}}) }}
  '''
)

nc_add_style(
  f'''
  a.stylized-button {{
    padding: 0.25em;
    margin: 0.1em;
    border: 1px solid slategrey;
    background-color: lightslategray;
    border-radius: 5px;
    color: lightyellow;
    cursor: pointer;
    display: inline-block;
    user-select: none;        
  }}
  '''
)

nc_html(f'<a class="stylized-button" onclick="sendData()">Button</a>')

What's going on here? Well, first off we are creating a new instance of the notification_center() component. The key supplied can be anything but will positively identify this NotificationCenter instance from another should there be one. If debug printing is enabled (see below) then you'll see "[NotificationCenter(key)][target] value" strings in the python logs. Note that key will be the key= value supplied when the notification_center() component is created, target will be the key the array of messages will be appended to and value will be the array of values for that target.

Then we are injecting into the page a block of HTML with some CSS and JavaScript. You'll note in this example that the ncSendMessage function from above is being used in the block. This is the easiest way to handle sending a postMessage() call to all frames in the parent of the iframe this code will reside in. The NotificationCenter component will be residing in another iframe with how streamlit works.

We now have the ability to inject custom HTML that can speak back to streamlit into your streamlit pages. Something that was not provided by default behavior.

Working with custom components

Other components should be able to import notification_center and use the nc- prefixed python functions for communicating with NotificationCenter data. As components send messages to the NotificationCenter, those values can be checked on the python side and can be worked with.

Debug messaging

If you're running into trouble, turn on debug messaging. This is a runtime variable that is set in

st.session_state.setdefault('_nc_debug', False)

To turn it on, simply change the value to True

st.session_state._nc_debug = True

Visual examples

Screenshot 2023-08-16 at 2 25 06 PM

After this point you'll see NotificationCenter messages appearing in your Python console.

FAQs


Did you know?

Socket

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Install

Related posts

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc