Skip to content

Single-File Components powered app

License

Unknown, MIT licenses found

Licenses found

Unknown
LICENSE
MIT
LICENSE.md
Notifications You must be signed in to change notification settings

RinteRface/shinyComponent

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

33 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

shinyComponent

Lifecycle: experimental R-CMD-check

App powered by {golem}, webpack, Framework7 components (esm).

Installation

You can install the released version of shinyComponent from CRAN with:

remotes::install_github("RinteRface/shinyComponent")

Proof of Concept

Interface is built with Framework 7 from JS, powered by webpack. We leverage Template components to create the app root and subroutes. On the server side, we process data from R and send them back to JS to update the components.

UI

Components may be written with JSX (supported by the framework7-loader, no need to install React!), which is more convenient than the classic template syntax. Components have either the old/new syntax so you can compare both approaches.

Main page

App component is driven by:

import ListItem from './custom-list.f7.jsx';

export default (props, {$f7, $f7ready, $on, $update }) => {
  const title = 'Hello World';
  let names = ['John', 'Vladimir', 'Timo'];

  Shiny.addCustomMessageHandler('init', function(message) {
    names = message;
    $update();
  });

  // App events callback
  $on('click', () => {
    // callback
  });

  // This method need to be used only when you use Main App Component
  // to make sure to call Framework7 APIs when app initialized.
  $f7ready(() => {
    // do stuff
    console.log('Hello');
  });

  const openAlert = () => {
    $f7.dialog.alert(title, function() {
      // ok button callback
      Shiny.setInputValue('alert_opened', false);
    });
    Shiny.setInputValue('alert_opened', true);
    Shiny.setInputValue(
      'alert',
      {
        message: 'Alert dialog was triggered!',
        title: title,
      },
      {priority: 'event'}
    );
  }

  const openPanel = () => {
    $f7.panel.open('.panel-left');
  }

  return () => (
    <div id="app">
      <div class="panel panel-left panel-init">
        <div class="block">"Hello"</div>
      </div>
      <div class="view view-main view-init safe-areas">
        <div class="page">
          <div class="navbar">
            <div class="navbar-bg"></div>
            <div class="navbar-inner">
              <div class="title">{title}</div>
            </div>
          </div>
          <div class="toolbar toolbar-bottom">
            <div class="toolbar-inner">
              <a onClick={() => openAlert()}>Open Alert</a>
              <a class="button button-fill" href="#" class="panel-open" data-panel=".panel-left">Left Panel</a>
              <a href="/extra/" data-transition="f7-cover">New Page</a>
            </div>
          </div>
          <div class="page-content">
            <ul>
              <ListItem title="Item 1" />
              <ListItem title="Item 2" />
              <ListItem title="Item 3" />
            </ul>
            <div class="list simple-list">
              <ul>
                {names.map((name) =>
                  <li>{name}</li>
                )}
              </ul>
            </div>
          </div>
        </div>
      </div>
    </div>
  )
}

Below is the code a of local sub-component, being invoked in the main App template:

const ListItem = (props) => {
  return () => <li>{props.title}</li>;
}

export default ListItem

Router

This app requires a router to navigate between pages. This is done with the Framework7 builtin feature. You may pass entire app components to the router, as shown below.

import Extra from '../components/extra.f7.jsx';

export default [
  //{
  //  path: '/'
  //},
  {
    path: '/extra/',
    asyncComponent: () => Extra
  }
]

App init

Importantly, we only import Framework7 modules we need, to lighten the final bundle:

import Dialog from 'framework7/esm/components/dialog/dialog.js';
import Gauge from 'framework7/esm/components/gauge/gauge.js';
import Panel from 'framework7/esm/components/panel/panel.js';
import View from 'framework7/esm/components/view/view.js';
Framework7.use([Dialog, Gauge, Panel, View]);

App UI is initialized passing the main app component, the routes and targeting the #app element, located within the app_ui() function:

app_ui <- function(request) {
  tagList(
    # Leave this function for adding external resources
    golem_add_external_resources(),
    # Your application UI logic
    tags$body(
      div(id = "app"),
      tags$script(src = "www/index.js")
    )
  )
}

Since the JS assets have to go after the #app element in the body tag, we had to comment out the {golem} predefined script:

tags$head(
  favicon(),
  #bundle_resources(
  #  path = app_sys('app/www'),
  #  app_title = 'shinyFramework7'
  #)
  # Add here other external resources
  # for example, you can add shinyalert::useShinyalert()
  )

Whole index.js code:

import 'shiny';
// Import Framework7
import Framework7 from 'framework7';
// Import Framework7 Styles
import 'framework7/framework7-bundle.min.css';

// Install F7 Components using .use() method on class:
import Dialog from 'framework7/esm/components/dialog/dialog.js';
import Gauge from 'framework7/esm/components/gauge/gauge.js';
import Panel from 'framework7/esm/components/panel/panel.js';
import View from 'framework7/esm/components/view/view.js';
Framework7.use([Dialog, Gauge, Panel, View]);

// Import App component
import App from './components/app.f7.jsx';

// Import other routes
import routes from './modules/routes.js';

// Initialize app
var app = new Framework7({
  el: '#app',
  theme: 'ios',
  // specify main app component
  routes: routes,
  component: App
});

Server

On the server side (R):

observeEvent(TRUE, {
  session$sendCustomMessage("init", colnames(mtcars))
})

observeEvent(input$alert, {
  message(sprintf("Received from JS: %s", input$alert$message))
  message(sprintf("App title is %s", input$alert$title))
})

observe({print(input$alert_opened)})

Example

This is a basic example which shows you how to solve a common problem:

Run app

library(shinyComponent)
## basic example code
run_app()

Dev mode

library(shinyComponent)
## basic example code
packer::bundle_dev()
devtools::load_all()
run_app()

About

Single-File Components powered app

Topics

Resources

License

Unknown, MIT licenses found

Licenses found

Unknown
LICENSE
MIT
LICENSE.md

Code of conduct

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published