瀏覽代碼

feat(tailwind): add tailwind config, update general pages using tiles

Christhoval Barba 1 年之前
父節點
當前提交
6588508ea8
共有 3 個文件被更改,包括 519 次插入45 次删除
  1. 1
    1
      Makefile
  2. 296
    44
      gitstats
  3. 222
    0
      tailwind.json

+ 1
- 1
Makefile 查看文件

@@ -1,7 +1,7 @@
1 1
 PREFIX=/usr/local
2 2
 BINDIR=$(PREFIX)/bin
3 3
 RESOURCEDIR=$(PREFIX)/share/gitstats
4
-RESOURCES=gitstats.css sortable.js *.gif
4
+RESOURCES=gitstats.css sortable.js *.gif tailwind.json
5 5
 BINARIES=gitstats
6 6
 VERSION=$(shell git describe 2>/dev/null || git rev-parse --short HEAD 2>/dev/null || date +%Y-%m-%d)
7 7
 SEDVERSION=perl -pi -e 's/VERSION = 0/VERSION = "$(VERSION)"/' --

+ 296
- 44
gitstats 查看文件

@@ -853,7 +853,7 @@ class HTMLReportCreator(ReportCreator):
853 853
 		binarypath = os.path.dirname(os.path.abspath(__file__))
854 854
 		secondarypath = os.path.join(binarypath, '..', 'share', 'gitstats')
855 855
 		basedirs = [binarypath, secondarypath, '/usr/share/gitstats']
856
-		for file in (conf['style'], 'sortable.js', 'arrow-up.gif', 'arrow-down.gif', 'arrow-none.gif'):
856
+		for file in (conf['style'], 'sortable.js', 'arrow-up.gif', 'arrow-down.gif', 'arrow-none.gif', 'tailwind.json'):
857 857
 			for base in basedirs:
858 858
 				src = base + '/' + file
859 859
 				if os.path.exists(src):
@@ -866,21 +866,47 @@ class HTMLReportCreator(ReportCreator):
866 866
 		# General
867 867
 		format = '%Y-%m-%d %H:%M:%S'
868 868
 
869
-		general_content = ['<h1 class="text-3xl font-bold underline">GitStats - %s</h1>' % data.projectname]
869
+		# general_content = ['<h1 class="text-3xl font-bold underline">GitStats - %s</h1>' % data.projectname]
870
+
871
+		general_content = []
872
+
873
+
874
+		tiles = '\n'.join([
875
+			self.tilesItemStat(title='Project name', info=data.projectname),
876
+			self.tilesItemStat(title='Generated', info=datetime.datetime.now().strftime(format)),
877
+			self.tilesItemStat(title='Report Period', info=f'{data.getFirstCommitDate().strftime(format)} to {data.getLastCommitDate().strftime(format)}'),
878
+		])
879
+
880
+		cards = '\n'.join([
881
+			self.cardItemStat(title='Branches', count=data.total_branches), 
882
+			self.cardItemStat(title='Tags', count=data.total_tags), 
883
+			self.cardItemStat(title='Age', count=f'{data.getCommitDeltaDays():.1f} days'), 
884
+			self.cardItemStat(title='Active days', count=f'{len(data.getActiveDays())}', stat=f'{(100.0 * len(data.getActiveDays()) / data.getCommitDeltaDays()):3.2f}%', arrow='up'), 
885
+			self.cardItemStat(title='Total files', count=data.getTotalFiles()), 
886
+			self.cardItemStat(title='Total LOC', count=data.getTotalLOC()),
887
+			self.cardItemStat(title='Total lines added', count=data.total_lines_added, stat=f'{((data.total_lines_added/data.getTotalLOC())*100):.2f}%', arrow='up'),
888
+			self.cardItemStat(title='Total lines removed', count=data.total_lines_removed, stat=f'{((data.total_lines_removed/data.getTotalLOC())*100):.2f}%', arrow='up'), 
889
+			self.cardItemStat(title='Total commits', count=data.getTotalCommits(), stat=f'{(float(data.getTotalCommits()) / len(data.getActiveDays())):.1f}', arrow='up'),
890
+			self.cardItemStat(title='Authors', count=data.getTotalAuthors(), stat=f'{((1.0 * data.getTotalCommits()) / data.getTotalAuthors()):.1f}', arrow='up'),
891
+			])
892
+		
893
+		general_content.append(f'<div class="grid grid-cols-1 lg:grid-cols-2 gap-6 mb-6">{tiles}</div>')
894
+
895
+		general_content.append(f'<div class="grid grid-cols-1 gap-4 md:grid-cols-2 md:gap-6 xl:grid-cols-4 2xl:gap-7.5">{cards}</div>')
870 896
 
