Source code for cardsGrid

"""Cards with title, subtitle and image displayed in rows and columns"""
# Author(s): Davide.De-Marchi@ec.europa.eu
# Copyright © European Union 2022-2023
# 
# Licensed under the EUPL, Version 1.2 or as soon they will be approved by 
# the European Commission subsequent versions of the EUPL (the "Licence");
# 
# You may not use this work except in compliance with the Licence.
# 
# You may obtain a copy of the Licence at:
# https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12

# Unless required by applicable law or agreed to in writing, software
# distributed under the Licence is distributed on an "AS IS"
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
# express or implied.
# 
# See the Licence for the specific language governing permissions and
# limitations under the Licence.
import ipyvuetify as v
import traitlets


[docs]class cardsGrid(v.VuetifyTemplate): """ Cards with title, subtitle and image displayed in a grid of rows and columns. Parameters ---------- cards : list of json element, one for each card to display, optional Each of the json elements must have this structure: { "title": "", "subtitle": "", "image": ""}, optional tags are "color", "imagesize", "icon" and "iconsize", "titletooltip" width : str, optional Width of the cards (default is '400px') height : str, optional Heigth of the cards (default is '' which means that each card has its own height defined by its content) cols : int, optional Horizontal column span [1,12] for each of the card (default is 6) color : str, optional Background color of the cards (default is 'white') dark : bool, optional If True the title and subtitle texts are displayed in white color, if False they are displayed in black (default is False) ripple : bool, optional If True the click on the card is highlighted (default is False) iconsize : str, optional Size of the area where the icon is displayed (default is '32px') imagesize : str, optional Size of the area where the image is displayed (default is '190px') on_click : function, optional Python function to call when the user clicks on the card. The function will receive as parameter the index of the clicked card. (default is None) responsive : bool, optional If True, the font size is automatically changed according to the page size (default is False) fontsizemultiplier : float, optional Multiply factor for changing the standard size of the font used for title and subtitle (default is 1.0) tooltipwidth : str, optional Max width of the tooltip window to open on hover on the cards title (default is '600px') Example ------- Creation of a cards grid to display text and an image:: from vois.vuetify import cardsGrid from ipywidgets import widgets from IPython.display import display output = widgets.Output() def on_click(index): with output: print(index) cards = [ { "title": "title0", "subtitle": "subtitle0", "image": "https://cdn.vuetifyjs.com/images/cards/sunshine.jpg", "titletooltip": "Example of card title tooltip" }, { "title": "title1", "subtitle": "subtitle1", "image": "https://cdn.vuetifyjs.com/images/cards/road.jpg" }, { "title": "title2", "subtitle": "subtitle2", "image": "https://cdn.vuetifyjs.com/images/cards/plane.jpg" }, { "title": "title3", "subtitle": "subtitle3", "image": "https://cdn.vuetifyjs.com/images/cards/house.jpg" } ] g = cardsGrid.cardsGrid(cards=cards, cols=6, width='350px', imagesize='200px', on_click=on_click) display(g) display(output) .. figure:: figures/cardsGrid.png :scale: 100 % :alt: cardsGrid widget Example of a cardsGrid to display multiple cards containing texts and an images """ cards = traitlets.Any([]).tag(sync=True) # Cards to display (array of json objects containing "title", "subtitle", "image" tags) width = traitlets.Unicode('400px').tag(sync=True) # Width of the cards height = traitlets.Unicode('').tag(sync=True) # Height of the cards color = traitlets.Unicode('white').tag(sync=True) # Background color cols = traitlets.Int(6).tag(sync=True) # Horizontal column span [1,12] for each of the card dark = traitlets.Bool(False).tag(sync=True) # Dark mode flag (if True the text is displayed in white) ripple = traitlets.Bool(False).tag(sync=True) # Ripple flag (if True the click on the card is highlighted) on_click = traitlets.Any(None).tag(sync=False) # Name of a python function to call when one of the cards is clicked (it will receive as argument the index of the clicked card) responsive = traitlets.Bool(False).tag(sync=True) fontsizemultiplier = traitlets.Float(1.0).tag(sync=True) tooltipwidth = traitlets.Unicode('600px').tag(sync=True) # Width of the tooltip @traitlets.default('template') def _template(self): return ''' <v-container fluid> <v-row dense> <v-col v-for="card,index in cards" :key="card.title" :cols="cols" > <v-card class="pa-0 ma-1" :width="width" :color="color" :dark="dark" :height="height" hover :ripple="ripple" link @click="clicked(index)" > <div class="d-flex flex-no-wrap justify-space-between"> <div> <v-row dense> <v-avatar class="pa-0 ma-0 ml-3 mt-2 mr-n2" :size="card.iconsize" tile > <v-img :src="card.icon"></v-img> </v-avatar> <div :style="card.color"> <v-tooltip v-if="card.titletooltip" bottom :max-width="tooltipwidth"> <template v-slot:activator="{ on, attrs }"> <v-card-title v-bind="attrs" v-on="on" class="mt-n2 mb-1" :style="fontSizeTitle" v-text="card.title"></v-card-title> </template> <span>{{card.titletooltip}}</span> </v-tooltip> <v-card-title v-else class="mt-n2 mb-1" :style="fontSizeTitle" v-text="card.title"></v-card-title> </div> </v-row> <div :style="card.color"> <div :class="card.margins" :style="fontSizeSubTitle" v-html="card.subtitle"/> </div> </div> <v-avatar class="ma-0" :size="card.imagesize" tile > <v-img :src="card.image" contain ></v-img> </v-avatar> </div> </v-card> </v-col> </v-row> </v-container> <script> export default { computed: { fontSizeTitle (num) { if( this.responsive ) { switch( this.$vuetify.breakpoint.name ) { case 'xs': return 'font-size: ' + (0.75*this.fontsizemultiplier).toFixed(3) + 'em;' case 'sm': return 'font-size: ' + (1.00*this.fontsizemultiplier).toFixed(3) + 'em;' case 'md': return 'font-size: ' + (1.25*this.fontsizemultiplier).toFixed(3) + 'em;' case 'lg': return 'font-size: ' + (1.45*this.fontsizemultiplier).toFixed(3) + 'em;' case 'xl': return 'font-size: ' + (1.60*this.fontsizemultiplier).toFixed(3) + 'em;' } } else { return 'font-size: ' + (1.25*this.fontsizemultiplier).toFixed(3) + 'em;' } }, fontSizeSubTitle () { if( this.responsive ) { switch( this.$vuetify.breakpoint.name ) { case 'xs': return 'font-size: ' + (0.8*this.fontsizemultiplier).toFixed(3) + 'em;' case 'sm': return 'font-size: ' + (0.9*this.fontsizemultiplier).toFixed(3) + 'em;' case 'md': return 'font-size: ' + (1.0*this.fontsizemultiplier).toFixed(3) + 'em;' case 'lg': return 'font-size: ' + (1.1*this.fontsizemultiplier).toFixed(3) + 'em;' case 'xl': return 'font-size: ' + (1.2*this.fontsizemultiplier).toFixed(3) + 'em;' } } else { return 'font-size: ' + (1.0*this.fontsizemultiplier).toFixed(3) + 'em;' } }, }, } </script> ''' def __init__(self, *args, cards=[], width='400px', height='', color='white', cols=6, dark=False, ripple=False, iconsize='32px', imagesize='190px', on_click=None, responsive=False, fontsizemultiplier=1.0, tooltipwidth='600px', **kwargs): for c in cards: if not "color" in c: if dark: c["color"] = "color: white;" else: c["color"] = "color: black;" else: c["color"] = "color: %s;" % c["color"] if not "iconsize" in c: c["iconsize"] = iconsize if not "icon" in c: c["icon"] = "" c["iconsize"] = '0px' if not "imagesize" in c: c["imagesize"] = imagesize if not "subtitle" in c or len(c["subtitle"]) == 0: c["margins"] = "ma-0 ml-4 mb-n3" else: c["margins"] = "ma-0 ml-4 mb-3" self.cards = cards self.width = width self.height = height self.color = color self.cols = cols self.dark = dark self.ripple = ripple self.on_click = on_click self.responsive = responsive self.fontsizemultiplier = fontsizemultiplier self.tooltipwidth = tooltipwidth super().__init__(*args, **kwargs) # Manage "click" event def vue_clicked(self, data): if not self.on_click is None: self.on_click(data)