871
-		general_content.append('<div><div class="card-body"><dl>')
872
-		general_content.append('<dt>Project name</dt><dd>%s (%s branches, %s tags)</dd>' % (data.projectname, data.total_branches, data.total_tags))
873
-		general_content.append('<dt>Generated</dt><dd>%s (in %d seconds)</dd>' % (datetime.datetime.now().strftime(format), time.time() - data.getStampCreated()))
874
-		general_content.append('<dt>Generator</dt><dd><a href="http://gitstats.sourceforge.net/">GitStats</a> (version %s), %s, %s</dd>' % (getversion(), getgitversion(), getgnuplotversion()))
875
-		general_content.append('<dt>Report Period</dt><dd>%s to %s</dd>' % (data.getFirstCommitDate().strftime(format), data.getLastCommitDate().strftime(format)))
876
-		general_content.append('<dt>Age</dt><dd>%d days, %d active days (%3.2f%%)</dd>' % (data.getCommitDeltaDays(), len(data.getActiveDays()), (100.0 * len(data.getActiveDays()) / data.getCommitDeltaDays())))
877
-		general_content.append('<dt>Total Files</dt><dd>%s</dd>' % data.getTotalFiles())
878
-		general_content.append('<dt>Total Lines of Code</dt><dd>%s (%d added, %d removed)</dd>' % (data.getTotalLOC(), data.total_lines_added, data.total_lines_removed))
879
-		general_content.append('<dt>Total Commits</dt><dd>%s (average %.1f commits per active day, %.1f per all days)</dd>' % (data.getTotalCommits(), float(data.getTotalCommits()) / len(data.getActiveDays()), float(data.getTotalCommits()) / data.getCommitDeltaDays()))
880
-		general_content.append('<dt>Authors</dt><dd>%s (average %.1f commits per author)</dd>' % (data.getTotalAuthors(), (1.0 * data.getTotalCommits()) / data.getTotalAuthors()))
881
-		general_content.append('</dl></div></div>')
897
+		# general_content.append('<div><div class="card-body"><dl>')
898
+		# general_content.append('<dt>Project name</dt><dd>%s (%s branches, %s tags)</dd>' % (data.projectname, data.total_branches, data.total_tags))
899
+		# general_content.append('<dt>Generated</dt><dd>%s (in %d seconds)</dd>' % (datetime.datetime.now().strftime(format), time.time() - data.getStampCreated()))
900
+		# general_content.append('<dt>Generator</dt><dd><a href="http://gitstats.sourceforge.net/">GitStats</a> (version %s), %s, %s</dd>' % (getversion(), getgitversion(), getgnuplotversion()))
901
+		# general_content.append('<dt>Report Period</dt><dd>%s to %s</dd>' % (data.getFirstCommitDate().strftime(format), data.getLastCommitDate().strftime(format)))
902
+		# general_content.append('<dt>Age</dt><dd>%d days, %d active days (%3.2f%%)</dd>' % (data.getCommitDeltaDays(), len(data.getActiveDays()), (100.0 * len(data.getActiveDays()) / data.getCommitDeltaDays())))
903
+		# general_content.append('<dt>Total Files</dt><dd>%s</dd>' % data.getTotalFiles())
904
+		# general_content.append('<dt>Total Lines of Code</dt><dd>%s (%d added, %d removed)</dd>' % (data.getTotalLOC(), data.total_lines_added, data.total_lines_removed))
905
+		# general_content.append('<dt>Total Commits</dt><dd>%s (average %.1f commits per active day, %.1f per all days)</dd>' % (data.getTotalCommits(), float(data.getTotalCommits()) / len(data.getActiveDays()), float(data.getTotalCommits()) / data.getCommitDeltaDays()))
906
+		# general_content.append('<dt>Authors</dt><dd>%s (average %.1f commits per author)</dd>' % (data.getTotalAuthors(), (1.0 * data.getTotalCommits()) / data.getTotalAuthors()))
907
+		# general_content.append('</dl></div></div>')
882 908
 
883
-		self.printPage(general_content, f'{path}/index.html', "FRONT'S STATS")
909
+		self.printPage(general_content, f'{path}/index.html', f"{data.projectname}'S STATS")
884 910
 
885 911
 		###
886 912
 		# activity.html
@@ -1404,7 +1430,7 @@ class HTMLReportCreator(ReportCreator):
1404 1430
 			lines_content.append('<tr><th>%s</th><td style="background-color: rgb(%d, 0, 0)">%d</td></tr>' % (i, r, commits))
1405 1431
 		lines_content.append('</tr></table>')
1406 1432
 
1407
-		self.printPage(lines_content, f'{path}/tags.html', title='Lines')
1433
+		self.printPage(lines_content, f'{path}/lines.html', title='Lines')
1408 1434
 
1409 1435
 		###
1410 1436
 		# tags.html
@@ -1746,7 +1772,8 @@ plot """
1746 1772
 
1747 1773
 	def getHeader(self, title = None):
1748 1774
 		title = title or f'GitStats - {self.title}'
1749
-		return """
1775
+		config = json.load(open('tailwind.json'))
1776
+		return '''
1750 1777
 <head>
1751 1778
 	<meta charset="UTF-8">
1752 1779
 	<meta http-equiv="X-UA-Compatible" content="IE=edge">
@@ -1755,8 +1782,9 @@ plot """
1755 1782
 	<link rel="stylesheet" href="%s" type="text/css" />
1756 1783
 	<meta name="generator" content="GitStats %s" />
1757 1784
 	<script src="https://cdn.tailwindcss.com"></script>
1785
+	<script> tailwind.config = %s </script>
1758 1786
 </head>
1759
-""" % (title, conf['style'], getversion())
1787
+''' % (title, conf['style'], getversion(), json.dumps(config))
1760 1788
 	
1761 1789
 
1762 1790
 	def getBody(self, content, title):
@@ -1775,37 +1803,131 @@ plot """
1775 1803
 	<!-- ===== Preloader End ===== -->
1776 1804
 
1777 1805
 	<!-- ===== Page Wrapper Start ===== -->
1778
-	<div class="flex h-screen overflow-hidden antialiased text-gray-900 bg-gray-100 ">
1806
+	<div class="flex h-screen overflow-hidden">
1779 1807
 		{sidebar}
1780 1808
 
1781 1809
 		<!-- ===== Content Area Start ===== -->
1782 1810
 		<div class="relative flex flex-1 flex-col overflow-y-auto overflow-x-hidden">
1783 1811
 			{topBar}
1784 1812
 			<!-- Main content -->
1785
-			<main class="p-2">{content}</main>
1813
+			<main>
1814
+				<div class="mx-auto max-w-screen-2xl p-4 md:p-6 2xl:p-10">
1815
+					{content}
1816
+				</div>
1817
+			</main>
1786 1818
 		</div>
1787 1819
 		<!-- ===== Content Area End ===== -->
1788 1820
 
1789 1821
 	</div>
1790 1822
 	<!-- ===== Page Wrapper End ===== -->
1791 1823
 
1792
-	<script src="//unpkg.com/alpinejs" defer></script>
1824
+	<!-- <script src="//unpkg.com/alpinejs" defer></script> -->
1825
+	<!-- Alpine Plugins -->
1826
+	<script defer src="https://cdn.jsdelivr.net/npm/@alpinejs/persist@3.13.0/dist/cdn.min.js"></script>
1827
+	
1828
+	<!-- Alpine Core -->
1829
+	<script defer src="https://cdn.jsdelivr.net/npm/alpinejs@3.13.0/dist/cdn.min.js"></script>
1793 1830
 	<script type="text/javascript" src="sortable.js"></script>
1794 1831
 </body>
1795 1832
 '''
1833
+	def tilesItemStat(self, title='', info='', icon=None, stat=None):
1834
+		if icon is not None:
1835
+			icon = f'''
1836
+<div class="flex items-center justify-center mb-6 md:mb-0">
1837
+	<span class="inline-flex justify-center items-center w-12 h-12 rounded-full text-emerald-500 bg-gray-50 dark:bg-slate-800 md:mr-6">
1838
+		<svg viewBox="0 0 24 24" width="24" height="24" class="inline-block">
1839
+			<path fill="currentColor" d="M15 15V17H18V20H20V17H23V15H20V12H18V15M14.97 11.61C14.85 10.28 13.59 8.97 12 9C10.3 9.03 9 10.3 9 12C9 13.7 10.3 14.94 12 15C12.38 15 12.77 14.92 13.14 14.77C13.41 13.67 13.86 12.63 14.97 11.61M13 16H7C7 14.9 6.11 14 5 14V10C6.11 10 7 9.11 7 8H17C17 9.11 17.9 10 19 10V10.06C19.67 10.06 20.34 10.18 21 10.4V6H3V18H13.32C13.1 17.33 13 16.66 13 16Z"></path>
1840
+		</svg>
1841
+	</span>
1842
+</div>'''
1843
+			
1844
+		if stat is not None:
1845
+			stat = '''
1846
+<div class="text-center md:text-right space-y-2">
1847
+	<p class="text-sm text-gray-500">Home Loan Account</p>
1848
+		<div>
1849
+			<div class="inline-flex items-center capitalize leading-none text-xs border rounded-full py-1 px-3 bg-emerald-500 border-emerald-500 text-white">
1850
+			<span>deposit</span>
1851
+		</div>
1852
+	</div>
1853
+</div>'''
1854
+
1855
+		return f'''
1856
+<div class="rounded-sm border border-stroke bg-white px-7.5 py-6 shadow-default dark:border-strokedark dark:bg-boxdark">
1857
+	<div class="flex flex-col">
1858
+		<div class="flex-1 p-6">
1859
+			<div class="justify-between items-center block md:flex">
1860
+				<div class="flex items-center justify-center mb-6 md:mb-0">
1861
+					<div class="justify-start items-center block md:flex">
1862
+						{icon or ''}
1863
+						<div class="flex items-center justify-center">
1864
+							<div class="text-center text-black dark:text-white space-y-1 md:text-left md:mr-6">
1865
+								<h4 class="text-xl">{info}</h4>
1866
+								<p>{title}</p>
1867
+							</div>
1868
+						</div>
1869
+					</div>
1870
+				</div>
1871
+				<div class="flex items-center justify-center">
1872
+					{stat or ''}
1873
+				</div>
1874
+			</div>
1875
+		</div>
1876
+	</div>
1877
+</div>'''
1878
+
1879
+	def cardItemStat(self, count='$3.456K', title='Total views', stat = None, arrow= 'up', icon = None):
1880
+
1881
+		stat_html = ''
1882
+		if stat is not None:
1883
+			stat_html = f'''
1884
+<span class="flex items-center gap-1 text-sm font-medium text-meta-3">
1885
+	{stat}
1886
+	<svg class="fill-meta-3" width="10" height="11" viewBox="0 0 10 11" fill="none" xmlns="http://www.w3.org/2000/svg">
1887
+		{arrow is 'up' and '<path d="M4.35716 2.47737L0.908974 5.82987L5.0443e-07 4.94612L5 0.0848689L10 4.94612L9.09103 5.82987L5.64284 2.47737L5.64284 10.0849L4.35716 10.0849L4.35716 2.47737Z" fill=""></path>'}
1888
+		{arrow is 'down'  and '<path d="M5.64284 7.69237L9.09102 4.33987L10 5.22362L5 10.0849L-8.98488e-07 5.22362L0.908973 4.33987L4.35716 7.69237L4.35716 0.0848701L5.64284 0.0848704L5.64284 7.69237Z" fill=""></path>'}
1889
+    </svg>
1890
+</span>'''
1891
+			
1892
+		if icon is None:
1893
+			icon = '''
1894
+<svg class="fill-primary dark:fill-white" width="22" height="16" viewBox="0 0 22 16" fill="none" xmlns="http://www.w3.org/2000/svg">
1895
+    <path d="M11 15.1156C4.19376 15.1156 0.825012 8.61876 0.687512 8.34376C0.584387 8.13751 0.584387 7.86251 0.687512 7.65626C0.825012 7.38126 4.19376 0.918762 11 0.918762C17.8063 0.918762 21.175 7.38126 21.3125 7.65626C21.4156 7.86251 21.4156 8.13751 21.3125 8.34376C21.175 8.61876 17.8063 15.1156 11 15.1156ZM2.26876 8.00001C3.02501 9.27189 5.98126 13.5688 11 13.5688C16.0188 13.5688 18.975 9.27189 19.7313 8.00001C18.975 6.72814 16.0188 2.43126 11 2.43126C5.98126 2.43126 3.02501 6.72814 2.26876 8.00001Z" fill=""></path>
1896
+    <path d="M11 10.9219C9.38438 10.9219 8.07812 9.61562 8.07812 8C8.07812 6.38438 9.38438 5.07812 11 5.07812C12.6156 5.07812 13.9219 6.38438 13.9219 8C13.9219 9.61562 12.6156 10.9219 11 10.9219ZM11 6.625C10.2437 6.625 9.625 7.24375 9.625 8C9.625 8.75625 10.2437 9.375 11 9.375C11.7563 9.375 12.375 8.75625 12.375 8C12.375 7.24375 11.7563 6.625 11 6.625Z" fill=""></path>
1897
+</svg>'''
1898
+
1899
+		return f'''
1900
+<div class="rounded-sm border border-stroke bg-white px-7.5 py-6 shadow-default dark:border-strokedark dark:bg-boxdark">
1901
+	<div class="flex h-11.5 w-11.5 items-center justify-center rounded-full bg-meta-2 dark:bg-meta-4">
1902
+		{icon}
1903
+	</div>
1904
+
1905
+    <div class="mt-4 flex items-end justify-between">
1906
+		<div>
1907
+			<h4 class="text-title-md font-bold text-black dark:text-white">{count}</h4>
1908
+            <span class="text-sm font-medium">{title}</span>
1909
+        </div>
1910
+
1911
+        {stat_html}
1912
+	</div>
1913
+</div>'''
1796 1914
 	
1797 1915
 	def getSideBar(self):
1798 1916
 		menu = ''.join([f'''
1799
-<a href="{href}"
1800
-	class="flex items-center p-2 text-gray-500 transition-colors rounded-md dark:text-light hover:bg-primary-100 dark:hover:bg-primary"
1801
-	role="button">
1802
-	<span aria-hidden="true">
1803
-		<svg class="w-5 h-5" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke="currentColor">
1804
-			<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M3 12l2-2m0 0l7-7 7 7M5 10v10a1 1 0 001 1h3m10-11l2 2m-2-2v10a1 1 0 01-1 1h-3m-6 0a1 1 0 001-1v-4a1 1 0 011-1h2a1 1 0 011 1v4a1 1 0 001 1m-6 0h6" />
1805
-		</svg>
1806
-	</span>
1807
-	<span class="ml-2 text-sm">{label}</span>
1808
-</a>
1917
+<li>
1918
+	<a href="{href}"
1919
+		@click="selected = (selected === '{label}' ? '':'{label}')"
1920
+		:class="{{ 'bg-graydark dark:bg-meta-4': (selected === '{label}') }}"
1921
+		class="group relative flex items-center gap-2.5 rounded-sm px-4 py-2 font-medium text-bodydark1 duration-300 ease-in-out hover:bg-graydark dark:hover:bg-meta-4"
1922
+		role="button">
1923
+		<span aria-hidden="true">
1924
+			<svg class="w-5 h-5" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke="currentColor">
1925
+				<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M3 12l2-2m0 0l7-7 7 7M5 10v10a1 1 0 001 1h3m10-11l2 2m-2-2v10a1 1 0 01-1 1h-3m-6 0a1 1 0 001-1v-4a1 1 0 011-1h2a1 1 0 011 1v4a1 1 0 001 1m-6 0h6" />
1926
+			</svg>
1927
+		</span>
1928
+		<span class="ml-2 text-sm">{label}</span>
1929
+	</a>
1930
+</li>
1809 1931
 ''' for (label, href) in [
1810 1932
 			("General", "index.html"),
1811 1933
 			("Activity", "activity.html"),
@@ -1817,18 +1939,49 @@ plot """
1817 1939
 
1818 1940
 		return f'''
1819 1941
 <!-- Sidebar -->
1820
-<aside class="flex-shrink-0 hidden w-64 bg-white border-r dark:border-primary-darker dark:bg-darker md:block">
1821
-	<div class="flex flex-col h-full">
1942
+<aside :class="sidebarToggle ? 'translate-x-0' : '-translate-x-full'"
1943
+	@click.outside="sidebarToggle = false"
1944
+	class="absolute left-0 top-0 z-9999 flex h-screen w-72.5 flex-col overflow-y-hidden bg-black duration-300 ease-linear dark:bg-boxdark lg:static lg:translate-x-0 -translate-x-full">
1945
+
1946
+	<!-- SIDEBAR HEADER -->
1947
+	<div class="flex items-center justify-between gap-2 px-6 py-5.5 lg:py-6.5">
1948
+		<a href="index.html">
1949
+		<img src="https://demo.tailadmin.com/src/images/logo/logo.svg" alt="Logo" />
1950
+		</a>
1951
+
1952
+		<button
1953
+		class="block lg:hidden"
1954
+		@click.stop="sidebarToggle = !sidebarToggle"
1955
+		>
1956
+		<svg
1957
+			class="fill-current"
1958
+			width="20"
1959
+			height="18"
1960
+			viewBox="0 0 20 18"
1961
+			fill="none"
1962
+			xmlns="http://www.w3.org/2000/svg"
1963
+		>
1964
+			<path
1965
+			d="M19 8.175H2.98748L9.36248 1.6875C9.69998 1.35 9.69998 0.825 9.36248 0.4875C9.02498 0.15 8.49998 0.15 8.16248 0.4875L0.399976 8.3625C0.0624756 8.7 0.0624756 9.225 0.399976 9.5625L8.16248 17.4375C8.31248 17.5875 8.53748 17.7 8.76248 17.7C8.98748 17.7 9.17498 17.625 9.36248 17.475C9.69998 17.1375 9.69998 16.6125 9.36248 16.275L3.02498 9.8625H19C19.45 9.8625 19.825 9.4875 19.825 9.0375C19.825 8.55 19.45 8.175 19 8.175Z"
1966
+			fill=""
1967
+			/>
1968
+		</svg>
1969
+		</button>
1970
+	</div>
1971
+	<!-- SIDEBAR HEADER -->
1972
+
1973
+	<div class="no-scrollbar flex flex-col overflow-y-auto duration-300 ease-linear">
1822 1974
 		  <!-- Sidebar links -->
1823
-		  <nav aria-label="Main" class="flex-1 px-2 py-4 space-y-2 overflow-y-hidden hover:overflow-y-auto">
1824
-		  {menu}
1975
+		  <nav aria-label="Main" class="mt-5 px-4 py-4 lg:mt-9 lg:px-6" x-data="{{selected: $persist('General')}}">
1976
+			<div>
1977
+				<h3 class="mb-4 ml-4 text-sm font-medium text-bodydark2">MENU</h3>
1978
+				<ul class="mb-6 flex flex-col gap-1.5">
1979
+				{menu}
1980
+				</ul>
1981
+			</div>
1825 1982
 		  </nav>
1826 1983
 		  <!-- Sidebar footer -->
1827
-		  <div class="flex-shrink-0 px-2 py-4 space-y-2">
1828
-			<button type="button" class="flex items-center justify-center w-full px-4 py-2 text-sm text-white rounded-md bg-blue hover:bg-primary-dark focus:outline-none focus:ring focus:ring-primary-dark focus:ring-offset-1 focus:ring-offset-white dark:focus:ring-offset-dark">
1829
-				<span>Customize</span>
1830
-			</button>
1831
-		  </div>
1984
+		  <div class="flex-shrink-0 px-2 py-4 space-y-2"></div>
1832 1985
 	</div>
1833 1986
 </aside>
1834 1987
 '''
@@ -1836,11 +1989,107 @@ plot """
1836 1989
 	def getTopBar(self, title):
1837 1990
 		return f'''
1838 1991
 <!-- Navbar -->
1839
-<header class="sticky top-0 z-999 flex w-full bg-white drop-shadow-1">
1992
+<header class="sticky top-0 z-999 flex w-full bg-white drop-shadow-1 dark:bg-boxdark dark:drop-shadow-none">
1840 1993
 	<div class="flex flex-grow items-center justify-between px-4 py-4 shadow-2 md:px-6 2xl:px-11">
1841
-		<a href="#" class="inline-block text-2xl font-bold tracking-wider uppercase text-primary-dark">{title}</a>
1994
+		<div class="flex items-center gap-2 sm:gap-4 lg:hidden">
1995
+			<!-- Hamburger Toggle BTN -->
1996
+			<button
1997
+				class="z-99999 block rounded-sm border border-stroke bg-white p-1.5 shadow-sm dark:border-strokedark dark:bg-boxdark lg:hidden"
1998
+				@click.stop="sidebarToggle = !sidebarToggle">
1999
+				<span class="relative block h-5.5 w-5.5 cursor-pointer">
2000
+				<span class="du-block absolute right-0 h-full w-full">
2001
+					<span
2002
+					class="relative left-0 top-0 my-1 block h-0.5 w-0 rounded-sm bg-black delay-[0] duration-200 ease-in-out dark:bg-white"
2003
+					:class="{{ '!w-full delay-300': !sidebarToggle }}"
2004
+					></span>
2005
+					<span
2006
+					class="relative left-0 top-0 my-1 block h-0.5 w-0 rounded-sm bg-black delay-150 duration-200 ease-in-out dark:bg-white"
2007
+					:class="{{ '!w-full delay-400': !sidebarToggle }}"
2008
+					></span>
2009
+					<span
2010
+					class="relative left-0 top-0 my-1 block h-0.5 w-0 rounded-sm bg-black delay-200 duration-200 ease-in-out dark:bg-white"
2011
+					:class="{{ '!w-full delay-500': !sidebarToggle }}"
2012
+					></span>
2013
+				</span>
2014
+				<span class="du-block absolute right-0 h-full w-full rotate-45">
2015
+					<span
2016
+					class="absolute left-2.5 top-0 block h-full w-0.5 rounded-sm bg-black delay-300 duration-200 ease-in-out dark:bg-white"
2017
+					:class="{{ '!h-0 delay-[0]': !sidebarToggle }}"
2018
+					></span>
2019
+					<span
2020
+					class="delay-400 absolute left-0 top-2.5 block h-0.5 w-full rounded-sm bg-black duration-200 ease-in-out dark:bg-white"
2021
+					:class="{{ '!h-0 dealy-200': !sidebarToggle }}"
2022
+					></span>
2023
+				</span>
2024
+				</span>
2025
+			</button>
2026
+			<!-- Hamburger Toggle BTN -->
2027
+			<a class="block flex-shrink-0 lg:hidden" href="index.html">
2028
+				<img src="https://demo.tailadmin.com/src/images/logo/logo-icon.svg" alt="Logo" />
2029
+			</a>
2030
+		</div>
2031
+		
2032
+		<div class="hidden sm:block">
2033
+			<a href="#" class="inline-block text-2xl font-bold tracking-wider uppercase">{title}</a>
2034
+		</div>
1842 2035
 		
1843 2036
 		<div class="flex items-center gap-3 2xsm:gap-7">
2037
+		<ul class="flex items-center gap-2 2xsm:gap-4">
2038
+			<li>
2039
+				<!-- Dark Mode Toggler -->
2040
+				<label
2041
+					:class="darkMode ? 'bg-primary' : 'bg-stroke'"
2042
+					class="relative m-0 block h-7.5 w-14 rounded-full"
2043
+				>
2044
+					<input
2045
+					type="checkbox"
2046
+					:value="darkMode"
2047
+					@change="darkMode = !darkMode"
2048
+					class="absolute top-0 z-50 m-0 h-full w-full cursor-pointer opacity-0"
2049
+					/>
2050
+					<span
2051
+					:class="darkMode && '!right-1 !translate-x-full'"
2052
+					class="absolute left-1 top-1/2 flex h-6 w-6 -translate-y-1/2 translate-x-0 items-center justify-center rounded-full bg-white shadow-switcher duration-75 ease-linear"
2053
+					>
2054
+					<span class="dark:hidden">
2055
+						<svg
2056
+						width="16"
2057
+						height="16"
2058
+						viewBox="0 0 16 16"
2059
+						fill="none"
2060
+						xmlns="http://www.w3.org/2000/svg"
2061
+						>
2062
+						<path
2063
+							d="M7.99992 12.6666C10.5772 12.6666 12.6666 10.5772 12.6666 7.99992C12.6666 5.42259 10.5772 3.33325 7.99992 3.33325C5.42259 3.33325 3.33325 5.42259 3.33325 7.99992C3.33325 10.5772 5.42259 12.6666 7.99992 12.6666Z"
2064
+							fill="#969AA1"
2065
+						/>
2066
+						<path
2067
+							d="M8.00008 15.3067C7.63341 15.3067 7.33342 15.0334 7.33342 14.6667V14.6134C7.33342 14.2467 7.63341 13.9467 8.00008 13.9467C8.36675 13.9467 8.66675 14.2467 8.66675 14.6134C8.66675 14.9801 8.36675 15.3067 8.00008 15.3067ZM12.7601 13.4267C12.5867 13.4267 12.4201 13.3601 12.2867 13.2334L12.2001 13.1467C11.9401 12.8867 11.9401 12.4667 12.2001 12.2067C12.4601 11.9467 12.8801 11.9467 13.1401 12.2067L13.2267 12.2934C13.4867 12.5534 13.4867 12.9734 13.2267 13.2334C13.1001 13.3601 12.9334 13.4267 12.7601 13.4267ZM3.24008 13.4267C3.06675 13.4267 2.90008 13.3601 2.76675 13.2334C2.50675 12.9734 2.50675 12.5534 2.76675 12.2934L2.85342 12.2067C3.11342 11.9467 3.53341 11.9467 3.79341 12.2067C4.05341 12.4667 4.05341 12.8867 3.79341 13.1467L3.70675 13.2334C3.58008 13.3601 3.40675 13.4267 3.24008 13.4267ZM14.6667 8.66675H14.6134C14.2467 8.66675 13.9467 8.36675 13.9467 8.00008C13.9467 7.63341 14.2467 7.33342 14.6134 7.33342C14.9801 7.33342 15.3067 7.63341 15.3067 8.00008C15.3067 8.36675 15.0334 8.66675 14.6667 8.66675ZM1.38675 8.66675H1.33341C0.966748 8.66675 0.666748 8.36675 0.666748 8.00008C0.666748 7.63341 0.966748 7.33342 1.33341 7.33342C1.70008 7.33342 2.02675 7.63341 2.02675 8.00008C2.02675 8.36675 1.75341 8.66675 1.38675 8.66675ZM12.6734 3.99341C12.5001 3.99341 12.3334 3.92675 12.2001 3.80008C11.9401 3.54008 11.9401 3.12008 12.2001 2.86008L12.2867 2.77341C12.5467 2.51341 12.9667 2.51341 13.2267 2.77341C13.4867 3.03341 13.4867 3.45341 13.2267 3.71341L13.1401 3.80008C13.0134 3.92675 12.8467 3.99341 12.6734 3.99341ZM3.32675 3.99341C3.15341 3.99341 2.98675 3.92675 2.85342 3.80008L2.76675 3.70675C2.50675 3.44675 2.50675 3.02675 2.76675 2.76675C3.02675 2.50675 3.44675 2.50675 3.70675 2.76675L3.79341 2.85342C4.05341 3.11342 4.05341 3.53341 3.79341 3.79341C3.66675 3.92675 3.49341 3.99341 3.32675 3.99341ZM8.00008 2.02675C7.63341 2.02675 7.33342 1.75341 7.33342 1.38675V1.33341C7.33342 0.966748 7.63341 0.666748 8.00008 0.666748C8.36675 0.666748 8.66675 0.966748 8.66675 1.33341C8.66675 1.70008 8.36675 2.02675 8.00008 2.02675Z"
2068
+							fill="#969AA1"
2069
+						/>
2070
+						</svg>
2071
+					</span>
2072
+					<span class="hidden dark:inline-block">
2073
+						<svg
2074
+						width="16"
2075
+						height="16"
2076
+						viewBox="0 0 16 16"
2077
+						fill="none"
2078
+						xmlns="http://www.w3.org/2000/svg"
2079
+						>
2080
+						<path
2081
+							d="M14.3533 10.62C14.2466 10.44 13.9466 10.16 13.1999 10.2933C12.7866 10.3667 12.3666 10.4 11.9466 10.38C10.3933 10.3133 8.98659 9.6 8.00659 8.5C7.13993 7.53333 6.60659 6.27333 6.59993 4.91333C6.59993 4.15333 6.74659 3.42 7.04659 2.72666C7.33993 2.05333 7.13326 1.7 6.98659 1.55333C6.83326 1.4 6.47326 1.18666 5.76659 1.48C3.03993 2.62666 1.35326 5.36 1.55326 8.28666C1.75326 11.04 3.68659 13.3933 6.24659 14.28C6.85993 14.4933 7.50659 14.62 8.17326 14.6467C8.27993 14.6533 8.38659 14.66 8.49326 14.66C10.7266 14.66 12.8199 13.6067 14.1399 11.8133C14.5866 11.1933 14.4666 10.8 14.3533 10.62Z"
2082
+							fill="#969AA1"
2083
+						/>
2084
+						</svg>
2085
+					</span>
2086
+					</span>
2087
+				</label>
2088
+				<!-- Dark Mode Toggler -->
2089
+			</li>
2090
+		</ul>
2091
+
2092
+		<!-- User Area -->
1844 2093
 		<div class="relative" x-data="{{ dropdownOpen: false }}" @click.outside="dropdownOpen = false">
1845 2094
 			<a class="flex items-center gap-4" href="#" @click.prevent="dropdownOpen = ! dropdownOpen">
1846 2095
 				<span class="hidden text-right lg:block">
@@ -1897,7 +2146,9 @@ plot """
1897 2146
 			</div>
1898 2147
 			<!-- Dropdown End -->
1899 2148
       		</div>
1900
-	  	</div>			
2149
+	  	</div>	
2150
+		<!-- User Area -->		
2151
+
1901 2152
 	</div>
1902 2153
 </header>'''
1903 2154
 	
@@ -1915,7 +2166,6 @@ Please see the manual page for more details.
1915 2166
 """ % conf
1916 2167
 	print(text)
1917 2168
 
1918
-
1919 2169
 class GitStats:
1920 2170
 	def run(self, args_orig):
1921 2171
 		optlist, args = getopt.getopt(args_orig, 'hc:', ["help"])
@@ -1999,4 +2249,6 @@ if __name__=='__main__':
1999 2249
 	g.run(sys.argv[1:])
2000 2250
 
2001 2251
 
2002
-# https://github.com/TailAdmin/tailadmin-free-tailwind-dashboard-template/tree/main
2252
+# https://github.com/TailAdmin/tailadmin-free-tailwind-dashboard-template/tree/main
2253
+# https://codepen.io/Zsena/pen/ZEMaaoX?editors=1010
2254
+# https://sebastiandedeyne.com/non-reactive-data-in-alpine-js/

+ 222
- 0
tailwind.json 查看文件

@@ -0,0 +1,222 @@
1
+{
2
+    "darkMode": "class",
3
+    "theme": {
4
+      "extend": {
5
+        "colors": {
6
+          "current": "currentColor",
7
+          "transparent": "transparent",
8
+          "white": "#FFFFFF",
9
+          "black": "#1C2434",
10
+          "black-2": "#010101",
11
+          "body": "#64748B",
12
+          "bodydark": "#AEB7C0",
13
+          "bodydark1": "#DEE4EE",
14
+          "bodydark2": "#8A99AF",
15
+          "primary": "#3C50E0",
16
+          "secondary": "#80CAEE",
17
+          "stroke": "#E2E8F0",
18
+          "gray": "#EFF4FB",
19
+          "graydark": "#333A48",
20
+          "gray-2": "#F7F9FC",
21
+          "gray-3": "#FAFAFA",
22
+          "whiten": "#F1F5F9",
23
+          "whiter": "#F5F7FD",
24
+          "boxdark": "#24303F",
25
+          "boxdark-2": "#1A222C",
26
+          "strokedark": "#2E3A47",
27
+          "form-strokedark": "#3d4d60",
28
+          "form-input": "#1d2a39",
29
+          "meta-1": "#DC3545",
30
+          "meta-2": "#EFF2F7",
31
+          "meta-3": "#10B981",
32
+          "meta-4": "#313D4A",
33
+          "meta-5": "#259AE6",
34
+          "meta-6": "#FFBA00",
35
+          "meta-7": "#FF6766",
36
+          "meta-8": "#F0950C",
37
+          "meta-9": "#E5E7EB",
38
+          "success": "#219653",
39
+          "danger": "#D34053",
40
+          "warning": "#FFA70B"
41
+      },
42
+      "fontSize": {
43
+          "title-xxl": ["44px", "55px"],
44
+          "title-xl": ["36px", "45px"],
45
+          "title-xl2": ["33px", "45px"],
46
+          "title-lg": ["28px", "35px"],
47
+          "title-md": ["24px", "30px"],
48
+          "title-md2": ["26px", "30px"],
49
+          "title-sm": ["20px", "26px"],
50
+          "title-xsm": ["18px", "24px"]
51
+      },
52
+      "spacing": {
53
+          "4.5": "1.125rem",
54
+          "5.5": "1.375rem",
55
+          "6.5": "1.625rem",
56
+          "7.5": "1.875rem",
57
+          "8.5": "2.125rem",
58
+          "9.5": "2.375rem",
59
+          "10.5": "2.625rem",
60
+          "11": "2.75rem",
61
+          "11.5": "2.875rem",
62
+          "12.5": "3.125rem",
63
+          "13": "3.25rem",
64
+          "13.5": "3.375rem",
65
+          "14": "3.5rem",
66
+          "14.5": "3.625rem",
67
+          "15": "3.75rem",
68
+          "15.5": "3.875rem",
69
+          "16": "4rem",
70
+          "16.5": "4.125rem",
71
+          "17": "4.25rem",
72
+          "17.5": "4.375rem",
73
+          "18": "4.5rem",
74
+          "18.5": "4.625rem",
75
+          "19": "4.75rem",
76
+          "19.5": "4.875rem",
77
+          "21": "5.25rem",
78
+          "21.5": "5.375rem",
79
+          "22": "5.5rem",
80
+          "22.5": "5.625rem",
81
+          "24.5": "6.125rem",
82
+          "25": "6.25rem",
83
+          "25.5": "6.375rem",
84
+          "26": "6.5rem",
85
+          "27": "6.75rem",
86
+          "27.5": "6.875rem",
87
+          "29": "7.25rem",
88
+          "29.5": "7.375rem",
89
+          "30": "7.5rem",
90
+          "31": "7.75rem",
91
+          "32.5": "8.125rem",
92
+          "34": "8.5rem",
93
+          "34.5": "8.625rem",
94
+          "35": "8.75rem",
95
+          "36.5": "9.125rem",
96
+          "37.5": "9.375rem",
97
+          "39": "9.75rem",
98
+          "39.5": "9.875rem",
99
+          "40": "10rem",
100
+          "42.5": "10.625rem",
101
+          "44": "11rem",
102
+          "45": "11.25rem",
103
+          "46": "11.5rem",
104
+          "47.5": "11.875rem",
105
+          "49": "12.25rem",
106
+          "50": "12.5rem",
107
+          "52": "13rem",
108
+          "52.5": "13.125rem",
109
+          "54": "13.5rem",
110
+          "54.5": "13.625rem",
111
+          "55": "13.75rem",
112
+          "55.5": "13.875rem",
113
+          "59": "14.75rem",
114
+          "60": "15rem",
115
+          "62.5": "15.625rem",
116
+          "65": "16.25rem",
117
+          "67": "16.75rem",
118
+          "67.5": "16.875rem",
119
+          "70": "17.5rem",
120
+          "72.5": "18.125rem",
121
+          "73": "18.25rem",
122
+          "75": "18.75rem",
123
+          "90": "22.5rem",
124
+          "94": "23.5rem",
125
+          "95": "23.75rem",
126
+          "100": "25rem",
127
+          "115": "28.75rem",
128
+          "125": "31.25rem",
129
+          "132.5": "33.125rem",
130
+          "150": "37.5rem",
131
+          "171.5": "42.875rem",
132
+          "180": "45rem",
133
+          "187.5": "46.875rem",
134
+          "203": "50.75rem",
135
+          "230": "57.5rem",
136
+          "242.5": "60.625rem"
137
+      },
138
+      "maxWidth": {
139
+          "2.5": "0.625rem",
140
+          "3": "0.75rem",
141
+          "4": "1rem",
142
+          "11": "2.75rem",
143
+          "13": "3.25rem",
144
+          "14": "3.5rem",
145
+          "15": "3.75rem",
146
+          "22.5": "5.625rem",
147
+          "25": "6.25rem",
148
+          "30": "7.5rem",
149
+          "34": "8.5rem",
150
+          "35": "8.75rem",
151
+          "40": "10rem",
152
+          "42.5": "10.625rem",
153
+          "44": "11rem",
154
+          "45": "11.25rem",
155
+          "60": "15rem",
156
+          "70": "17.5rem",
157
+          "90": "22.5rem",
158
+          "94": "23.5rem",
159
+          "125": "31.25rem",
160
+          "132.5": "33.125rem",
161
+          "142.5": "35.625rem",
162
+          "150": "37.5rem",
163
+          "180": "45rem",
164
+          "203": "50.75rem",
165
+          "230": "57.5rem",
166
+          "242.5": "60.625rem",
167
+          "270": "67.5rem",
168
+          "280": "70rem",
169
+          "292.5": "73.125rem"
170
+      },
171
+      "maxHeight": {
172
+          "35": "8.75rem",
173
+          "70": "17.5rem",
174
+          "90": "22.5rem",
175
+          "550": "34.375rem",
176
+          "300": "18.75rem"
177
+      },
178
+      "minWidth": {
179
+          "22.5": "5.625rem",
180
+          "42.5": "10.625rem",
181
+          "47.5": "11.875rem",
182
+          "75": "18.75rem"
183
+      },
184
+      "zIndex": {
185
+          "999999": "999999",
186
+          "99999": "99999",
187
+          "9999": "9999",
188
+          "999": "999",
189
+          "99": "99",
190
+          "9": "9",
191
+          "1": "1"
192
+      },
193
+      "opacity": {
194
+          "65": ".65"
195
+      },
196
+      "transitionProperty": { "width": "width", "stroke": "stroke" },
197
+      "borderWidth": {
198
+          "6": "6px"
199
+      },
200
+      "boxShadow": {
201
+          "default": "0px 8px 13px -3px rgba(0, 0, 0, 0.07)",
202
+          "card": "0px 1px 3px rgba(0, 0, 0, 0.12)",
203
+          "card-2": "0px 1px 2px rgba(0, 0, 0, 0.05)",
204
+          "switcher":
205
+          "0px 2px 4px rgba(0, 0, 0, 0.2), inset 0px 2px 2px #FFFFFF, inset 0px -1px 1px rgba(0, 0, 0, 0.1)",
206
+          "switch-1": "0px 0px 5px rgba(0, 0, 0, 0.15)",
207
+          "1": "0px 1px 3px rgba(0, 0, 0, 0.08)",
208
+          "2": "0px 1px 4px rgba(0, 0, 0, 0.12)",
209
+          "3": "0px 1px 5px rgba(0, 0, 0, 0.14)",
210
+          "4": "0px 4px 10px rgba(0, 0, 0, 0.12)",
211
+          "5": "0px 1px 1px rgba(0, 0, 0, 0.15)",
212
+          "6": "0px 3px 15px rgba(0, 0, 0, 0.1)",
213
+          "7": "-5px 0 0 #313D4A, 5px 0 0 #313D4A",
214
+          "8": "1px 0 0 #313D4A, -1px 0 0 #313D4A, 0 1px 0 #313D4A, 0 -1px 0 #313D4A, 0 3px 13px rgb(0 0 0 / 8%)"
215
+      },
216
+      "dropShadow": {
217
+          "1": "0px 1px 0px #E2E8F0",
218
+          "2": "0px 1px 4px rgba(0, 0, 0, 0.12)"
219
+      }
220
+      }
221
+    }
222
+  }